/* ==========================================================================
   OOGLE V5 — STYLESHEET

   Order: reset · layout · type · marks · buttons · header · footer · hero ·
   home · services · audiences · work · editorial · forms · utilities.

   Two structural habits worth keeping:
   · every grid track is minmax(0, 1fr). A bare 1fr has an auto minimum and
     nowrap content will push the page sideways.
   · nothing is clipped with clip-path, so box-shadow behaves normally. V4
     needed drop-shadow filters because its corners were cut. V5 uses plain
     border-radius from the --radius-* scale in tokens.css (V5.9.2): pills
     for controls, 12–20px for surfaces, nothing at all on brand marks.
   ========================================================================== */

/* ==========================================================================
   RESET
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--hdr-h) + 24px);
  /* A focused field scrolled into view lands above the mobile sticky bar
     (56px + safe area), never underneath it. */
  scroll-padding-bottom: calc(72px + env(safe-area-inset-bottom));
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  font-weight: var(--w-book);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "cv05" 1;
  overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul { margin: 0; }
ol, ul { padding: 0; list-style: none; }

/* Nothing focused or jumped to should land underneath the sticky header.
   Without this, tabbing to the first breadcrumb link on an interior page put
   it at y=22 behind a 75px header (WCAG 2.2 AA 2.4.11), and every #anchor
   landed a heading half under the bar. */
html { scroll-padding-top: calc(var(--hdr-h) + 16px); }
a, button, input, select, textarea, summary, [tabindex] {
  scroll-margin-top: calc(var(--hdr-h) + 16px);
}
img, svg, video { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: var(--link); text-decoration: none; }
a:hover { color: var(--link-hover); }
address { font-style: normal; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 0; }
table { border-collapse: collapse; width: 100%; }

::selection { background: var(--blue); color: #fff; }
.on-dark ::selection { background: var(--cyan); color: var(--navy); }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

.skip {
  position: absolute; left: 12px; top: -60px; z-index: 200;
  background: var(--blue); color: #fff;
  padding: 12px 18px; font-weight: var(--w-semi); font-size: var(--t-sm);
  border-radius: var(--radius-control); transition: top var(--d-state) var(--ease);
}
.skip:focus { top: 12px; color: #fff; }

.vh {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ==========================================================================
   LAYOUT
   ========================================================================== */
.wrap {
  width: 100%;
  max-width: calc(var(--track) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap-wide { max-width: calc(var(--track-wide) + var(--gutter) * 2); }
.wrap-text { max-width: calc(var(--measure) + var(--gutter) * 2); }

.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--s10) var(--s8);
} .c4 { grid-column: span 4; }
.c5 { grid-column: span 5; }
.c7 { grid-column: span 7; } .c8 { grid-column: span 8; }
/* An offset, for the two places a column needs to START at track 5 rather
   than be pushed there by an empty <div class="c4"></div>. V5.20 removed
   those two spacers: GenerateBlocks cannot express "an empty container used
   for offset", so an editor would have seen two empty boxes and deleted
   them. This says the same thing to the grid and nothing at all to a
   reader. */
.c8-end { grid-column: 5 / -1; }

/* em, not px: 61.25em is 980px at the default root size and it tracks the
   reader's text size, which 980px does not. A reader at 200% text zoom on a
   1280px viewport never reaches 980 CSS px, so a .c4 column stayed 380px wide
   while its type doubled. */
@media (max-width: 61.25em) {
  .grid { grid-template-columns: minmax(0, 1fr); gap: var(--s8); }
  .c3, .c4, .c5, .c6, .c7, .c8, .c9, .c8-end { grid-column: 1 / -1; }
}

/* Section rhythm. Deliberately uneven: the site breathes more around the
   pages that have to feel expensive and tightens up around utility pages. */
.sec    { padding-block: clamp(64px, 8vw, 120px); }
.sec-lg { padding-block: clamp(80px, 10vw, 168px); }
.sec-sm { padding-block: clamp(48px, 5.5vw, 80px); }

/* A ground change is always announced by a hairline, never by nothing. */
.on-paper { background: var(--paper-2); }

.ruled-top { border-top: 1px solid var(--rule); } .mt-4 { margin-top: var(--s4); }
.mt-5 { margin-top: var(--s5); } .mt-6 { margin-top: var(--s6); }
.mt-7 { margin-top: var(--s7); } .mt-8 { margin-top: var(--s8); }
.mt-9 { margin-top: var(--s9); } .mt-10 { margin-top: var(--s10); }
.mt-11 { margin-top: var(--s11); }
.mt-12 { margin-top: var(--s12); }

/* ==========================================================================
   TYPE
   ========================================================================== */
/* Display type is set large enough that one long word can be wider than a
   320px screen. A non-breaking space joining two words makes an unbreakable
   token out of both. Headings therefore break as a last resort rather than
   pushing the page sideways — the /who-we-help/education/ h1 did exactly
   that, and audit.py could see the document scrolling without being able to
   name the element responsible. */
/* `break-word` only breaks when a word alone exceeds the line. At 200% text
   zoom a 490px heading in a 320px viewport still reported min-content 490px
   from inside a flex row, and body{overflow-x:hidden} then destroyed the
   overflow instead of letting it scroll. `anywhere` also shrinks min-content,
   which is the half that matters here. */
.mega, h1, h2, h3, .display, .h2, .h3 { overflow-wrap: anywhere; }
.lead, p, li, dd, dt { overflow-wrap: break-word; }

/* ONE RECIPE. Every display size is 900 / -0.042em / 0.94 / 94% width, and
   only the size changes. That repetition is the whole difference between type
   that reads as art-directed and type that reads as a stack of defaults. */
.mega, h1, .display, h2, .h2, h3, .h3, h4, .h4, .era-year {
  font-weight: var(--w-black);
  letter-spacing: var(--tr-display);
  font-stretch: var(--wd-display);
  word-spacing: var(--ws-display);
}
/* Tracked uppercase closes its word gaps twice as fast. */
.eyebrow, .ftr-h, .mono, .era-list, .hero-band li, .workcap-n {
  word-spacing: var(--ws-caps);
}

.mega {
  font-size: var(--t-mega); line-height: var(--lh-display);
  /* V5.21: a floor that guarantees the longest word in a headline fits the
     line it is on. --t-mega bottoms out at 56px, and at 390 the copy column
     is 294px wide, so any word past about ten characters is wider than the
     line and gets broken mid-word by the overflow-wrap rule above -- the
     homepage headline was setting as "maintaine / d." on a phone. Capping at
     15vw makes the type follow the column it has to fit in, and it only bites
     below 373px of viewport, so nothing changes at any width the headline
     already fitted. */
  font-size: min(var(--t-mega), 12.5vw);
  /* Sized so the longest line nearly fills it. At 19ch the block inked 66% of
     its own box and the headline read as underweight in its own hero. */
  /* No text-wrap:balance here. Balance gave the hero a 910px line over a
     470px line; the headline is capped by measure instead, so it stacks into
     near-equal lines like a masthead. */
  max-width: 13ch;
}
h1, .display {
  font-size: var(--t-display); line-height: var(--lh-display);
  /* V5.22: the same floor guard the homepage .mega carries, for the same
     reason. --t-display bottoms out at 40px and an eleven-letter word
     ("WooCommerce") is 306px at that size, wider than the 280px line a .wrap
     gives at 320. `break-word` then split it as "WooCommerc / e" in a section
     heading. 11vw only bites below about 366px of viewport, so nothing moves
     at any width where the type already fitted. Internal page headings have
     their own container-relative cap; see .phero-copy h1 in art.css, where
     the constraint is a grid column rather than the viewport. */
  font-size: min(var(--t-display), 11vw);
  text-wrap: balance;
}
h2, .h2 {
  /* V5.22: the same floor guard h1 and .display carry. Without it an h2 set
     at 60px in a 294px column at 200% text zoom broke mid-word on thirteen
     headings ("Somethin/g broken", "Maintenan/ce questions"). 9vw only bites
     below about 400px of effective viewport. */
  font-size: min(var(--t-h2), 9vw); line-height: var(--lh-display);
  text-wrap: balance;
}
h3, .h3 {
  font-size: var(--t-h3); line-height: var(--lh-h3);
  text-wrap: balance;
}
h4, .h4 {
  font-size: var(--t-h4); line-height: 1.25;
  letter-spacing: var(--tr-h4);
}
h5, .h5 { font-size: var(--t-body); font-weight: var(--w-bold); line-height: 1.4; }

.lead {
  font-size: var(--t-lead); line-height: var(--lh-lead);
  color: var(--text-dim); font-weight: var(--w-book);
  letter-spacing: var(--tr-text); max-width: 58ch; text-wrap: pretty;
}

.sm    { font-size: var(--t-sm); }
.xs    { font-size: var(--t-xs); }
.dim   { color: var(--text-dim); }
.faint { color: var(--text-faint); }

.mono {
  font-family: var(--font-mono);
  font-size: var(--t-mono); letter-spacing: var(--tr-mono);
  text-transform: uppercase; color: var(--text-faint);
  font-weight: 500;
}

/* The eyebrow, with the keyline above it. This is the site's most repeated
   brand shape: a short 2px blue rule, then a small uppercase label. */
.eyebrow {
  display: block;
  font-size: var(--t-eyebrow); font-weight: var(--w-black);
  letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
  color: var(--mark);
  padding-top: var(--s4);
  position: relative;
}
.eyebrow::before {
  content: ""; position: absolute; top: 0; left: 0;
  width: 34px; height: 2px; background: var(--keyline);
  transform-origin: left center;
}
.eyebrow b { color: var(--mark); font-weight: var(--w-black); }
.eyebrow.no-rule { padding-top: 0; }
.eyebrow.no-rule::before { display: none; }

.sec-head { max-width: 44ch; }
.sec-head .eyebrow { margin-bottom: var(--s6); }
.sec-head h2 + .lead, .sec-head .h2 + .lead { margin-top: var(--s5); }

/* Prose. Used inside long-form blocks, never for whole pages. */
/* Inline links inside running text carry an underline. Colour alone measured
   1.51:1 against the surrounding copy, which is the 1.4.1 failure, not a
   contrast-against-background one. Links that are already obviously links —
   buttons, arrows, nav, cards, footer columns — are excluded. */
p > a:not(.btn):not(.arrow):not(.tel):not(.nav-lnk),
li > a:not(.btn):not(.arrow):not(.tel):not(.nav-lnk),
dd > a:not(.btn):not(.arrow):not(.tel) {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
}
p > a:not(.btn):not(.arrow):not(.tel):hover,
li > a:not(.btn):not(.arrow):not(.tel):hover {
  text-decoration-color: currentColor;
}
/* Navigation lists are already unmistakably links: a column of underlines
   in the footer is noise, not an affordance. */
.ftr a, .crumbs a, .index-row, .worklist-row, .workfeat, .lane a,
.mnav a, .nav a, .beats a, .era-list a { text-decoration: none; }
/* The fact bar's cells are surfaces, not prose links, so they take the card
   treatment. Written at the underline rule's own specificity plus one class:
   `li > a:not()...` is (0,4,2) and a bare `.facts a` is (0,1,1), so the short
   version loses and every line in the bar shipped underlined. */
.facts li > a:not(.btn):not(.arrow):not(.tel):not(.nav-lnk),
.facts li > a:not(.btn):not(.arrow):not(.tel):hover { text-decoration: none; }

.prose { max-width: var(--track-text); color: var(--text-dim); }
.prose > * + * { margin-top: var(--s5); }
.prose h3 { color: var(--text); margin-top: var(--s9); }
.prose h3:first-child { margin-top: 0; }
.prose h4 { color: var(--text); margin-top: var(--s7); }
.prose b, .prose strong { color: var(--text); font-weight: var(--w-semi); }
.prose a { text-decoration: underline; text-underline-offset: 3px;
           text-decoration-thickness: 1px; text-decoration-color: color-mix(in srgb, var(--link) 40%, transparent); }
.prose a:hover { text-decoration-color: currentColor; }
.prose ul { display: grid; gap: var(--s3); }
.prose ul li { position: relative; padding-left: 24px; }
.prose ul li::before {
  content: ""; position: absolute; left: 2px; top: 0.72em;
  width: 8px; height: 1px; background: var(--mark);
}
.prose ol { display: grid; gap: var(--s3); counter-reset: p; }
.prose ol li { position: relative; padding-left: 32px; counter-increment: p; }
.prose ol li::before {
  content: counter(p) "."; position: absolute; left: 0; top: 0;
  font-variant-numeric: tabular-nums; color: var(--mark); font-weight: var(--w-semi);
}

/* ==========================================================================
   MARKS
   ========================================================================== */

/* The corner bracket. One L at the top left of panels that carry weight.
   It is the second brand shape and it survives any logo change. Since the
   panels went to 16px corners it sits inset, inside the padding, so the
   square mark reads against the soft container instead of fighting it.
   Drawn with ::after, not ::before: .card owns ::before for its hover accent
   bar, and .card.bracket (three cards on /pricing/, one on /website-help/)
   silently lost its bracket to that rule from V5 until V5.9.3. */
/* V5.19: the mark got its own space. It was drawn at a fixed 14px inset
   inside boxes whose padding it knew nothing about, so it ran into whatever
   was in the corner: on .phero-note (20px of left padding, 24px of top) the L
   crossed the label, and on a .card at 620px and under (24px of padding) it
   sat behind the eyebrow. The geometry is three numbers now, in one place,
   and every box that carries the mark starts its content clear of it. */
.bracket {
  position: relative;
  /* mark ends at inset + size; content starts a further --bracket-air past it */
  --bracket-inset: 16px;
  --bracket-size: 16px;
  --bracket-air: 16px;
  --bracket-clear: calc(var(--bracket-inset) + var(--bracket-size) + var(--bracket-air));
}
.bracket::after {
  content: ""; position: absolute;
  top: var(--bracket-inset); left: var(--bracket-inset);
  width: var(--bracket-size); height: var(--bracket-size);
  border-top: 2px solid var(--keyline);
  border-left: 2px solid var(--keyline);
  pointer-events: none;
}
/* Nothing is laid out under the mark. Padding, not a margin on the first
   child: the eyebrow, the heading and a paragraph all take their turn at
   being first in one of these boxes, and only the box knows where its
   content may start. */
.card.bracket,
.panel.bracket,
.phead-side.bracket {
  padding: var(--bracket-clear);
}
/* The ruled note is not a box -- it is a left keyline with copy beside it --
   so it clears the mark on the two axes the mark uses and leaves the rest
   alone. */
.phero-note.bracket {
  padding-left: var(--bracket-clear);
  padding-top: var(--bracket-clear);
}
@media (max-width: 620px) {
  .bracket { --bracket-inset: 13px; --bracket-size: 14px; --bracket-air: 13px; }
}

.panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-card);
  padding: var(--s8);
}
.panel-lift { box-shadow: var(--lift-2); }
.panel-tint { background: var(--film); border-color: color-mix(in srgb, var(--mark) 22%, transparent); }

/* V5.20: the .tbd placeholder badge is gone. crawl.py fails the build if
   class="tbd" ever appears in shipped HTML, so shipping a stylesheet that
   knows how to draw one was the site keeping a rule for a thing it forbids. */

.crumbs {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  font-size: var(--t-xs); color: var(--text-faint);
  padding-block: var(--s6) 0;
}
.crumbs li { display: flex; gap: 8px; align-items: center; min-height: 32px; }
.crumbs li + li::before { content: "/"; color: var(--rule-strong); }
.crumbs a { color: var(--text-faint); display: inline-flex; align-items: center; min-height: 32px; }
.crumbs a:hover { color: var(--link); }
.crumbs [aria-current] { color: var(--text-dim); }

/* Link with a moving arrow. Used instead of a button wherever the action is
   navigation rather than commitment. */
.arrow {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px;
  font-weight: var(--w-bold); color: var(--link);
  font-size: var(--t-sm);
  max-width: 100%; min-width: 0;
  /* `anywhere`, NOT `break-word`. V5.22 tried break-word here, to stop domain
     names on /work/ splitting at large text sizes ("ollective.co/m"). It
     regressed the layout: the two differ in MIN-CONTENT sizing, and only
     `anywhere` shrinks it. Inside a flex or grid item, break-word leaves
     min-content at the full width of the longest token, so at 320px and 200%
     text zoom /work/ overflowed the document by 151px and the body clipped
     its own content. The same trade is documented above for .mega. A domain
     that breaks is worse-looking than one that does not; a page that scrolls
     sideways and hides text is a WCAG failure. */
  overflow-wrap: anywhere;
}
/* Inside a row that already provides its own height, the arrow is a label
   rather than a target of its own. */
.lane .arrow, .workcap .arrow { min-height: 0; }
/* V5.22: keeps a focused button clear of the fixed mobile bar. */
.btn, .arrow, a.tel, .path, a.card, .price-card { scroll-margin-bottom: 72px; }
.arrow::after {
  content: ""; width: 16px; height: 8px; flex: none;
  background: currentColor;
  -webkit-mask: var(--arrow-mask) no-repeat center / contain;
  mask: var(--arrow-mask) no-repeat center / contain;
  transition: transform var(--d-state) var(--ease);
}
.arrow:hover::after { transform: translateX(4px); }

/* ==========================================================================
   BUTTONS
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  font-size: var(--t-sm); font-weight: var(--w-bold); letter-spacing: -0.005em;
  line-height: 1; text-align: center; white-space: nowrap;
  max-width: 100%; padding: 14px 24px; min-height: 46px;
  border-radius: var(--radius-pill); border: 1px solid transparent;
  background: var(--blue); color: #fff;
  transition: background var(--d-state) var(--ease), transform var(--d-tap) var(--ease),
              box-shadow var(--d-state) var(--ease), border-color var(--d-state) var(--ease),
              color var(--d-state) var(--ease);
}
.btn:hover { background: var(--blue-deep); color: #fff; box-shadow: var(--lift-blue); }
.btn:active { transform: translateY(1px); }
.btn svg { width: 17px; height: 17px; flex: none; }

.on-dark .btn { background: var(--blue); color: #fff; }
.on-dark .btn:hover { background: var(--blue-lift); }

.btn-ghost {
  background: transparent; color: var(--text);
  border-color: var(--control-edge);
}
.btn-ghost:hover {
  background: var(--film); color: var(--text);
  border-color: var(--mark); box-shadow: none;
}
.on-dark .btn-ghost { background: transparent; color: var(--text); border-color: var(--control-edge); }
.on-dark .btn-ghost:hover { background: var(--dark-film); color: var(--text); border-color: var(--blue-light); }

.btn-quiet {
  background: var(--surface-2); color: var(--text);
  border-color: var(--control-edge);
}
.btn-quiet:hover { background: var(--paper-3); color: var(--text); border-color: var(--rule-strong); box-shadow: none; }
.on-dark .btn-quiet { background: var(--navy-4); }
.on-dark .btn-quiet:hover { background: var(--navy-5); }

.btn-lg { padding: 17px 30px; min-height: 54px; font-size: var(--t-body); }
.btn-sm { padding: 12px 18px; min-height: 44px; font-size: var(--t-sm); }
.btn-block { display: flex; width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s4); align-items: center; }

/* 44px of height on every phone link. The label is unchanged; only the hit
   area is, so nothing about the design moves. */
.tel {
  display: inline-flex; align-items: center; gap: 9px;
  min-height: 44px;
  font-weight: var(--w-bold); font-size: var(--t-sm);
  color: var(--text); font-variant-numeric: tabular-nums;
}
.tel svg { width: 16px; height: 16px; color: var(--mark); flex: none; }
.tel:hover { color: var(--link); }

/* ==========================================================================
   HEADER
   ========================================================================== */
.hdr {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--d-state) var(--ease), background var(--d-state) var(--ease);
}
.hdr[data-stuck="1"] { border-bottom-color: var(--rule); background: color-mix(in srgb, var(--paper) 96%, transparent); }
@supports not (backdrop-filter: blur(1px)) { .hdr { background: var(--paper); } }

.hdr-in {
  display: flex; align-items: center; gap: var(--s6);
  height: var(--hdr-h);
  flex-wrap: wrap; min-width: 0;
}
.hdr-in > * { min-width: 0; }
/* The wordmark is set, not drawn. "Oogle." with the period as the mark. It is
   a placeholder for a logo still being designed, so nothing else in this
   stylesheet takes a measurement from it. */
.brand { display: flex; align-items: center; flex: none; color: var(--ink);
         min-height: 44px; padding-right: var(--s2); }
.brand:hover { color: var(--blue); }
/* The wordmark is drawn artwork now, not type, so it has left the shared
   display rule above -- font-weight and tracking on a box containing one
   <svg> are dead declarations, and a dead declaration is what a future pass
   trips over. What survives is the sizing: font-size is still the single
   knob, because .wordmark-lg and everything else already turned it. */
.wordmark {
  display: inline-flex; align-items: center;
  font-size: var(--t-h4); line-height: 1; color: currentColor;
}
/* .88em is not a guess. The artwork is cropped to its own ink with no
   padding, the O's cap is 0.796 of that height, and Archivo's cap at 900 is
   0.70 of its font-size: 0.70 / 0.796 puts the new cap exactly where the
   typographic stand-in had it, so nothing beside the mark moved. The letters
   take currentColor and follow --ink, --snow and the blue hover for free;
   only the square is painted, and it is painted with the token, so the day
   the brand blue moves the mark moves with it. aspect-ratio is belt and
   braces for anything that will not derive an auto width from the viewBox. */
.wordmark svg {
  display: block; height: .88em; width: auto;
  aspect-ratio: 593.92 / 191.41;
}
.wordmark-lg { font-size: calc(var(--t-h4) * 1.16); }

.nav { display: flex; align-items: center; gap: 2px; margin-left: var(--s5); }
/* The mega panels anchor to the content track, not to their trigger, so a
   nine-item column can never push the panel off the right edge. */
.hdr-in { position: relative; }
.nav-lnk {
  display: inline-flex; align-items: center; gap: 6px;
  /* 13px/700 against a 14px/700 button. V5.10 shipped the reverse — nav at
     15px/500 over a 14px CTA — so the primary action was the smallest type in
     the header. Weight and size both say which one matters now. */
  padding: 11px 13px; border-radius: var(--radius-pill);
  font-size: var(--t-eyebrow); font-weight: var(--w-bold);
  color: var(--ink-3); letter-spacing: 0.02em;
  transition: color var(--d-state) var(--ease), background var(--d-state) var(--ease);
}
.nav-lnk:hover, .nav-lnk[aria-expanded="true"] { color: var(--blue); background: var(--blue-film); }
.nav-lnk[aria-current="page"], .nav-lnk[data-active="1"] { color: var(--blue); }
.nav-lnk svg { width: 9px; height: 6px; opacity: 0.65; transition: transform var(--d-state) var(--ease); }
.nav-lnk[aria-expanded="true"] svg { transform: rotate(180deg); }

.hdr-act { display: flex; align-items: center; gap: var(--s3); margin-left: auto;
            flex: 0 1 auto; min-width: 0; flex-wrap: wrap; justify-content: flex-end; }
.hdr-act .tel { margin-right: var(--s2); }

/* With scripting off, .mnav can never open (only main.js writes data-open), so
   the burger is a control that does nothing. The footer reaches /services/ and
   /who-we-help/ without script, so removing the dead control loses nothing. */
html:not(.js) .burger { display: none; }

/* --- burger and mobile menu --------------------------------------------- */
/* Raised above the panel while the menu is open: it is inside an inert
   header but is the control that closes the overlay, so it stays live and
   stays clickable. */
.burger[data-escape] { position: relative; z-index: 210; pointer-events: auto; }
.burger {
  display: none; width: 44px; height: 44px; flex: none;
  border: 1px solid var(--line); border-radius: var(--radius-control);
  align-items: center; justify-content: center;
  background: var(--paper);
}
.burger span, .burger span::before, .burger span::after {
  display: block; width: 17px; height: 1.5px; background: var(--ink);
  transition: transform var(--d-state) var(--ease), opacity var(--d-tap) var(--ease);
}
.burger span { position: relative; }
.burger span::before, .burger span::after { content: ""; position: absolute; left: 0; }
.burger span::before { top: -5.5px; }
.burger span::after  { top: 5.5px; }
.burger[aria-expanded="true"] span { background: transparent; }
.burger[aria-expanded="true"] span::before { transform: translateY(5.5px) rotate(45deg); }
.burger[aria-expanded="true"] span::after  { transform: translateY(-5.5px) rotate(-45deg); }

.mnav {
  position: fixed; inset: var(--hdr-h) 0 0; z-index: 98;
  background: var(--paper);
  display: none; flex-direction: column;
  overflow-y: auto; overscroll-behavior: contain;
}
.mnav[data-open="1"] { display: flex; }
body[data-lock="1"] { overflow: hidden; }

.mnav-scroll { flex: 1 1 auto; padding: var(--s6) var(--gutter) var(--s8); }
.mnav-primary { display: grid; gap: 2px; padding-bottom: var(--s6); }
.mnav-primary > a {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 0; font-size: 1.25rem; font-weight: var(--w-semi);
  letter-spacing: -0.018em; color: var(--ink);
  border-bottom: 1px solid var(--line);
}
.mnav-primary > a:hover { color: var(--blue); }

.mgroup { border-bottom: 1px solid var(--line); }
.mgroup > summary {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 0; cursor: pointer; list-style: none;
  font-size: 1.25rem; font-weight: var(--w-semi); letter-spacing: -0.018em;
  color: var(--ink);
}
.mgroup > summary::-webkit-details-marker { display: none; }
.mgroup > summary::after {
  content: ""; width: 12px; height: 12px; flex: none;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1v10M1 6h10' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1v10M1 6h10' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat center / contain;
  transition: transform var(--d-state) var(--ease);
}
.mgroup[open] > summary::after { transform: rotate(45deg); }
.mgroup-body { padding-bottom: var(--s6); }
.msub-h {
  display: block; font-size: var(--t-mono); font-weight: var(--w-semi);
  letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
  color: var(--ink-4); margin: var(--s5) 0 var(--s2);
}
.mgroup-body ul { display: grid; gap: 1px; }
.mgroup-body a {
  display: block; padding: 9px 0; font-size: var(--t-sm); color: var(--ink-2);
}
.mgroup-body a:hover { color: var(--blue); }
.mgroup-body .arrow { margin-top: var(--s4); }

.mnav-foot {
  flex: none; padding: var(--s5) var(--gutter);
  padding-bottom: max(var(--s5), env(safe-area-inset-bottom));
  border-top: 1px solid var(--line); background: var(--paper-2);
  display: grid; gap: var(--s3);
}
.mnav-foot .row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }

/* The header's narrowest desktop track. .nav appears at 1081px, so the phone
   number has to leave well before that or the row overflows: at 1081px the
   project button clipped 12px past the viewport and "Who We Help" stacked to
   three lines. body{overflow-x:hidden} masked it from scrollWidth, which is
   why 360/768/1440 sampling never caught it. Both rules are required --
   nowrap alone converts the wrap into real overflow. */
.nav-lnk { white-space: nowrap; }

/* These three are in `em`, not `px`, on purpose. When a reader turns text size
   up, the header's contents grow but a pixel breakpoint does not, so the nav,
   the phone number and two buttons all stayed on one row and pushed 93px off
   the right edge at 200% — and body{overflow-x:hidden} then destroyed it
   rather than letting it scroll. `em` in a media query tracks the reader's
   own default size, so the header collapses to the burger when the type gets
   big, exactly as it does when the window gets narrow. `flex-wrap` on the
   row below is the backstop: if it still does not fit, it wraps instead of
   overflowing. (WCAG 1.4.4.) */
@media (max-width: 77.5em) {
  .hdr-act .tel { display: none; }
}
@media (max-width: 67.5em) {
  .nav { display: none; }
  .burger { display: inline-flex; }
}
/* When only one action fits, the one that survives is Help — the same action
   that is emphasized in the hero, the closer and the mobile menu. This is the
   site whose promise is that website problems stop here; the broken-site
   visitor is the one who cannot afford to go looking for the link. */
@media (max-width: 35em) {
  .hdr-act .btn-project { display: none; }
}
/* Below this the header is a wordmark, one action and the burger. Anything
   more and large text has nowhere to go.

   V5.22 tried to lower this to 23.5em, so the common phone band kept a header
   action instead of a phone icon. Measured, it does not fit: .hdr-act is
   278px and the wordmark is 60px, so below about 400px the action row wraps
   to a second line and pushes out of the header box. 26em is the real limit
   and it stays. The mobile CTA problem it was trying to solve is solved in
   main.js instead, by bringing the sticky bar up at 360px of scroll rather
   than 560 -- and the bar carries the page's OWN action, which a generic
   header button would not. */
@media (max-width: 26em) {
  .hdr-act .btn-help { display: none; }
}

/* ==========================================================================
   FOOTER
   Brand on the left, three short groups on the right, one utility row. It is
   navigation assistance, not the sitemap: the full service and audience lists
   live in the header menus and on /services/ and /who-we-help/. V5.9.1 listed
   all of them here (six columns, ~40 links) and at common desktop widths the
   sixth column wrapped alone under the rest. Named grid areas so the columns
   can never wrap unexpectedly: the layout is stated at every breakpoint.
   ========================================================================== */
.ftr {
  background: var(--navy); color: var(--snow);
  padding-block: clamp(48px, 5.5vw, 72px) var(--s7);
  border-top: 2px solid var(--blue);
}
.ftr-top {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(4, minmax(0, 1fr));
  grid-template-areas: "brand svc co recent start";
  gap: var(--s8) var(--s6);
}
.ftr-brand { grid-area: brand; padding-right: var(--s8); }
.ftr-svc   { grid-area: svc; }
.ftr-co    { grid-area: co; }
.ftr-recent{ grid-area: recent; }
.ftr-start { grid-area: start; }
.ftr-brand .wordmark { height: 26px; color: var(--snow); }
.ftr-brand p { font-size: var(--t-sm); color: var(--snow-2); margin-top: var(--s5); max-width: 30ch; line-height: 1.55; }
.ftr-tel { margin-top: var(--s5) !important; }
.ftr-tel .tel { color: var(--snow); font-size: var(--t-body); }
.ftr-tel .tel svg { color: var(--blue-light); }
/* Two lines, one target. The icon sits against the digits rather than
   centred on the pair, so the eye lands on the number first. */
.tel-two { align-items: flex-start; gap: 11px; min-height: 0; padding-block: 4px; }
.tel-two svg { margin-top: 4px; }
.tel-lines { display: grid; gap: 2px; min-width: 0; }
.tel-n { font-size: var(--t-body); line-height: 1.25; }
.tel-alt {
  font-size: var(--t-sm); font-weight: var(--w-book);
  color: var(--snow-3); letter-spacing: 0.02em; line-height: 1.25;
}
.tel-two:hover .tel-alt { color: var(--snow-2); }
.ftr-h {
  font-size: var(--t-eyebrow); font-weight: var(--w-black);
  letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
  color: var(--snow-3); padding-bottom: var(--s3); margin-bottom: var(--s4);
  border-bottom: 1px solid var(--line-dark); position: relative;
}
.ftr-h::after { content: ""; position: absolute; left: 0; bottom: -1px; width: 24px; height: 1px; background: var(--cyan); }
.ftr-col ul { display: grid; gap: 2px; }
/* 24px minimum with clear spacing (WCAG 2.2 AA 2.5.8) for the dense link
   lists; the 44px floor is kept for standalone controls — buttons, the phone,
   the burger, the drop zone. Padding a 25-link footer to 44 each added 234px
   to a phone page that was already too long, and traded one problem for
   another. */
.ftr-col a { display: block; padding-block: 7px; min-height: 24px;
             font-size: var(--t-sm); color: var(--snow-2); }
.ftr-col a:hover { color: var(--cyan-soft); }
.ftr-start a { color: var(--snow); font-weight: var(--w-bold); }
.ftr-start .ftr-note { font-size: var(--t-xs); color: var(--snow-3); margin-top: var(--s4); max-width: 22ch; line-height: 1.55; }

.ftr-bot {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); align-items: center;
  justify-content: space-between;
  margin-top: var(--s8); padding-top: var(--s4);
  border-top: 1px solid var(--line-dark);
  font-size: var(--t-xs); color: var(--snow-3);
}
.ftr-legal { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); }
.ftr-legal a { color: var(--snow-3); display: inline-flex; align-items: center;
               min-height: 44px; padding-block: 6px; }
.ftr-legal a:hover { color: var(--cyan-soft); }

@media (max-width: 900px) {
  .ftr-top {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-areas: "brand brand brand brand" "svc co recent start";
  }
  .ftr-brand { padding-right: 0; }
}
@media (max-width: 768px) {
  .ftr-top {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-areas: "brand brand" "svc co" "recent start";
    gap: var(--s6) var(--s6);
  }
  /* Two links and a note do not need a column of their own on a phone. */
  .ftr-start ul { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s7); }
  .ftr-start .ftr-note { max-width: none; }
}
/* Two columns all the way down to 320px. A single stacked column turned the
   footer into 1,154px of links on a 390px phone, which is the oversized footer
   this site already removed once. */
@media (max-width: 340px) {
  .ftr-top { grid-template-columns: minmax(0, 1fr);
             grid-template-areas: "brand" "svc" "co" "recent" "start"; }
}

/* ==========================================================================
   INTAKE
   ========================================================================== */
.intake {
  margin-top: var(--s9);
  border: 1px solid var(--line); border-radius: var(--radius-panel);
  background: var(--paper); box-shadow: var(--lift-2);
  overflow: hidden;
}
.intake-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s5);
  padding: var(--s7) var(--s8);
  border-bottom: 1px solid var(--line); background: var(--paper-2);
}
.intake-head h3 { font-size: var(--t-h3); }
.intake-head p { font-size: var(--t-sm); color: var(--ink-4); margin-top: 6px; max-width: 58ch; }
.intake-body { padding: var(--s8); }
@media (max-width: 640px) {
  .intake-head, .intake-body { padding: var(--s6) var(--s5); }
}

/* ==========================================================================
   FORMS
   ========================================================================== */
.fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5); }
.f { display: grid; gap: 7px; min-width: 0; }
.f.full { grid-column: 1 / -1; }
.f label { font-size: var(--t-sm); font-weight: var(--w-semi); color: var(--ink-2); }
.f label .opt { font-weight: var(--w-book); color: var(--ink-4); }
.f .hint { font-size: var(--t-xs); color: var(--ink-4); }
.f input, .f textarea, .f select {
  width: 100%; background: var(--paper);
  border: 1px solid var(--control-edge); border-radius: var(--radius-input);
  padding: 12px 15px; font-size: var(--t-body); color: var(--ink);
  transition: border-color var(--d-state) var(--ease), box-shadow var(--d-state) var(--ease);
  min-width: 0;
}
.f textarea { min-height: 132px; resize: vertical; line-height: 1.55; }
.f select { appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1.2 5 4.8 9 1.2' fill='none' stroke='%23566579' stroke-width='1.4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 10px 6px;
  padding-right: 38px;
}
.f input::placeholder, .f textarea::placeholder { color: var(--ink-5); }
/* V5.14: this block used to be outline:none plus a box-shadow glow, and the
   glow measures 1.21:1 against the page. That is not a focus indicator. The
   brand glow stays; the ring comes back on top of it. Every interactive
   element on the site now keeps the global :focus-visible outline. */
.f input:focus, .f textarea:focus, .f select:focus {
  border-color: var(--blue);
  box-shadow: 0 0 0 3px var(--blue-film-2);
}
.f input:focus-visible, .f textarea:focus-visible, .f select:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
.f[data-bad="1"] input, .f[data-bad="1"] textarea, .f[data-bad="1"] select { border-color: var(--fault); }
.f-err { font-size: var(--t-xs); color: var(--fault); display: none; }
.f[data-bad="1"] .f-err { display: block; }

.radios { display: flex; flex-wrap: wrap; gap: var(--s2); }
.radios label { /* the border IS the control boundary (1.4.11) */
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--control-edge); border-radius: var(--radius-pill);
  padding: 10px 16px; font-size: var(--t-sm); cursor: pointer; font-weight: var(--w-mid);
  color: var(--ink-2); background: var(--paper);
  transition: border-color var(--d-tap) var(--ease), background var(--d-tap) var(--ease), color var(--d-tap) var(--ease);
}
.radios input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.radios label:hover { border-color: var(--line-2); background: var(--paper-2); }
.radios input:checked + span { color: var(--blue); }
.radios label:has(input:checked) { border-color: var(--blue); background: var(--blue-film); color: var(--blue); }
.radios label:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }

.dz {
  border: 1px dashed var(--control-edge); border-radius: var(--radius-card);
  padding: var(--s7) var(--s6); text-align: center; background: var(--paper-2);
  transition: border-color var(--d-state) var(--ease), background var(--d-state) var(--ease);
}
.dz[data-over="1"] { border-color: var(--blue); background: var(--blue-film); }
.dz svg { width: 22px; height: 22px; color: var(--ink-4); margin: 0 auto var(--s3); }
/* The whole zone is the target. The input inside it is 32x26 and invisible. */
.dz label {
  display: block; font-size: var(--t-sm); font-weight: var(--w-bold);
  color: var(--blue); cursor: pointer;
}
.dz label::after { content: ""; position: absolute; inset: 0; }
.dz { position: relative; }
.dz p { font-size: var(--t-xs); color: var(--ink-4); margin-top: 5px; }
.dz input[type="file"] { position: absolute; opacity: 0; width: 1px; height: 1px; }
.dz-list { display: grid; gap: 5px; margin-top: var(--s4); text-align: left; }
.dz-list li { font-size: var(--t-xs); color: var(--ink-3); font-family: var(--font-mono); }

.form-foot {
  display: flex; flex-wrap: wrap; gap: var(--s5); align-items: center;
  margin-top: var(--s7); padding-top: var(--s6); border-top: 1px solid var(--line);
}
.form-foot p { font-size: var(--t-xs); color: var(--ink-4); max-width: 44ch; }

.form-ok {
  border: 1px solid var(--blue); border-radius: var(--radius-card);
  background: var(--blue-film); padding: var(--s7);
}
.form-ok h3 { color: var(--ink); }
.form-ok p { margin-top: var(--s3); font-size: var(--t-sm); color: var(--ink-3); }

@media (max-width: 40em) { .fields { grid-template-columns: minmax(0, 1fr); } }

/* ==========================================================================
   SERVICE AND AUDIENCE CARDS
   ========================================================================== */
.card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s5); }

.card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel); padding: var(--s8);
  transition: border-color var(--d-state) var(--ease), box-shadow var(--d-state) var(--ease),
              transform var(--d-state) var(--ease);
  position: relative; overflow: hidden;
}
a.card:hover { border-color: var(--line-2); box-shadow: var(--lift-2); transform: translateY(-2px); }
.on-dark a.card:hover { border-color: var(--navy-5); box-shadow: var(--lift-2); }
.card::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 2px;
  background: var(--mark); transform: scaleY(0); transform-origin: top;
  transition: transform var(--d-state) var(--ease);
}
a.card:hover::before { transform: scaleY(1); }
.card h3 { font-size: var(--t-h4); color: var(--text); }
.card p { font-size: var(--t-sm); color: var(--text-dim); margin-top: var(--s3); line-height: 1.55; }
.card .arrow { margin-top: auto; padding-top: var(--s5); }
.card-meta { font-family: var(--font-mono); font-size: var(--t-mono); letter-spacing: 0.08em;
             text-transform: uppercase; color: var(--text-faint); margin-bottom: var(--s3); }

@media (max-width: 61.25em) {
  .card-grid, .card-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* An odd card last in a two-column band spans both, the same convention
     .needs and .index already use. Without it /pricing/ and /website-help/
     put two cards on one row and left the third alone beside 363px of empty
     ground at every width from 621 to 980. */
  .card-grid > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
  .card-grid, .card-grid-2, .card-grid-4 { grid-template-columns: minmax(0, 1fr); }
  .card { padding: var(--s6); }
}

/* A discipline block on /services/: heading, lead, then a ruled index. */
.disc { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--s8); }
.disc + .disc { margin-top: var(--s12); padding-top: var(--s12); border-top: 1px solid var(--rule); }
.disc-head .eyebrow { margin-bottom: var(--s5); }
.disc-head h2 { font-size: var(--t-h3); }
.disc-head p { font-size: var(--t-sm); color: var(--text-dim); margin-top: var(--s5); max-width: 40ch; }
/* V5.11: the group is a surface. /services/ presents the same four lanes the
   homepage presents inside a panel, and it was presenting them as bare
   hairline rows on a flat field -- the same four ideas losing their container,
   their hierarchy and their definition one click after the homepage sold them.
   Same treatment as .lanes: box the GROUP, never the rows. */
.index {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--s8);
  border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel);
  background: var(--bg);
  padding: 0 clamp(20px, 2.6vw, 36px) clamp(12px, 1.6vw, 20px);
}
.index a {
  display: block; padding: var(--s5) 0; border-top: 1px solid var(--rule);
  transition: translate var(--d-state) var(--ease);
}
/* The first row sits under the panel's own top edge; a rule there would double
   the boundary. Two cells wide above 620, one below. */
.index a:nth-child(-n+2) { border-top: 0; padding-top: clamp(16px, 2vw, 26px); }
/* An odd last item spans rather than leaving a hole beside itself. */
.index a:last-child:nth-child(odd) { grid-column: 1 / -1; }
.index a:hover { translate: 8px; }   /* was padding-left: layout per frame */
.index a b { display: block; font-size: var(--t-h4); font-weight: var(--w-semi); color: var(--text); letter-spacing: var(--tr-h4); }
.index a:hover b { color: var(--link); }
.index a span { display: block; font-size: var(--t-sm); color: var(--text-dim); margin-top: 5px; line-height: 1.5; }
@media (max-width: 61.25em) {
  .disc { grid-template-columns: minmax(0, 1fr); gap: var(--s7); }
  .disc + .disc { margin-top: var(--s10); padding-top: var(--s10); }
}
@media (max-width: 38.75em) {
  .index { grid-template-columns: minmax(0, 1fr); }
  .index a:nth-child(2) { border-top: 1px solid var(--rule); padding-top: var(--s5); }
}

/* ==========================================================================
   NUMBERED METHOD LISTS
   ========================================================================== */
.steps { display: grid; margin: 0; }
.step {
  display: grid; grid-template-columns: 68px minmax(0, 1fr); gap: var(--s5);
  padding: var(--s7) 0; border-top: 1px solid var(--rule);
}
.step:last-child { border-bottom: 1px solid var(--rule); }
.step-n { font-family: var(--font-mono); font-size: var(--t-sm); color: var(--mark); letter-spacing: 0.06em; }
.step h3 { font-size: var(--t-h4); color: var(--text); margin-bottom: 9px; }
.step p { font-size: var(--t-sm); color: var(--text-dim); max-width: 64ch; line-height: 1.62; }
@media (max-width: 35em) { .step { grid-template-columns: minmax(0, 1fr); gap: var(--s3); } }

/* Symptom / claim rows */
/* .rows and .steps are <ul>/<ol> as of V5.14: they were visually bulleted and
   numbered lists marked up as divs, so a screen reader got no item count and
   no list navigation. role="list" is required because the global reset removes
   list-style, which strips list semantics in Safari and VoiceOver. */
.rows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5) var(--s8); margin: 0; }
/* V5.22: `.rows { margin: 0 }` resets the UA list margin, and it also beat
   `.mt-8`/`.mt-9` -- same specificity, later in the sheet -- so on twelve
   pages a heading sat FLUSH against the list under it (measured gap: 0px on
   /404.html, /thank-you/, /start-a-project/ and all nine audience pages,
   against an intended 40px or 48px). The reset has to stay; the two utilities
   that are actually used with this component get it back explicitly. */
.rows.mt-8 { margin-top: var(--s8); }
.rows.mt-9 { margin-top: var(--s9); }
.row-i { display: grid; grid-template-columns: 10px minmax(0, 1fr); gap: var(--s4); align-items: start; }
.row-i > i { width: 8px; height: 8px; margin-top: 8px; background: var(--mark); flex: none; }
.row-i.is-warn > i { background: var(--state-warn); }
.row-i b { display: block; font-size: var(--t-body); font-weight: var(--w-semi); color: var(--text); letter-spacing: -0.008em; }
.row-i p { font-size: var(--t-sm); color: var(--text-dim); margin-top: 4px; line-height: 1.55; }
/* Same rule as .card-grid, for the same reason: /start-a-project/, /404.html
   and the nine audience pages all carry an odd number of rows. */
.rows > :last-child:nth-child(odd) { grid-column: 1 / -1; }
@media (max-width: 48.75em) { .rows { grid-template-columns: minmax(0, 1fr); } }

/* Caps list — a dense inventory of technologies, set as a ruled wrap. */
.caps { display: flex; flex-wrap: wrap; gap: 8px; }
.caps li {
  font-size: var(--t-xs); color: var(--text-dim);
  border: 1px solid var(--rule); border-radius: var(--radius-pill);
  padding: 6px 12px; background: var(--surface);
}

/* ==========================================================================
   FAQ
   ========================================================================== */
/* The group is the surface. Boxing each question would be card hell on a page
   that already carries three of them; boxing the SET stops the accordion
   reading as a page break. */
.faq {
  border: 1px solid var(--surface-edge); border-radius: var(--radius-panel);
  background: var(--bg); padding-inline: clamp(20px, 2.4vw, 34px);
}
.faq details { border-bottom: 1px solid var(--rule); }
.faq details:last-child { border-bottom: 0; }
.faq summary {
  /* A single long product name -- "WooCommerce?" -- is wider than this column
     at 200% text zoom on a 320px screen, and pushed the document 44px wide.
     break-word alone does NOT fix it here: summary is display:flex, its text
     is an anonymous flex item, and a flex item will not shrink below its
     min-content width. Only `anywhere` reduces min-content, so that is what
     the narrow query needs. Same normal/anywhere split as .phero-copy h1, and
     for the same reason: `anywhere` above the breakpoint would let a word
     break on a wide screen where there was never any need. */
  overflow-wrap: break-word;
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s5);
  padding: var(--s6) 0; cursor: pointer; list-style: none;
  font-size: var(--t-h4); font-weight: var(--w-semi); color: var(--text);
  letter-spacing: var(--tr-h4);
}
@media (max-width: 44em) {
  .faq summary { overflow-wrap: anywhere; min-width: 0; }
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--link); }
.faq summary::after {
  content: ""; width: 13px; height: 13px; flex: none; margin-top: 6px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1v10M1 6h10' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M6 1v10M1 6h10' stroke='%23000' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat center / contain;
  transition: transform var(--d-state) var(--ease);
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq details > div { padding-bottom: var(--s7); max-width: 68ch; color: var(--text-dim); font-size: var(--t-sm); }
.faq details > div > * + * { margin-top: var(--s4); }

/* ==========================================================================
   WORK
   The browser-chrome bar, the alternating 7/5 split and the per-row framing
   variants are gone with the simplification pass; what is left of .case lives
   in art.css beside the layout that replaced them.
   ========================================================================== */
/* .case, .case-meta and .shot are defined in art.css section 15 and always win
   (same file bundle, later source). The 7/5 alternating grid, the nth-of-type
   flip and the .shot aspect ratio that used to live here had been inert since
   V5.10 -- deleted rather than left to describe a layout that is not on the
   page. */
.case-meta h3 { font-size: var(--t-h3); margin-top: var(--s3); }
.case-meta > p { font-size: var(--t-sm); color: var(--text-dim); margin-top: var(--s4); line-height: 1.62; max-width: 46ch; }
.case-did { flex: 0 0 100%; font-family: var(--font-mono); font-size: var(--t-mono); color: var(--text-faint); letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 2px; }
.case-serv { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--s3); }
.case-serv a {
  font-size: var(--t-xs); border: 1px solid var(--rule); border-radius: var(--radius-pill);
  padding: 5px 11px; color: var(--text-dim);
}

/* The lead project and the roster grid are V5.11 and live in art.css section
   15, beside the .case rules that supersede the ones above. */
.case-serv a:hover { border-color: var(--mark); color: var(--link); }
@media (max-width: 900px) {
  .case, .case:nth-of-type(odd) { grid-template-columns: minmax(0, 1fr); }
  .case-media, .case-meta,
  .case:nth-of-type(odd) .case-media, .case:nth-of-type(odd) .case-meta { grid-column: 1; grid-row: auto; }
  .case-meta { order: 1; }
}

/* ==========================================================================
   EDITORIAL
   ========================================================================== */
.ed-feature { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(28px, 4vw, 64px); align-items: center; }
.ed-kicker {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: var(--t-mono); font-weight: var(--w-semi);
  letter-spacing: var(--tr-eyebrow); text-transform: uppercase; color: var(--mark);
}
.ed-feature h2 { margin-top: var(--s5); font-size: clamp(1.9rem, 1.3rem + 2.2vw, 2.9rem); }
.ed-feature h2 a { color: inherit; }
.ed-feature h2 a:hover { color: var(--link); }
.ed-feature .lead { margin-top: var(--s5); }
.ed-byline {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); align-items: center;
  margin-top: var(--s6); font-size: var(--t-xs); color: var(--text-faint);
}
.ed-byline span { display: inline-flex; align-items: center; gap: 7px; }
.ed-byline span + span::before { content: ""; width: 3px; height: 3px; background: var(--rule-strong); }

.ed-art {
  border: 1px solid var(--rule); border-radius: var(--radius-card); overflow: hidden;
  background: var(--navy); aspect-ratio: 4 / 3; position: relative;
  display: grid; place-items: center;
}
/* V5.11: the old "lit grid" plate -- two line gradients, five radial washes,
   one 128deg diagonal and a bordered label -- was superseded by the SVG plate
   in art.css section 1, which sets `.ed-art::before, .ed-art::after { content:
   none }` and wins on source order. Forty lines of CSS that had not rendered
   since V5.10, including the last diagonal gradient in the system. Deleted
   rather than commented out. */

.ed-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s8) var(--s7); }
.ed-card { display: flex; flex-direction: column; }
.ed-card .ed-art { aspect-ratio: 16 / 10; margin-bottom: var(--s5); }
.ed-card h3 { font-size: var(--t-h4); letter-spacing: var(--tr-h4); }
.ed-card h3 a { color: var(--text); }
.ed-card h3 a:hover { color: var(--link); }
.ed-card p { font-size: var(--t-sm); color: var(--text-dim); margin-top: var(--s3); line-height: 1.55; }
.ed-card .ed-byline { margin-top: auto; padding-top: var(--s5); }
@media (max-width: 900px) {
  .ed-feature { grid-template-columns: minmax(0, 1fr); }
  .ed-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 38.75em) { .ed-grid { grid-template-columns: minmax(0, 1fr); } }

/* Topic collections */
.topics { display: flex; flex-wrap: wrap; gap: var(--s2); }
.topics a {
  font-size: var(--t-sm); color: var(--text-dim);
  border: 1px solid var(--rule); border-radius: var(--radius-pill); padding: 9px 16px;
  background: var(--surface);
  transition: border-color var(--d-tap) var(--ease), color var(--d-tap) var(--ease), background var(--d-tap) var(--ease);
}
.topics a:hover { border-color: var(--mark); color: var(--link); background: var(--film); }

/* ==========================================================================
   ARTICLE
   Full width head, then one readable column. No sidebar anywhere.
   ========================================================================== */
.art-head { padding-block: clamp(40px, 5vw, 72px) clamp(32px, 4vw, 56px); border-bottom: 1px solid var(--rule); }
.art-head h1 { margin-top: var(--s5); max-width: 22ch; }
.art-head .lead { margin-top: var(--s6); max-width: 64ch; font-size: var(--t-lead); }
.art-meta {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s6); align-items: center;
  margin-top: var(--s8); padding-top: var(--s5); border-top: 1px solid var(--rule);
  font-size: var(--t-xs); color: var(--text-faint);
}
.art-meta b { color: var(--text-2); font-weight: var(--w-semi); }

.art-body { padding-block: clamp(40px, 5vw, 72px); }
.art-col { max-width: var(--measure); margin-inline: auto; }
.art-col > * + * { margin-top: var(--s6); }
.art-col p { color: var(--ink-2); line-height: 1.72; }
.art-col h2 { font-size: clamp(1.5rem, 1.2rem + 1.3vw, 2rem); margin-top: var(--s11); letter-spacing: var(--tr-h3); }
.art-col h3 { font-size: var(--t-h4); margin-top: var(--s9); }
.art-col h2 + p, .art-col h3 + p { margin-top: var(--s5); }
.art-col ul, .art-col ol { display: grid; gap: var(--s3); color: var(--ink-2); }
.art-col ul li { position: relative; padding-left: 24px; }
.art-col ul li::before { content: ""; position: absolute; left: 2px; top: 0.75em; width: 9px; height: 1px; background: var(--blue); }
.art-col ol { counter-reset: a; }
.art-col ol li { position: relative; padding-left: 32px; counter-increment: a; }
.art-col ol li::before { content: counter(a) "."; position: absolute; left: 0; color: var(--blue); font-weight: var(--w-semi); }
.art-col a { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.art-col b, .art-col strong { font-weight: var(--w-semi); color: var(--ink); }
.art-col blockquote {
  border-left: 2px solid var(--blue); padding-left: var(--s6);
  font-size: var(--t-lead); line-height: 1.5; color: var(--ink-2);
}
.art-col code {
  font-family: var(--font-mono); font-size: 0.88em;
  background: var(--paper-3); border: 1px solid var(--line);
  padding: 2px 6px; border-radius: var(--radius-chip); color: var(--ink-2);
}
.art-col pre {
  background: var(--navy); color: var(--snow); border-radius: var(--radius-inset);
  padding: var(--s6); overflow-x: auto; font-family: var(--font-mono);
  font-size: var(--t-sm); line-height: 1.6; margin: 0;
}
.art-col pre code { background: none; border: 0; padding: 0; color: inherit; font-size: 1em; }

/* Content allowed to break wider than the column */
.art-wide { max-width: min(1040px, 100%); margin-inline: auto; }
.art-wide .art-note { font-size: var(--t-xs); color: var(--text-faint); margin-top: var(--s3); text-align: center; }
.art-table { /* focusable: see build note — tabindex is set in the renderer */ overflow-x: auto; border: 1px solid var(--rule); border-radius: var(--radius-inset); }
.art-table table { min-width: 520px; font-size: var(--t-sm); }
.art-table th, .art-table td { padding: 12px var(--s5); text-align: left; border-bottom: 1px solid var(--rule); vertical-align: top; }
.art-table th { font-weight: var(--w-semi); color: var(--ink); background: var(--paper-2); font-size: var(--t-xs);
                letter-spacing: 0.04em; text-transform: uppercase; }
.art-table tr:last-child td { border-bottom: 0; }
.art-table td { color: var(--ink-3); }

.art-callout {
  border: 1px solid var(--line-2); border-left: 2px solid var(--blue);
  border-radius: 0 var(--radius-inset) var(--radius-inset) 0; background: var(--paper-2);
  padding: var(--s6) var(--s7);
}
.art-callout b { display: block; color: var(--ink); font-weight: var(--w-semi); margin-bottom: 6px; }
.art-callout p { font-size: var(--t-sm); color: var(--ink-3); line-height: 1.62; }
.art-callout > * + * { margin-top: var(--s4); }

.art-foot { border-top: 1px solid var(--rule); padding-block: clamp(40px, 5vw, 72px); }

/* ==========================================================================
   RELATED / NEXT
   ========================================================================== */
/* One panel with three cells, not three hairline-separated tiles that read as
   an accident of the grid. The 1px gap IS the divider; the border and the
   radius make the group a thing. */
.next { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; background: var(--rule);
        border: 1px solid var(--surface-edge); border-radius: var(--radius-panel); overflow: clip; }
.next > a { background: var(--bg); padding: var(--s7); display: block; transition: background var(--d-state) var(--ease); }
.next > a:hover { background: var(--film); }
.next .card-meta { margin-bottom: var(--s4); }
.next b { display: block; font-size: var(--t-h4); font-weight: var(--w-semi); color: var(--text); letter-spacing: var(--tr-h4); }
.next > a:hover b { color: var(--link); }
.next p { font-size: var(--t-sm); color: var(--text-dim); margin-top: var(--s3); line-height: 1.5; }
/* em: three columns of related links do not fit at 200% text zoom, whatever
   the viewport is. 56em is 896px at the default root. */
@media (max-width: 56em) { .next { grid-template-columns: minmax(0, 1fr); } }

/* ==========================================================================
   CLOSER
   One group, read top to bottom: headline, one line of context, the two
   actions, then the phone number as a sentence. V5.9.1 put the number in a
   bordered card beside the actions and the bottom of every page read like a
   helpdesk. The pixel field (art.css) fills the right of the band; the
   content stays left and grouped at every width rather than stretching to
   meet it. Markup comes from src/closer.py.
   ========================================================================== */
/* The closer.

   V5.9.5 ended every page on navy with the same pixel field the hero opened
   with, so the page finished exactly where it started and the last thing a
   visitor saw was a repeat. It closes on the object instead — the same square
   as the hero, mirrored, entering from the left — over deep blue rather than
   navy, so the sequence resolves somewhere new. */
.closer {
  /* V5.11: the section is a light page ground and the blue is a contained
     PANEL sitting on it. V5.10 painted the blue edge to edge with the hero's
     torn square bleeding off its left corner, on 46 pages -- a dark content
     area and an aggressively shaped blue area in the same band, which is the
     one composition this release exists to remove. */
  position: relative;
}
.closer-panel {
  position: relative;
  isolation: isolate;
  overflow: clip;
  border-radius: var(--radius-feature);
  /* One controlled ramp, orthogonal. Oogle blue into the deep blue, top to
     bottom, so the panel has depth without a second shape in it. The
     background-COLOR is stated separately and is not decoration: a gradient
     alone leaves computed background-color transparent, so white text on this
     panel resolves against the page's white ground -- 1:1 -- for a contrast
     checker, and for a reader in the moment before the gradient paints. */
  background-color: var(--blue);
  background-image: linear-gradient(180deg, var(--blue) 0%, #0B51B4 100%);
  color: #fff;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  align-items: center;
  /* V5.20: the gap and the inline padding are capped lower than they were.
     .wrap stops at 1280, but these kept growing on vw past it, so the action
     column got smaller as the screen got bigger -- 470px at 1440, 455px at
     1920 -- and the two closing buttons, which need 466px, stacked at the
     widest viewport and only there. */
  gap: clamp(28px, 4vw, 56px);
  padding: clamp(44px, 5.5vw, 84px) clamp(28px, 4vw, 56px);
  box-shadow: var(--lift-3);
}
/* .closer-art lives in art.css section 20: anything that positions inside an
   .art-layer has to be declared AFTER it, or .art-layer's inset: 0 wins on
   source order at equal specificity. */
.closer-in, .closer-act { position: relative; z-index: 2; min-width: 0; }
.closer h2 { color: #fff; max-width: 16ch; }
.closer .lead { margin-top: var(--s6); color: #E6F0FF; max-width: 40ch; }
/* The action zone. A darkened inset inside the same boundary rather than a
   second panel: one object, two zones. */
.closer-act {
  background: rgba(4, 12, 30, 0.30);
  border-radius: var(--radius-panel);
  padding: clamp(24px, 2.6vw, 36px);
}
.closer-act .btn-row { display: flex; flex-wrap: wrap; gap: var(--s4); }
/* #CFE2FF measured 4.38:1 on the Oogle blue at 15px. */
.closer-tel { margin-top: var(--s6); font-size: var(--t-sm); color: #DCEBFF; line-height: 1.6; }
.closer-tel .tel { display: inline; color: #fff; font-size: inherit; }
.closer-tel .tel:hover { color: #E6F0FF; }   /* 5.01:1 on --blue; cyan-soft was 3.81 without the inset */
/* White on Oogle blue is its own set of values; the .on-dark token re-point is
   built for navy, so the closer opts out of it and states these directly. */
.closer .btn:not(.btn-ghost) { background: #fff; color: var(--blue-deep); }
.closer .btn:not(.btn-ghost):hover { background: #E6F0FF; color: var(--blue-deep); }
.closer .btn-ghost { border-color: rgba(255, 255, 255, 0.62); color: #fff; }
.closer .btn-ghost:hover { border-color: #fff; background: rgba(255, 255, 255, 0.12); color: #fff; }
.closer :focus-visible { outline-color: #fff; }
.closer .eyebrow { color: #fff; }
.closer .eyebrow::before { background: var(--cyan-soft); }
@media (max-width: 900px) {
  .closer-panel { grid-template-columns: minmax(0, 1fr); }
  .closer .lead { max-width: 46ch; }
  /* Single column: the darkened inset stops being a zone beside the message
     and becomes a box filling the panel, so the strongest colour on the site
     survives only as a margin around a dark rectangle. The actions sit on the
     blue instead. */
  .closer-act { background: none; padding: 0; border-radius: 0; }
}
@media (max-width: 600px) {
  .closer-panel { border-radius: var(--radius-panel); }
  .closer-act .btn { flex: 1 1 100%; }
}

/* ==========================================================================
   PAGE HEAD (interior pages)
   ========================================================================== */
.phead { padding-block: clamp(40px, 5vw, 76px) clamp(48px, 6vw, 88px); }
.phead h1 { margin-top: var(--s6); }
.phead .lead { margin-top: var(--s7); }
.phead-act { display: flex; flex-wrap: wrap; gap: var(--s4); align-items: center; margin-top: var(--s8); }
/* The one surface every interior page already had. V5.11 raises it to the
   panel tier and gives it the padding a panel earns: it is the element that
   makes the top of thirty-four pages read as composed rather than as text set
   next to more text. */
.phead-side { border: 1px solid var(--surface-edge); border-radius: var(--radius-panel); padding: var(--s8); background: var(--surface-2); }
.phead-side .mono { display: block; margin-bottom: var(--s4); }
.phead-side p { font-size: var(--t-sm); color: var(--text-dim); line-height: 1.62; }

/* ==========================================================================
   STICKY MOBILE BAR
   ========================================================================== */
.bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  display: none; gap: 1px; background: var(--line);
  border-top: 1px solid var(--line);
  transform: translateY(102%); transition: transform var(--d-state) var(--ease);
  padding-bottom: env(safe-area-inset-bottom);
}
/* Hidden means hidden. Translating it off-screen left both links in the tab
   order at stops 47 and 48 of 48, focusable and invisible. */
.bar[data-show="0"] { visibility: hidden; }
.bar[data-show="1"] { transform: none; visibility: visible; }
.bar a {
  flex: 1 1 0; min-width: 0; text-align: center;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 15px 10px; font-size: var(--t-sm); font-weight: var(--w-semi);
}
.bar svg { width: 16px; height: 16px; flex: none; }
.bar-call { background: var(--paper); color: var(--ink); }
.bar-go { background: var(--blue); color: #fff; }
.bar-go:hover, .bar-call:hover { color: inherit; }
@media (max-width: 760px) { .bar { display: flex; } body { padding-bottom: 0; } }

/* ==========================================================================
   REVEAL
   One transform, once, on section heads. Motion carries hierarchy here and
   nothing else; it is fully removed under prefers-reduced-motion.
   ========================================================================== */
/* The reveal system lives in art.css section 10, as transitions. */

/* ==========================================================================
   PRINT
   ========================================================================== */
@media print {
  .hdr, .bar, .mnav, .closer, .ftr { display: none !important; }
  body { color: #000; background: #fff; }
  a { color: #000; }
}

/* ==========================================================================
   INDEX ROWS
   A ruled list used where cards would be too heavy: article footers, and
   anywhere a short set of links needs a title and a line of context.
   ========================================================================== */
.index-row {
  display: block; padding: var(--s5) 0;
  border-top: 1px solid var(--rule);
}
.index-row:last-child { border-bottom: 1px solid var(--rule); }

.index-row b {
  display: block; font-size: var(--t-h4); font-weight: var(--w-semi);
  color: var(--text); letter-spacing: var(--tr-h4); line-height: 1.3;
}
.index-row:hover b { color: var(--link); }
.index-row span { display: block; font-size: var(--t-sm); color: var(--text-dim); margin-top: 5px; }

/* ==========================================================================
   ERA TIMELINE
   Four generations of the web, on /about/. A ruled sequence rather than a
   graphic timeline, because the content is the point and a decorative line
   with dots on it would add nothing.
/* The V5.9.5 era strip is deleted. Its .era-year rule set font-family to
   DM Mono, a static 500 face, so the homepage numeral was rendering at 900
   as a synthetic bold. The replacement lives in art.css section 7b. */


/* A legend inside a form, styled to match a field label. */
.f-legend {
  font-size: var(--t-sm); font-weight: var(--w-semi); color: var(--ink-2);
  padding: 0; margin-bottom: 9px;
}

/* Slightly tighter page head, used where a form follows immediately. */
.phead-tight { padding-bottom: clamp(32px, 4vw, 56px); }

/* The Recent column. The category is the visible label and the headline sits
   under it in one clamped line, so three long article titles cannot turn the
   footer into a second homepage. The full headline is still the link text. */
.ftr-recent ul { gap: var(--s4); }
.ftr-cat {
  display: block; font-family: var(--font-mono); font-size: var(--t-mono);
  letter-spacing: var(--tr-mono); text-transform: uppercase;
  color: var(--snow-3); margin-bottom: 2px;
}
.ftr-recent a { display: block; }
/* V5.22 removed a two-line clamp from here. A `-webkit-line-clamp` is a LINE
   count, so it does not grow with the reader's text size: at 100% all three
   recent-article titles fit their two lines exactly (measured: scrollHeight
   equals clientHeight), but at 200% text zoom the longest needed five lines
   and lost 60% of itself with no way to reveal it -- WCAG 1.4.4. Because the
   titles already fit at 100%, dropping the clamp changes nothing at normal
   size and returns the hidden text at large ones. */
.ftr-hl { line-height: 1.35; }

/* At large text sizes a nowrap label is wider than a 320px screen, so below
   this the buttons are allowed to wrap rather than push the page sideways. */
@media (max-width: 30em) {
  .btn { white-space: normal; }
}

/* The intake forms' live region. It carries a count on failure and is empty
   the rest of the time, so it announces once rather than on every keystroke. */
.f-live {
  margin: 0 0 var(--s5);
  font-size: var(--t-sm); font-weight: var(--w-bold);
  color: var(--state-fault);
}
.f-live:empty { display: none; }

/* The drop zone owns its own focus ring: the file input inside it is 32x26,
   opacity 0, and had no visible indicator at all at tab stop 24 of 56. */
.dz:has(input:focus-visible) {
  outline: 2px solid var(--focus); outline-offset: 3px;
}

/* The phone, as an icon, once the number itself no longer fits. */
.tel-mini {
  display: none; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: none;
  border-radius: var(--radius-pill); color: var(--ink-2);
}
.tel-mini svg { width: 18px; height: 18px; }
.tel-mini:hover { color: var(--blue); background: var(--film); }
@media (max-width: 77.5em) { .tel-mini { display: inline-flex; } }

/* ==========================================================================
   PRICE
   Two published starting prices and nowhere for a third to grow. See
   src/price.py for what is published and why.

   THE NUMBER IS NOT A DISPLAY SIZE. --t-h3 normally and --t-h2 at its largest,
   on the page's own ground. A figure set at 72px in a coloured box reads as an
   offer, and this site sells a considered engagement rather than a deal. The
   hierarchy comes from the label above it and the sentence below it, not from
   scale.

   "From" is inside the price element and is styled to be READ. It is smaller
   than the number because it is a smaller word, not because it is fine print:
   it stays at --t-body, in --text-2, above every contrast floor. Nothing here
   may hide it, and no rule may set it to display:none at any width.
   ========================================================================== */
.price {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 8px;
  color: var(--text);
}
.price-from {
  font-size: var(--t-body); font-weight: var(--w-bold);
  color: var(--text-2); letter-spacing: var(--tr-text);
}
.price-n {
  font-size: var(--t-h3); font-weight: var(--w-black);
  letter-spacing: var(--tr-h3); line-height: 1; color: var(--text);
}
.price-u {
  font-size: var(--t-body); font-weight: var(--w-bold); color: var(--text-2);
  margin-left: -4px;
}
/* The one place the figure is allowed to be larger, and --t-h2 is as far as
   it goes. It was --t-display for one build: at 72px on a pale plate it stopped
   being a fact on a page and started being a billboard, which is the exact
   register this site does not work in. A heading size is enough. */
.price-lg .price-n { font-size: var(--t-h2); }
.price-lg .price-from, .price-lg .price-u { font-size: var(--t-lead); }

/* Six of the ten intake options are wider than a 375px select can show, so
   the preselected value arrives truncated on the smallest phones -- and on a
   deep link that is the only confirmation the visitor gets that anything was
   preselected at all. */
@media (max-width: 26em) {
  .f select { font-size: var(--t-xs); }
}

/* The plan a visitor clicked, echoed above the form they landed on. */
.plan-echo {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px;
  padding: var(--s5) var(--s6); margin-bottom: var(--s6);
  background: var(--film); border-radius: var(--radius-card);
  font-size: var(--t-sm); color: var(--text-dim);
}
.plan-echo b { color: var(--text); font-weight: var(--w-bold); }
.plan-echo-p { font-variant-numeric: tabular-nums; }

/* --- the route off a service page ----------------------------------------
   Four lines and a figure, at the foot of every service and audience page.
   Not a second price panel: the two pages that own a plan render the full one
   and would otherwise state their own offer twice. */
.route {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: var(--s7) var(--s10); align-items: center;
  background: var(--surface-2); border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel); padding: clamp(24px, 3vw, 40px);
}
.route-why {
  margin-top: var(--s4);
  font-size: var(--t-lead); font-weight: var(--w-bold);
  color: var(--text); line-height: 1.35; max-width: 26ch;
}
.route-act { border-left: 2px solid var(--keyline); padding-left: var(--s7); }
.route-line { margin-top: var(--s3); font-size: var(--t-sm); color: var(--text-dim); }
.route-alt { font-size: var(--t-sm); color: var(--text-dim); }
@media (max-width: 56em) {
  .route { grid-template-columns: minmax(0, 1fr); gap: var(--s6); }
  .route-act { border-left: 0; border-top: 2px solid var(--keyline);
               padding-left: 0; padding-top: var(--s6); }
}

/* --- the three paths -----------------------------------------------------
   V5.21. The one block whose job is to let a visitor put themselves in a
   category. Each card opens with the visitor's own situation in the eyebrow
   slot, then the product name, then the figure -- situation, name, price, in
   that order, because a visitor reads the first line to find out whether the
   card is about them and stops if it is not.

   The third card carries the word "Quoted" where the other two carry a
   figure. It is set in the same size and weight: "quoted" IS the answer to
   what it costs, and shrinking it would say the third path matters less than
   the two that fit in a package, which is the opposite of true. */
.paths {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s5);
}
.path {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel); padding: var(--s8);
  text-decoration: none; min-width: 0;
  transition: border-color var(--d-state) var(--ease), box-shadow var(--d-state) var(--ease),
              transform var(--d-state) var(--ease);
}
.path:hover {
  border-color: var(--line-2); box-shadow: var(--lift-2); transform: translateY(-2px);
}
.path-when {
  font-size: var(--t-body); font-weight: var(--w-bold); color: var(--text);
  line-height: 1.35; letter-spacing: -0.008em;
}
.path-name {
  display: block; margin-top: var(--s5);
  font-family: var(--font-mono); font-size: var(--t-mono);
  letter-spacing: var(--tr-mono); text-transform: uppercase; color: var(--text-faint);
}
.path .price { margin-top: var(--s3); }
.path p {
  font-size: var(--t-sm); color: var(--text-dim);
  margin-top: var(--s5); line-height: 1.55;
}
.path .arrow { margin-top: auto; padding-top: var(--s6); }
/* The quoted path reads as the same kind of thing as the other two, so it
   keeps the card. A tinted ground would make it look like an upsell. */
.price-word { letter-spacing: var(--tr-h3); }

@media (max-width: 61.25em) {
  .paths { grid-template-columns: minmax(0, 1fr); }
  .path { padding: var(--s6) var(--s7); }
  /* On a phone the three cards are a stack and the question is whether the
     visitor scrolls to the second one. The situation line is what makes them,
     so it gets the room it needs and the padding comes off the sides. */
  .path-when { font-size: var(--t-lead); }
}

/* --- what one plan has that the other does not ---------------------------
   Two columns, not a matrix. The second opens with "Everything in Website
   Care", set apart, because that one fact is what makes the price difference
   make sense and it used to be the ninth item in a nine-item list. */
.cmp { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s8); }
.cmp-col {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel); padding: clamp(24px, 3vw, 40px);
}
.cmp-head { padding-bottom: var(--s6); border-bottom: 1px solid var(--rule); }
.cmp-head .price { margin-top: var(--s3); }
.cmp-when {
  margin-top: var(--s4); font-size: var(--t-sm);
  color: var(--text-dim); font-weight: var(--w-bold);
}
.cmp-col .price-list { flex: 1 1 auto; }
.cmp-carry {
  font-weight: var(--w-bold); color: var(--text) !important;
  padding-bottom: var(--s4); margin-bottom: var(--s2);
  border-bottom: 1px solid var(--rule);
}
.cmp-col .btn { width: 100%; justify-content: center; }

@media (max-width: 46em) {
  .cmp { grid-template-columns: minmax(0, 1fr); gap: var(--s5); }
  /* Stacked, the second plan's price sits 800px below the first, so a reader
     comparing two seven-item lists is holding one of them in their head
     across a screen and a quarter. Sticking each column's head to the top of
     the viewport keeps the name and the figure beside the list they belong
     to for the whole scroll, which is the only part of a side-by-side
     comparison a single column can actually preserve. */
  .cmp-col { position: relative; }
  .cmp-head {
    position: sticky; top: calc(var(--hdr-h) + 8px); z-index: 2;
    background: var(--surface); padding-top: var(--s4); margin-top: calc(var(--s4) * -1);
  }
}

/* --- the pair ------------------------------------------------------------
   Two cards, for the pages choosing between the two PRICED plans rather than
   between all three. /pricing/ and the homepage show the trio. */
.price-pair {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5);
}
.price-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel); padding: var(--s8);
  text-decoration: none; min-width: 0;
  transition: border-color var(--d-state) var(--ease), box-shadow var(--d-state) var(--ease),
              transform var(--d-state) var(--ease);
}
.price-card:hover {
  border-color: var(--line-2); box-shadow: var(--lift-2); transform: translateY(-2px);
}
.price-card .price { margin-top: var(--s4); }
.price-card p {
  font-size: var(--t-sm); color: var(--text-dim);
  margin-top: var(--s5); line-height: 1.55;
}
.price-card .arrow { margin-top: auto; padding-top: var(--s6); }
.price-quote {
  font-size: var(--t-sm); color: var(--text-dim); max-width: 64ch; line-height: 1.6;
}
.price-quote .arrow { display: inline-flex; margin-left: 6px; min-height: 0; }

@media (max-width: 46em) {
  .price-pair { grid-template-columns: minmax(0, 1fr); }
  .price-card { padding: var(--s6); }
}

/* --- the hero line -------------------------------------------------------
   On a service hero the price sits between the lead and the actions: high
   enough to answer "am I in the right range" before the visitor decides
   whether to keep reading, and below the sentence that says what the thing
   actually is, because the number means nothing before that. */
.phero-price {
  margin-top: var(--s7);
  padding-top: var(--s5);
  border-top: 2px solid var(--keyline);
  display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0 10px;
}
.phero-price .price-n { font-size: var(--t-h3); }
.phero-price + .phero-act { margin-top: var(--s6); }
.phero-price-line {
  margin-top: var(--s3);
  font-size: var(--t-sm); color: var(--text-dim);
}

/* --- the panel -----------------------------------------------------------
   What the starting figure includes, and what moves it. Both halves are
   required: a starting price with no boundary is a bait price. */
.price-panel {
  display: grid; grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: clamp(28px, 4vw, 72px);
  align-items: start;
}
.price-panel-head {
  background: var(--paper-2); border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel); padding: clamp(24px, 3vw, 40px);
  position: sticky; top: calc(var(--hdr-h) + 24px);
}
.price-panel-head .price { margin-top: var(--s4); }
.price-line {
  margin-top: var(--s5); font-size: var(--t-body);
  color: var(--text-2); font-weight: var(--w-bold); line-height: 1.5;
  max-width: 26ch;
}
.price-list { display: grid; gap: var(--s3); }
.price-list li {
  position: relative; padding-left: 28px;
  font-size: var(--t-body); color: var(--text-dim); line-height: 1.55;
}
.price-list li::before {
  content: ""; position: absolute; left: 0; top: 8px;
  width: 12px; height: 12px; background: var(--mark);
  -webkit-mask: var(--ck-tick) center / 12px 12px no-repeat;
  mask: var(--ck-tick) center / 12px 12px no-repeat;
}
.price-list li b { color: var(--text); font-weight: var(--w-bold); }
.price-boundary {
  font-size: var(--t-sm); color: var(--text-dim); line-height: 1.6; max-width: 62ch;
}

@media (max-width: 61.25em) {
  .price-panel { grid-template-columns: minmax(0, 1fr); }
  .price-panel-head { position: static; }
}

/* --- how the monthly plan works ------------------------------------------
   V5.19. Four beats and then the two sentences that have to be read
   together, directly under the figure they explain. Numbered because the
   order is the point: it is built, it is paid monthly, it can be stopped,
   and after twelve payments the site is released. Set as an ordered list so
   the numbering survives the stylesheet being switched off, with the drawn
   numeral hidden from assistive tech -- the list already announces them.

   The terms block is a plate, not a footnote in grey 12px. It is the part a
   business owner is deciding on, and small print is how a fair arrangement
   comes to look like an unfair one. */
.model {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s5);
  counter-reset: none;
}
.model-step {
  background: var(--surface); border: 1px solid var(--surface-edge);
  border-radius: var(--radius-panel);
  padding: var(--s7) var(--s6) var(--s7);
  display: flex; flex-direction: column;
}
.model-n {
  font-family: var(--font-mono); font-size: var(--t-mono);
  font-weight: var(--w-mid); letter-spacing: var(--tr-mono);
  color: var(--mark);
  padding-bottom: var(--s4); margin-bottom: var(--s4);
  border-bottom: 2px solid var(--keyline); align-self: flex-start;
  min-width: 26px;
}
.model-step h3 { font-size: var(--t-h4); color: var(--text); }
.model-step p {
  font-size: var(--t-sm); color: var(--text-dim);
  line-height: 1.55; margin-top: var(--s3);
}
.model-terms {
  background: var(--film); border: 1px solid color-mix(in srgb, var(--mark) 22%, transparent);
  border-radius: var(--radius-panel);
  padding: clamp(22px, 2.6vw, 32px);
}
.model-terms-h {
  font-size: var(--t-lead); font-weight: var(--w-bold);
  color: var(--text); line-height: 1.4; max-width: 40ch;
}
.model-terms p + p {
  margin-top: var(--s4);
  font-size: var(--t-body); color: var(--text-dim); line-height: 1.6; max-width: 70ch;
}

@media (max-width: 61.25em) {
  .model { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 34em) {
  .model { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .model-step { padding: var(--s6); }
}
