/* =============================================================================
   Austra Digital v12 — layout/type system measured from the licensed Framer
   reference, recoloured to Austra cobalt, plus the v12 motion/interaction layer
   specified in docs/ANIMATION-BRIEF-v12.md.

   Rules held throughout:
     · every pre-animation hidden state is gated behind .js-enabled, so with JS
       disabled the page renders fully visible
     · .js-fallback (set by the <head> net) force-unhides everything
     · prefers-reduced-motion collapses every sequence to its final visible state
     · no animation of width/height/top/left during scroll — transform+opacity only
   ========================================================================== */

/* Self-hosted Archivo (variable, latin subset). Single file covers 300–800;
   no Google Fonts dependency — fully offline. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('fonts/archivo.woff2') format('woff2');
}

:root {
  /* surfaces */
  --bg:        #fbfbfc;
  --card:      #ffffff;
  --band:      #f1f2f4;
  --ink:       #131519;   /* deep charcoal — CTA band */
  /* text */
  --text:      #24272d;
  --muted:     #4e535b;
  --tertiary:  #666c76;
  /* lines + accent
     v15 keeps v14's monochrome discipline — labels stay silver-grey — and gives
     ONE colour to the things that mean "act" or "active": CTAs, prices, ticks,
     the rail, active states.

     Emerald is applied as a GRADIENT on surfaces (buttons, badges, dots, rails)
     and as a SOLID on text, because a gradient on text makes contrast
     unverifiable. --accent is the solid; --grad-* are the surface stops. */
  --hairline:  #d6d9de;
  --accent:    #3d444e;   /* brushed steel — 9.5:1 on the page, 8.8:1 on band sections */
  --accent-dk: #2f353d;
  --accent-mid:#5a616c;   /* silver-grey, for labels — unchanged from v14 */

  /* SILVER FOLLOWS THE GOLD INVERSION, NOT THE COBALT RULE.
     Polished metal is a LIGHT surface, so it cannot carry white text — it
     carries a near-black label. And silver as TEXT is impossible: a darkened
     silver is just grey, so the solid becomes the brushed steel above.

     v17b lifts the whole ramp. The material still comes from --metal-inset,
     not from colour, but the face now sits close to the page at the lit corner
     and only separates at the shaded one. The SILHOUETTE is carried by the
     bottom stop plus the cast shadow — not by darkening the face.

     Stops vs --on-metal: #f8fafc = 17.3 · #edf1f6 = 16.0 · #cfd6e0 = 12.4.
     Composited under the label, worst case in any state: 13.0:1.
     Darkest composited face pixel vs the page: 1.63:1, down from 2.11:1 in
     v17a — lighter, but still a defined object. An earlier attempt that ENDED
     light (~1.06:1 everywhere) dissolved into the page; this one only STARTS
     light. That is the whole difference. */
  --on-metal:  #14161a;   /* the label colour on any metal surface */
  --gem-lit:   #ffffff;   /* brightest polish — decoration only (hero rail) */
  --grad-a:    #f8fafc;   /* lit corner    — 17.3:1 vs --on-metal */
  --grad-m:    #edf1f6;   /* body          — 16.0:1 */
  --grad-b:    #cfd6e0;   /* shaded corner — 12.4:1, and 1.42:1 vs the page */
  --grad:      linear-gradient(135deg, var(--grad-a) 0%, var(--grad-m) 48%, var(--grad-b) 100%);

  /* Hover brightening, painted on ::after and cross-faded with OPACITY —
     background-image does not interpolate, and opacity is the one property
     this build is happy to animate. Directional, so the shaded corner (and
     therefore the silhouette) survives the hover. */
  --metal-sheen: linear-gradient(135deg,
                   rgba(255, 255, 255, .62) 0%,
                   rgba(255, 255, 255, .22) 46%,
                   rgba(255, 255, 255, 0)  100%);

  --metal-edge:rgba(20, 22, 26, .16);   /* the cut edge — used by .pc-badge */
  --metal-back:#aab2be;   /* fallback body only; 8.5:1 vs --on-metal if ::before
                             ever fails to paint. No longer shows as the rim. */
  --metal-rim: rgba(72, 80, 92, .55);   /* the pressed rim. A rim reads as
                             pressed because it is darker THAN THE FACE, not
                             because it is near-black. Steel, not #3a414b. */

  /* THE MATERIAL. All in em so .btn-sm and .drawer-cta get a proportionally
     smaller bevel. Two dark layers pulled back (.22->.13, .20->.12) and two
     white layers pushed up (.30->.42, .28->.34) — the dome survives, the
     weight does not. */
  --metal-inset:
    -0.05em -0.05em 0.05em 0    inset rgba(20, 22, 26, .13),
     0      0      0.05em 0.2em inset rgba(255, 255, 255, .42),
     0.025em 0.05em 0.1em  0    inset rgba(255, 255, 255, 1),
     0.12em  0.12em 0.12em      inset rgba(255, 255, 255, .34),
    -0.075em -0.22em 0.22em 0.08em inset rgba(20, 22, 26, .12);

  /* PRESSED. Layer 1 is a spread-only inset ring — it draws the rim by
     REPAINTING, not by moving geometry. So `inset` never animates (the file's
     own header rule forbids animating top/left), border-radius draws and
     antialiases the curve itself, and the em unit still scales it. This
     replaces BOTH the reference's clip-path and v17a's inset:-1px->1px. */
  --metal-inset-in:
     0       0      0      0.075em inset var(--metal-rim),
     0.09em  0.13em 0.10em 0       inset rgba(20, 22, 26, .28),
    -0.03em -0.04em 0.07em 0.02em  inset rgba(20, 22, 26, .22),
     0.20em  0.20em 0.20em 0       inset rgba(20, 22, 26, .16),
     0       0      0.05em 0.42em  inset rgba(255, 255, 255, .22),
    -0.075em -0.12em 0.20em 0.10em inset rgba(20, 22, 26, .12);
  --metal-lite:#d8dee6;   /* silver on charcoal — 13.5:1 on the CTA band */
  /* facet colours for the .gem field — LIGHT-SURFACE set (must be darker than
     the page or they vanish). Flipped to a light set on the charcoal band. */
  --gf-a:      #c0c7d1;
  --gf-b:      #9aa3ae;
  --gf-c:      #6f7784;
  --silver:    #c3c9d1;
  --silver-lt: #e7eaee;

  /* radii */
  --r-card:  40px;
  --r-round: 99px;

  /* rhythm */
  --container: 1440px;
  --pad:       96px;
  --sec-gap:   72px;
  --grid-gap:  20px;
  --lift:      48px;      /* section overlap */

  /* INTERACTIVE BOUNDARIES — one ramp, three steps, mirroring the primary's
     rest -> hover -> press escalation. Every value clears 3:1 (WCAG 1.4.11) on
     all three light surfaces the page uses. --hairline (1.37:1 on the page,
     1.26:1 on --band) stays DECORATIVE and is never a control boundary again.

       --edge     3.36 page · 3.47 card · 3.10 band
       --edge-on  4.56 page · 4.72 card · 4.21 band
       --accent   9.51 page · 9.83 card · 8.78 band

     #8b919b (v14's value) was retired from control borders: it measures 2.83:1
     on --band, and .btn-ghost and .faq-ico both sit on --band. */
  --edge:    #848a94;
  --edge-on: #6d747e;

  /* The focus ring colour is a VARIABLE so dark surfaces can re-point it.
     --accent is 9.51:1 on the page but 1.86:1 on the footer ink — a focus
     indicator you cannot see is not a focus indicator. */
  --focus:   var(--accent);

  /* ---- SHADOW TEST (v17e) — REVERT BY SETTING THESE FOUR BACK ---------------
     was: --sh-a .30  --sh-b .34  --sh-hover .40  --sh-lift .26
     Deeper hover shadow on light sections, and the same shape inverted to WHITE
     on dark ones — on --ink a black shadow is invisible, so a raised object on
     the CTA band and the footer had no lift at all. Every consumer reads these
     tokens, so this whole experiment is four numbers wide. */
  --sh-a:      .42;
  --sh-b:      .48;
  --sh-hover:  .58;
  --sh-lift:   .36;
  --sh:        rgba(20, 30, 70, var(--sh-b));
  --sh-tight:  rgba(20, 30, 70, var(--sh-a));
  --sh-deep:   rgba(20, 30, 70, var(--sh-hover));
  --sh-ghost:  rgba(20, 30, 70, var(--sh-lift));

  /* motion */
  --e-out:  cubic-bezier(.22, 1, .36, 1);
  --e-back: cubic-bezier(.34, 1.4, .64, 1);

  /* ---- MARKER FAMILY -------------------------------------------------------
     Every circular marker on this site is one of two things: a STATE DOT with
     nothing inside it, or a MARK DISC that carries a glyph. Before this there
     were 8 diameters, 5 ring specs, 7 transition lists and 5 "active" scales
     across 14 markers. Sizes still differ by role — a 1px-rail dot and a 64px
     numbered token are not the same object — but the GRAMMAR is now shared:
     one radius, one ring rule, three states, one active increment.

     Tier A states:  idle = hairline ring, no fill
                     done = filled silver, silver ring
                     live = metal face + accent ring + halo (an ::after, so it
                            is revealed by OPACITY and can therefore animate) */
  --dot-xs:    8px;    /* inline with text: .status-pill .dot, .ts-dot        */
  --dot-sm:    9px;    /* on the hero rail: .fn-dot — 9 keeps top:3 centred
                          on the 1px rail at 7.5px. Do not change to 10.      */
  --dot-md:    14px;   /* sequence nodes: .sn-dot, .badge-core                */
  --mark-md:   22px;   /* glyph discs: .tick, .cmp-x                          */
  --dot-ring:      2px;     /* >= 12px */
  --dot-ring-thin: 1.5px;   /* <  12px */
  --dot-idle:  var(--hairline);
  --dot-done:  #aab2bd;
  --dot-mute:  #e3e6ea;     /* the recede, on decorative graphics only        */
  --dot-pop:   1.15;   /* tier A */
  --mark-pop:  1.08;   /* tier B — 15% of 22px shoves the baseline, 15% of 8px
                          does not. Equal visual increment, not equal factor. */
  --dot-t: box-shadow .3s linear, background-color .3s linear,
           transform .3s var(--e-back);

  /* The ONLY two hues on this site, and they exist for exactly one component:
     the x/tick comparison, where a red and a green are adjacent and opposed.
     Chosen for LIGHTNESS as much as hue — #a3453b sits at --tertiary's
     greyscale value and #1c5340 at --accent's, so desaturating the page
     preserves the existing "left column is lighter" hierarchy (1.48:1 grey).
     Measured: x 5.07:1 on its disc, tick 7.30:1 on its disc; both >= 3:1 for
     WCAG 1.4.11 and both >= 4.5:1 if ever used as text.
     NOTE: for a protanope these two are 1.11:1 — the SAME COLOUR. The glyph
     shape carries the meaning; colour is a bonus for those who can see it.
     Never reduce these marks to two coloured dots. */
  --mark-no:  #a3453b;
  --mark-yes: #1c5340;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-size: 18px;
  font-weight: 300;
  line-height: 1.5;
}

img, svg { max-width: 100%; }

h1, h2, h3, h4, h5 { margin: 0; font-weight: 600; letter-spacing: normal; }
/* h1-h5 set margin, weight and tracking but never line-height, so 33 heading
   elements were inheriting body's 1.5 — a 34px heading carrying 51px of leading.
   Specificity (0,0,1), so any component that already decided still wins. */
h3, h4, h5 { line-height: 1.2; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }

/* The skip link was a metal surface with no material — background: var(--grad)
   and no shadow, so it read as a flat swatch rather than the same object as the
   button. The bevel is in em and body font-size is 18px, so it scales for free. */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background-image: var(--grad);
  background-color: var(--metal-back);
  color: var(--on-metal);
  padding: 12px 20px; border-radius: 0 0 var(--r-round) 0;
  box-shadow: var(--metal-inset),
              0 0.40em 0.90em -0.55em var(--sh);
}
.skip:focus { left: 0; }   /* :focus, not :focus-visible — a skip link must show
                              for every focus path, and is never mouse-focused. */

/* Focus ring. Drawn 3px OUTSIDE the control, so on .btn-primary it lands on the
   page background, never on the metal face. outline follows border-radius, so
   pills get a pill ring with no clip-path. */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* Arriving at /#how from another page, the browser's native hash jump lands the
   section at 0 — underneath the 112px fixed header. Clicking the same link
   in-page, Lenis lands it at 112. Same anchor, two different results depending
   on how you got there. */
section[id], .cta-band[id], main[id] { scroll-margin-top: 112px; }

/* =============================================================================
   MARKER FAMILY — the shared recipe. Tokens live in :root.
   ========================================================================== */
.status-pill .dot, .ts-dot, .fn-dot, .sn-dot, .badge-core {
  border-radius: var(--r-round);
  background: var(--dot-fill, transparent);
  box-shadow: inset 0 0 0 var(--dot-ring, 2px) var(--dot-line, var(--dot-idle));
  transition: var(--dot-t);
}
.status-pill .dot, .ts-dot { position: relative; display: inline-block; flex: none; }

/* THE LIVE OVERLAY. One rule, one property: "lit" is opacity 1, everywhere.
   A background-image cannot be interpolated, so the metal face has to live on
   a pseudo if any state is ever going to animate — and on the hero rail it is. */
.status-pill .dot::after, .ts-dot::after, .fn-dot::after,
.sn-dot::after, .badge-core::after {
  content: ''; position: absolute; inset: 0;
  border-radius: inherit; pointer-events: none;
  background-image: var(--grad);
  box-shadow: inset 0 0 0 var(--dot-ring, 2px) var(--accent),
              0 0 0 3px rgba(61, 68, 78, .13);
  opacity: 0;
  transition: opacity .3s linear;
}
.status-pill .dot, .ts-dot { width: var(--dot-xs); height: var(--dot-xs); --dot-ring: var(--dot-ring-thin); }
.fn-dot                    { width: var(--dot-sm); height: var(--dot-sm); --dot-ring: var(--dot-ring-thin); }
.sn-dot, .badge-core       { width: var(--dot-md); height: var(--dot-md); }

/* Dark surfaces re-point the token. --metal-lite is 13.50:1 on the footer ink.
   .qform is a WHITE panel nested inside .cta-band, so it must reset to --accent
   (--metal-lite would be 1.35:1 on white). Inheritance handles the nesting. */
.footer, .cta-band { --focus: var(--metal-lite); }
.qform, .form-done { --focus: var(--accent); }

/* ---------- layout primitives ---------- */
.container {
  max-width: var(--container);
  margin: 0 auto;
  padding-left: var(--pad);
  padding-right: var(--pad);
}

.section { padding: var(--sec-gap) 0; position: relative; }

/* Layered section transition: each section rises over the previous one with
   rounded top corners. Backgrounds alternate so the seam is visible. */
.sec-lift {
  border-radius: var(--r-card) var(--r-card) 0 0;
  margin-top: calc(var(--lift) * -1);
  padding-top: calc(var(--sec-gap) + var(--lift));
  background: var(--bg);
  z-index: 1;
}
#system, #process, #team, #faq { background: var(--band); }

/* ---------- type scale ---------- */
/* Was a flat 42px in an otherwise fluid file, which made the page's h1 SMALLER
   than its h2 from 621px to 1167px (ratio 0.81 at 621-900), with an 89% jump
   across the single pixel between 620 and 621. Fluid, so the ladder holds at
   every width. -0.015em tracking re-wraps nothing (verified at 10 viewports —
   negative tracking can only remove wraps, never add them). */
h2 { font-size: clamp(30px, 2.9vw, 42px); font-weight: 600; line-height: 1.05; letter-spacing: -.015em; text-transform: uppercase; }

.eyebrow {
  font-size: 18px; font-weight: 300; line-height: 1.5;
  color: var(--accent-mid); margin-bottom: 14px;
}
.lead { font-size: 18px; font-weight: 300; line-height: 1.5; color: var(--muted); }

.sec-head { max-width: 940px; margin-bottom: 44px; }
.sec-lead { margin-top: 18px; max-width: 68ch; }

/* ---------- buttons ---------- */
.btn {
  position: relative; z-index: 0;
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 18px; font-weight: 400; line-height: 1;
  padding: 18px 30px;
  border-radius: var(--r-round);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color .3s var(--e-out), color .3s var(--e-out),
              border-color .3s var(--e-out), transform .35s var(--e-out),
              box-shadow .35s var(--e-out);
}
.btn-primary {
  isolation: isolate;
  background: var(--metal-back);       /* fallback body only — never seen */
  color: var(--on-metal);
  border-color: transparent;
  font-weight: 500;
  /* the site's shadow family: blue-tinted, wide blur, heavy negative spread.
     In em so .btn-sm scales with it. This is .review's shadow, sized down. */
  box-shadow: 0 0.06em 0.10em -0.045em var(--sh-tight),
              0 0.40em 0.90em -0.55em  var(--sh);
  /* --e-back for the lift (matches .sn-dot / .review), --e-out for the shadow,
     linear for colour. No `ease` — it appears nowhere else in this file. */
  transition: transform  .35s var(--e-back),
              box-shadow .35s var(--e-out),
              color      .3s  linear;
}
/* THE FACE. -1px, not 0: an absolutely positioned pseudo resolves inset
   against the PADDING box, so at inset:0 the .btn 1px border leaks the body.
   inset is now CONSTANT in every state — the press is box-shadow only. */
.btn-primary::before {
  content: ""; position: absolute; inset: -1px; z-index: -1;
  border-radius: inherit;
  background-image: var(--grad);
  box-shadow: var(--metal-inset);
  transition: box-shadow .35s var(--e-out);
}
/* THE SHEEN. The third layer, free on .btn. Opacity only. */
.btn-primary::after {
  content: ""; position: absolute; inset: -1px; z-index: -1;
  border-radius: inherit; pointer-events: none;
  background-image: var(--metal-sheen);
  opacity: 0;
  transition: opacity .35s linear;
}
/* HOVER = lift and brighten, like every other hover on this site. The cast
   shadow GROWS (cards gain shadow on hover here; nothing loses it). Geometry
   does not change, so there is no jitter and no disagreement with .arw, which
   is also moving outward at .38s var(--e-out). */
.btn-primary:hover, .btn-primary:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 0.06em 0.10em -0.045em var(--sh-ghost),
              0 0.72em 1.45em -0.60em  var(--sh-deep);
}
.btn-primary:hover::after, .btn-primary:focus-visible::after { opacity: 1; }

/* .btn:active MUST stay ABOVE .btn-primary:active — identical specificity, so
   the later rule wins. Having it below silently killed the primary's press. */
.btn:active { transform: scale(.985); box-shadow: none; }

/* ACTIVE = the press, which is where the skeuomorphism belongs. The face takes
   the pressed stack including its rim ring, the lift is given back, and the
   button settles onto the page. .985 matches .btn — one press speed for both
   buttons sitting together in .hero-cta. .12s because a press under the finger
   is a state change, not a transition. */
.btn-primary:active {
  transform: translateY(0) scale(.985);
  box-shadow: 0 0.045em 0.09em -0.03em var(--sh);
  transition-duration: .12s;
}
.btn-primary:active::before { box-shadow: var(--metal-inset-in); transition-duration: .12s; }
.btn-primary:active::after  { opacity: .3;                       transition-duration: .12s; }
.btn-sm { font-size: 16px; padding: 12px 22px; }
.btn-block { width: 100%; justify-content: center; }

/* SECONDARY — the primary's gesture, one volume down.
   The old hover filled solid var(--text): a 14.5:1 slab standing next to a metal
   face whose darkest pixel is 1.63:1 against the page. The secondary action became
   the highest-contrast object on the screen, so the eye went to "See client work"
   instead of the primary CTA. And the old REST border was --hairline at 1.37:1 —
   below the 3:1 that identifies a control at all, which is WHY hover had to shout.
   Separation is now by MATERIAL, not gesture: the primary is the only filled
   object with a cast shadow, in every state. */
.btn-ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--edge);      /* 3.36 page · 3.47 card · 3.10 band */
  overflow: hidden;
  transition: color .3s linear, border-color .3s linear,
              transform .35s var(--e-back), box-shadow .35s var(--e-out);
}
/* border-radius on the pseudo, not just overflow:hidden — the curve is then drawn
   and antialiased by border-radius (no clip-path, no rectangular overflow clip). */
.btn-ghost::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: rgba(61, 68, 78, .09);
  transform: scaleX(0); transform-origin: left center;
  transition: transform .4s var(--e-out), box-shadow .35s var(--e-out);
}
/* label 12.54:1 on the composited fill · border 8.24:1 · fill vs page 1.15:1 */
.btn-ghost:hover, .btn-ghost:focus-visible {
  border-color: var(--accent);
  transform: translateY(-2px);
  box-shadow: 0 0.30em 0.60em -0.55em var(--sh-ghost);
}
.btn-ghost:hover::before, .btn-ghost:focus-visible::before { transform: scaleX(1); }

/* MUST declare transform explicitly and MUST stay below :hover. .btn:active is
   further up the file at identical specificity, so a :hover transform with no
   matching :active transform silently kills the press — the same trap that killed
   .btn-primary:active in v17a. */
.btn-ghost:active {
  transform: translateY(0) scale(.985);
  border-color: var(--accent-dk);
  box-shadow: none;
  transition-duration: .12s;
}
.btn-ghost:active::before {
  transform: scaleX(1);
  box-shadow: inset 0 .09em .16em -.02em rgba(20, 22, 26, .18);
  transition-duration: .12s;
}

/* DISABLED — main.js sets #formSubmit.disabled during submit, and there was NO
   disabled style at all: measured live, the button kept cursor:pointer, the full
   metal face, the full cast shadow and a 12.38:1 label — identical to armed.
   opacity is deliberately NOT used: this build's verification counts text below
   opacity 1, and dimming a label is what the five safety nets exist to prevent.
   The light goes out of the MATERIAL instead. Label 12.38 -> 6.54:1, still well
   over AA, and main.js swaps it to "Sending…" so it says so in words too. */
.btn:disabled, .btn[aria-disabled="true"] { cursor: default; }
.btn:disabled:hover, .btn:disabled:active,
.btn[aria-disabled="true"]:hover { transform: none; }

.btn-primary:disabled,
.btn-primary:disabled:hover,
.btn-primary:disabled:focus-visible,
.btn-primary:disabled:active {
  color: var(--muted);
  background: #e9ecf1;
  box-shadow: none;
  transform: none;
}
.btn-primary:disabled::before {
  background-image: none;
  background-color: #e9ecf1;
  box-shadow: 0 0 0 1px inset var(--silver);
}
.btn-primary:disabled::after,
.btn-primary:disabled:hover::after { opacity: 0; }
.btn-primary:disabled .arw::before { transform: none; opacity: 1; }
.btn-primary:disabled .arw::after  { opacity: 0; }

/* arrow swap: existing arrow exits upper-right, replacement enters lower-left */
.arw {
  position: relative; display: inline-block; flex: none;
  width: 1.05em; height: 1.25em; overflow: hidden; line-height: 1.25em;
  font-size: 1em;
}
.arw::before, .arw::after {
  content: '\2192';
  position: absolute; inset: 0;
  text-align: center; line-height: 1.25em;
  transition: transform .38s var(--e-out), opacity .3s linear;
}
.arw::after { transform: translate3d(-110%, 110%, 0); opacity: 0; }
.btn:hover .arw::before, .btn:focus-visible .arw::before { transform: translate3d(110%, -110%, 0); opacity: 0; }
.btn:hover .arw::after,  .btn:focus-visible .arw::after  { transform: none; opacity: 1; }
.arw-sm { font-size: .95em; }

/* =============================================================================
   NAV — transparent rounded shell → translucent blur, hides on scroll-down
   ========================================================================== */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  padding-top: 20px; padding-bottom: 4px;
  transition: transform .35s cubic-bezier(.4, 0, .2, 1), padding-top .3s var(--e-out);
}
.nav-shell {
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  height: 66px; padding: 0 10px 0 22px;
  border: 1px solid transparent; border-radius: var(--r-round);
  transition: background-color .35s var(--e-out), border-color .35s var(--e-out),
              box-shadow .35s var(--e-out), backdrop-filter .35s var(--e-out);
}
.nav.is-stuck { padding-top: 12px; }
.nav.is-stuck .nav-shell {
  background: rgba(252, 252, 252, .84);
  -webkit-backdrop-filter: blur(16px);
          backdrop-filter: blur(16px);
  border-color: var(--hairline);
  box-shadow: 0 12px 34px -22px rgba(20, 30, 70, .38);
}
.nav.is-hidden { transform: translateY(-130%); }

.brand { display: inline-flex; align-items: center; gap: 10px; font-size: 18px; font-weight: 600; }
.brand-mark { width: 18px; height: 18px; border-radius: var(--r-round); background: var(--grad); display: inline-block; }

.nav-links { display: flex; gap: 32px; }
.nav-links a { font-size: 17px; font-weight: 300; color: var(--muted); transition: color .25s linear, transform .25s var(--e-out); display: inline-block; }
.nav-links a:hover, .nav-links a:focus-visible { color: var(--text); transform: translateY(-1px); }
/* a lifted thing must come back down when you press it — same .12s as the button */
.nav-links a:active { transform: translateY(0); transition-duration: .12s; }
.nav-links a.is-active { color: var(--text); }

.nav-right { display: flex; align-items: center; gap: 14px; }

/* Below 900px this is the primary navigation control and it had a 1.37:1
   boundary, no hover and no press. Press is scale(.94), not .985: the site's
   press is ~3px of travel on a 200-330px button, and .985 on a 44px square is
   0.7px — sub-pixel, i.e. no feedback. Constant displacement, not constant ratio. */
.burger {
  display: none;
  width: 44px; height: 44px; border-radius: var(--r-round);
  border: 1px solid var(--edge); background: var(--card);
  cursor: pointer; align-items: center; justify-content: center; flex-direction: column; gap: 5px;
  transition: border-color .25s linear, background-color .25s linear,
              transform .25s var(--e-back);
}
.burger span { display: block; width: 18px; height: 1px; background: var(--text); transition: transform .3s var(--e-out); }
.burger:hover { border-color: var(--edge-on); background: #f3f5f8; }
.burger:active { transform: scale(.94); transition-duration: .12s; }
.burger[aria-expanded="true"] { border-color: var(--accent); }
.burger[aria-expanded="true"] span:first-child { transform: translateY(3px) rotate(45deg); }
.burger[aria-expanded="true"] span:last-child  { transform: translateY(-3px) rotate(-45deg); }

.drawer { position: fixed; top: 86px; left: 0; right: 0; z-index: 99; padding: 0 var(--pad); }
.drawer[hidden] { display: none; }
.drawer-inner {
  background: rgba(252, 252, 252, .96);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border: 1px solid var(--hairline); border-radius: 28px;
  padding: 22px 24px 26px;
  display: flex; flex-direction: column; gap: 16px;
}
.drawer a { font-size: 20px; color: var(--muted); transition: color .25s linear; }
/* nav-links and foot-col both darken on hover AND focus; the drawer had neither. */
.drawer a:not(.btn):hover, .drawer a:not(.btn):focus-visible { color: var(--text); }
.drawer .drawer-cta { align-self: flex-start; color: var(--on-metal); }

/* =============================================================================
   1. HERO
   ========================================================================== */
/* 132, not 156: the nav shell ends at 86px, so this still leaves 46px of clear air
   above the pill, and it is the SAME value the <=900px breakpoint already uses —
   one number for the hero's top inset instead of two. Buys 24px of fold, which is
   what puts the whole rail plus its captions above a 900px viewport. */
.hero { position: relative; overflow: hidden; padding: 132px 0 var(--sec-gap); }
.hero-bg {
  position: absolute; left: -8%; right: -8%; top: -14%; height: 128%;
  background:
    radial-gradient(46% 42% at 72% 12%, rgba(61, 68, 78, .07), transparent 70%),
    radial-gradient(52% 46% at 12% 44%, rgba(120, 130, 155, .12), transparent 72%);
  z-index: 0; pointer-events: none;
}

/* --- faceted gem field ---------------------------------------------------
   HARD-stop conic gradients radiating from a point, like the cut of a stone —
   here reading as polished metal catching light rather than a gem. The drama in a gem comes from sharp luminance jumps
   between adjacent facets, so this is deliberately NOT heavily blurred — a
   soft wash would read as a smudge, not a stone. Kept at low opacity so it is
   a texture behind the layout rather than a picture in front of it.
   Decorative only: no text ever sits on the lit facets. --------------------- */
.gem {
  position: absolute; inset: -25% -12% auto; height: 150%;
  z-index: 0; pointer-events: none;
  opacity: .15;
  filter: blur(26px) saturate(106%);
  /* Masked out of the middle: the facets belong at the edges of the frame, not
     across the headline. Keeps the refraction as peripheral atmosphere. */
  -webkit-mask-image: radial-gradient(115% 95% at 46% 46%, transparent 0%, transparent 34%, #000 82%);
          mask-image: radial-gradient(115% 95% at 46% 46%, transparent 0%, transparent 34%, #000 82%);
  background:
    conic-gradient(from 198deg at 74% 8%,
      var(--gf-a) 0deg 9deg, transparent 9deg 21deg,
      var(--gf-b) 21deg 27deg, transparent 27deg 44deg,
      var(--gf-a) 44deg 51deg, transparent 51deg 68deg,
      var(--gf-c) 68deg 77deg, transparent 77deg 96deg,
      var(--gf-a) 96deg 103deg, transparent 103deg 124deg,
      var(--gf-b) 124deg 132deg, transparent 132deg 360deg),
    conic-gradient(from 22deg at 14% 66%,
      var(--gf-a) 0deg 7deg, transparent 7deg 26deg,
      var(--gf-b) 26deg 34deg, transparent 34deg 55deg,
      var(--gf-a) 55deg 61deg, transparent 61deg 84deg,
      var(--gf-c) 84deg 93deg, transparent 93deg 360deg);
}
.hero .gem { height: 138%; }
/* on the charcoal CTA panel the same cut reads as light trapped inside the stone */
.cta-band .gem {
  /* flipped: on charcoal the facets have to be LIGHTER than the surface */
  --gf-a: #ffffff; --gf-b: #d8dee6; --gf-c: #aab2bd;
  inset: 0 0 auto; height: 100%; opacity: .26; filter: blur(32px) saturate(112%);
  -webkit-mask-image: radial-gradient(110% 100% at 50% 50%, transparent 0%, transparent 26%, #000 76%);
          mask-image: radial-gradient(110% 100% at 50% 50%, transparent 0%, transparent 26%, #000 76%);
}
.hero > .container { position: relative; z-index: 1; }
.hero-copy { position: relative; }

/* THE META ROW. The pill and the eyebrow were stacked: 44 + 26 + 27 + 14 = 111px
   and two lines of text before the promise, both of which say "Melbourne". They are
   the same KIND of thing — orientation, not argument — so they are now one row and
   one glance. align-items: center because the pill is a 44px object and the eyebrow
   a 24px line; baseline would hang the pill. flex-wrap means that below ~1274px the
   eyebrow drops under the pill by itself, with no media query. */
.hero-meta {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 10px 20px;
  margin-bottom: 20px;
}
.hero-meta .status-pill { margin-bottom: 0; }

/* The eyebrow was 18px/300 — the same size AND weight as the lead beneath it and as
   body copy, separated only by 1.45 units of contrast (6.04 vs 7.49:1). That is not
   a label, that is a first sentence. At 16px it matches the pill it now sits beside,
   so the row reads as one piece of furniture. Colour untouched: still 6.04:1. The ch
   cap is removed because it wrapped 16px type to two lines and un-levelled the row. */
.hero .eyebrow { font-size: 16px; line-height: 24px; margin-bottom: 0; max-width: none; }

.status-pill {
  display: inline-flex; align-items: center; gap: 9px;
  border: 1px solid var(--hairline); border-radius: var(--r-round);
  padding: 9px 18px; font-size: 16px; font-weight: 300; color: var(--muted);
  background: var(--card); margin-bottom: 26px;
}
/* "we are open" IS the live state — said in the family's words */
.status-pill .dot::after { opacity: 1; }

/* 52px capped from a 1444px viewport up, against a 42px UPPERCASE h2, is a ratio of
   1.24 — and uppercase carries more apparent mass per line, so the headline was
   barely louder than the section headings below it. 60px takes the ratio to 1.43.
   4.4vw (not 3.6) keeps it growing to the cap instead of stalling early.
   line-height 1.04: 1.1 is body-copy leading and it opened a visible river between
   two 60px lines. max-width 34ch keeps the natural break long-over-short. */
.hero-title {
  font-size: clamp(38px, 4.4vw, 60px);
  font-weight: 600; line-height: 1.04;
  letter-spacing: -0.012em;
  max-width: 34ch; margin: 0 0 24px;
}

/* The flex row put .btn-primary's left edge at x=814.3 while every other line in
   the hero started at x=168.5 — the primary action was 646px off the axis the eye
   is already travelling down, and level with the paragraph's FIRST line rather than
   after it. It also needed 1082px of content width, so it silently collapsed to this
   same stacked layout below a ~1274px viewport: the hero had two personalities
   either side of a width nobody chose. Now it has one. */
.hero-body { display: block; margin-bottom: 32px; }
.hero-body .lead { max-width: 58ch; margin-bottom: 32px; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 14px; }

.trust-strip { display: flex; flex-wrap: wrap; gap: 10px; padding-top: 28px; margin-bottom: 44px; border-top: 1px solid var(--hairline); }
.trust-strip li {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 16px; font-weight: 300; color: var(--tertiary);
  border: 1px solid var(--hairline); border-radius: var(--r-round);
  padding: 8px 18px; background: var(--card);
  transition: color .28s linear, border-color .28s linear;
}
.trust-strip li:hover { color: var(--text); border-color: #aeb6c1; }
.trust-strip li:hover .ts-dot { transform: scale(var(--dot-pop)); }
.trust-strip li:hover .ts-dot::after { opacity: 1; }
/* These are <li>, not controls — the boundary deliberately stays --hairline; a
   3:1 edge would make static decoration look clickable. Only the mechanics change:
   the padding shift was NOT in the transition list, so the content snapped 2px
   while the colour eased over .28s. The transform transition below was already
   declared and never driven by anything — this wires it, which also takes the
   shift off the layout path. */
.trust-strip li > *:not(.ts-dot) { transition: transform .28s var(--e-out); }
.trust-strip li:hover > *:not(.ts-dot) { transform: translateX(2px); }

/* ---------- 2. hero system preview ---------- */
/* 4px scale. The hero's spacing ran 26/14/4/28/40/28/44 — seven values, no common
   step. Now 20/24/32/32/24/32. Hero-scoped so about.html's .page-head is untouched. */
.hero .trust-strip { padding-top: 24px; margin-bottom: 32px; }

/* HORIZONTAL padding stays 40px: .flow-fill's width is a percentage of .flow-rail
   and main.js measures --rail off the dots, so narrowing the card horizontally
   would move both. Only the vertical padding changes. */
.flow-card { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--r-card); padding: 32px 40px; }
.flow-head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; padding-bottom: 22px; border-bottom: 1px solid var(--hairline); margin-bottom: 26px; }
.flow-t { font-size: 22px; font-weight: 600; }
.flow-s { font-size: 16px; font-weight: 300; color: var(--tertiary); }

.flow-rail { position: relative; }
/* The rail draws once on load and then STAYS lit — the glow is the point.
   A soft dot loops along it afterwards. */
.flow-fill {
  position: absolute; top: 7px; left: 7px; height: 1px;
  /* THE LIT LINE MUST END WHERE THE RAIL ENDS. `calc(100% - 14px)` ran it 202px
     (17% of the rail) past "Customer" into empty space — measured: the grey
     track ends at 956.8px, the lit line ended at 1159px. With five equal 1fr
     columns the last dot's left edge is exactly 80% + 0.8 x gap; +8px covers
     the last node's hairline stub, -7px for the left inset. Exact at any
     width, resize-proof, no JS. */
  width: calc(80% + var(--grid-gap) * 0.8 + 1px);
  background: linear-gradient(90deg, var(--accent), var(--gem-lit) 50%, var(--accent));
  box-shadow: 0 0 8px rgba(154, 163, 176, .8);
  z-index: 4;
  transform-origin: left center;
}
.js-enabled .flow-fill { transform: scaleX(0); }
/* THE LIGHT. Same 9x9 box as .fn-dot, so at every beat it sits exactly ON the
   node instead of near it — the light and the lit dot are the same object. */
.flow-laser {
  position: absolute; top: 3px; left: 0;
  width: var(--dot-sm); height: var(--dot-sm); border-radius: var(--r-round);
  background: #fff;
  box-shadow: 0 0 0 2px var(--accent),
              0 0 10px 3px rgba(170, 178, 189, .85),
              0 0 26px 8px rgba(223, 228, 235, .5);
  opacity: 0; z-index: 5; pointer-events: none;
  will-change: transform, opacity;
}
/* comet trail — proportional to the rail, not a fixed 88px */
.flow-laser::before {
  content: ''; position: absolute; right: 60%; top: 50%;
  width: calc(var(--rail, 880px) / 10); height: 3px; margin-top: -1.5px;
  border-radius: var(--r-round);
  background: linear-gradient(90deg, rgba(138, 146, 158, 0), rgba(138, 146, 158, .85));
  /* THE TRAIL WAS LEAVING THE CARD. At 1600: --rail 911px, so the trail is 91.09px
     wide at right: 60% of a 9px light. Parked on "Visitors" (the first 10% of every
     4.8s cycle) its left edge sits at 122.0px while .flow-card's left edge is
     168.5px with overflow: visible — a 46.5px grey streak hanging on the page
     background, on the card whose whole argument is that nothing leaks.
     Fixed with OPACITY, not clipping: clipping the rail would also cut the first
     dot's 3px halo and the lit line's 8px glow. The trail fades in across 10% -> 18%
     of the cycle; the light departs at 10% and by 18% has travelled 135px, so all
     91px of trail is inside the rail before it is visible at all. It also reads
     better — the comet streaks because it is moving, instead of dragging a tail
     while parked. */
  opacity: 0;
}
.flow-rail.is-live .flow-laser::before { animation: flowTrail 4.8s linear infinite; }
@keyframes flowTrail {
  0%, 10%   { opacity: 0 }
  18%, 60%  { opacity: 1 }
  66%, 100% { opacity: 0 }
}

/* CONSTANT VELOCITY. The old cubic-bezier(.62,0,.38,1) made the light crawl to
   node 2 in 1.091s, whip through 3 and 4 in 0.301s each, then crawl 1.091s to
   node 5 — a 3.6x velocity swing, which is why it read as a whip rather than a
   signal moving through a system. Linear across 10%->64% of 4.8s gives five
   equal 648ms beats. The life moves to the ENDS instead: the light fades up
   sitting on "Visitors", holds, travels, then holds lit on "Customer" before
   fading in place. It departs from a node and arrives at a node. */
.flow-rail.is-live .flow-laser { animation: flowLaser 4.8s linear infinite; }
@keyframes flowLaser {
  0%        { transform: translate3d(0, 0, 0);                 opacity: 0; }
  4%, 10%   { transform: translate3d(0, 0, 0);                 opacity: 1; }
  64%, 76%  { transform: translate3d(var(--rail, 0px), 0, 0);  opacity: 1; }
  88%, 100% { transform: translate3d(var(--rail, 0px), 0, 0);  opacity: 0; }
}

/* THE PASS LIGHTS EACH NODE — and they hold lit TOGETHER at the end of every
   cycle before decaying as one. That is the section's whole argument, animated:
   the light travels, and then the system is connected. Crossing times are
   arithmetic, not guesses: node k arrives at 10% + 13.5k of the cycle. */
.flow-rail.is-live .flow-node:nth-child(1) .fn-dot::after { animation: fnLit1 4.8s linear infinite; }
.flow-rail.is-live .flow-node:nth-child(2) .fn-dot::after { animation: fnLit2 4.8s linear infinite; }
.flow-rail.is-live .flow-node:nth-child(3) .fn-dot::after { animation: fnLit3 4.8s linear infinite; }
.flow-rail.is-live .flow-node:nth-child(4) .fn-dot::after { animation: fnLit4 4.8s linear infinite; }
.flow-rail.is-live .flow-node:nth-child(5) .fn-dot::after { animation: fnLit5 4.8s linear infinite; }
/* one class pauses the light and every node light together, always in phase */
.flow-rail.is-paused .flow-laser,
.flow-rail.is-paused .flow-laser::before,
.flow-rail.is-paused .fn-dot::after { animation-play-state: paused; }

@keyframes fnLit1 { 0%,  4%  { opacity: 0 } 9%,    78% { opacity: 1 } 90%, 100% { opacity: 0 } }
@keyframes fnLit2 { 0%, 21.5%{ opacity: 0 } 26.5%, 78% { opacity: 1 } 90%, 100% { opacity: 0 } }
@keyframes fnLit3 { 0%, 35%  { opacity: 0 } 40%,   78% { opacity: 1 } 90%, 100% { opacity: 0 } }
@keyframes fnLit4 { 0%, 48.5%{ opacity: 0 } 53.5%, 78% { opacity: 1 } 90%, 100% { opacity: 0 } }
@keyframes fnLit5 { 0%, 62%  { opacity: 0 } 67%,   78% { opacity: 1 } 90%, 100% { opacity: 0 } }

.flow-track { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--grid-gap); position: relative; z-index: 3; }
.flow-node { position: relative; padding-top: 26px; transition: transform .3s var(--e-out); }
.flow-node::before { content: ''; position: absolute; top: 7px; left: 0; right: 0; height: 1px; background: var(--hairline); transition: background-color .3s linear; }
.flow-node:first-child::before { left: 7px; }
.flow-node:last-child::before  { right: calc(100% - 8px); }
.fn-dot { position: absolute; top: 3px; left: 0; }
/* "is-key" = the parts we own. It used to paint --accent on BOTH the dot and
   the label — the same colour the travelling light uses, so the emphasis and
   the animation were fighting over one meaning. It is now the family's DONE
   state (filled silver); --accent means LIVE and nothing else. The label goes
   back to --text: 14.5:1, up from --accent's 9.5:1. */
.flow-node.is-key .fn-dot { --dot-fill: var(--dot-done); --dot-line: var(--dot-done); }
.fn-lbl { display: block; font-size: 18px; font-weight: 600; margin-bottom: 6px; transition: color .3s linear; }
.fn-side { display: block; font-size: 16px; font-weight: 300; color: var(--tertiary); line-height: 1.45; transition: color .3s linear; }

/* HOVER. Replaces `.flow-track:hover .flow-node { opacity: .65 }`, which dimmed
   real text — the one thing this build promises never to do. The tell now lives
   entirely in the DOT and the CONNECTOR, both aria-hidden decorative graphics
   with no contrast obligation. Every label stays at opacity 1 and its own
   colour, and the ONE text change in the whole interaction makes text DARKER:
   .fn-side 5.1:1 -> 7.5:1. The hovered node LIFTS, because every other hover on
   this site lifts and nothing else here scales a block of type (scaling text
   by 3.5% resamples the glyphs). */
.flow-track:hover .fn-dot            { --dot-line: var(--dot-mute); }
.flow-track:hover .flow-node::before { background: var(--dot-mute); }
.flow-track .flow-node:hover              { transform: translateY(-2px); }
.flow-track .flow-node:hover::before      { background: var(--accent-mid); }
.flow-track .flow-node:hover .fn-dot      { --dot-line: var(--accent); transform: scale(var(--dot-pop)); }
.flow-track .flow-node:hover .fn-dot::after { opacity: 1; }
.flow-track .flow-node:hover .fn-side     { color: var(--muted); }

.flow-foot { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 28px; padding-top: 22px; border-top: 1px solid var(--hairline); }
.tag { font-size: 15px; font-weight: 300; color: var(--tertiary); border: 1px solid var(--hairline); border-radius: var(--r-round); padding: 7px 16px; }
.tag-accent { color: var(--accent); border-color: var(--accent); }
.tag { transition: color .25s linear, border-color .25s linear, transform .3s var(--e-out); }
.tag:hover { color: var(--text); border-color: #aeb6c1; transform: translateY(-2px); }
.tag-accent:hover { color: var(--accent-dk); border-color: var(--accent); }

/* =============================================================================
   3. RECOGNITION
   ========================================================================== */
.recog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--grid-gap); }
.recog-item {
  position: relative; overflow: hidden;
  display: flex; gap: 14px; align-items: flex-start;
  background: var(--card); border: 1px solid var(--hairline);
  border-radius: var(--r-card); padding: 30px 32px;
  transition: transform .3s var(--e-back), border-color .3s linear, box-shadow .3s var(--e-out);
}
.recog-item::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 0% 0%, rgba(61, 68, 78, .07), transparent 62%);
  opacity: 0; transition: opacity .3s linear;
}
.recog-item p { font-size: 18px; font-weight: 300; line-height: 1.5; transition: transform .3s var(--e-out); }
.recog-item:hover { transform: translateY(-6px); border-color: #aeb6c1; box-shadow: 0 16px 34px -20px rgba(20, 30, 70, .3); }
.recog-item:hover::after { opacity: 1; }
.recog-item:hover p { transform: translateY(-2px); }
.recog-item:hover .tick { transform: scale(1.12); }

/* Tier B — a mark disc: a 22px token that CARRIES A GLYPH. Shares the family's
   radius, transition and pop; the size differs because the job differs. */
.tick, .cmp-x {
  flex: none; position: relative;
  width: var(--mark-md); height: var(--mark-md);
  border-radius: var(--r-round);
  transition: transform .3s var(--e-back), background-color .3s linear;
}
.tick { margin-top: 3px; background: rgba(61, 68, 78, .12); }
/* 6x10 at 1.8px = 28.8 stroke-px, matching the cross's 2 x 9 at 1.6px = 28.8.
   Rendered in greyscale the old 5x9 at 1.6px carried 29% LESS ink than the x,
   so the "bad" mark visually outweighed the "good" one. Equalised — and stroke
   mass then becomes a second non-colour channel on top of the shape. */
.tick::after {
  content: ''; position: absolute; left: 6.5px; top: 4px;
  width: 6px; height: 10px;
  border: solid var(--accent); border-width: 0 1.8px 1.8px 0;
  transform: rotate(45deg);
}

.recog-close { margin-top: 34px; max-width: 76ch; font-size: 18px; font-weight: 300; color: var(--muted); }
.recog-close strong { font-weight: 600; color: var(--text); }

/* =============================================================================
   4. GROWTH SYSTEM — sticky, scroll-scrubbed five-stage sequence
   ========================================================================== */
.sys-sec { padding-top: 26px; }
.sys-scroll { position: relative; height: 260vh; }
.sys-sticky { position: sticky; top: 11vh; height: 84vh; display: flex; align-items: center; }
/* The rail column is sized to its content plus breathing room, not to a share of
   the grid. At 1.08fr/.92fr it was 541px wide holding ~130px of ink — 476px of
   void that read as a column that failed to load — while the h2 beside it broke
   to three lines. */
/* 208, not 292. The rail's labels only need 152.7px ("Follow up" + 38px pad);
   the 292 was set by .sys-done's single 274.1px line, which is opacity: 0 until
   the pin is 90% done — so for 90% of the scroll the reader saw 139.3px of void
   (47.7% of the column) beside every label. Measured after: 55.3px. */
.sys-inner { display: grid; grid-template-columns: minmax(0, 1fr) minmax(180px, 208px); gap: 80px; align-items: center; width: 100%; }

/* The section heading rides in the sticky column beside the changing stage. */
.sys-head { margin-bottom: 40px; padding-bottom: 30px; border-bottom: 1px solid var(--hairline); }
.sys-head h2 { font-size: clamp(26px, 2.3vw, 34px); line-height: 1.12; max-width: 23em; }
.sys-lead { margin-top: 14px; max-width: 50ch; font-size: 17px; }

/* Tabular figures so "/ 05" cannot shift sideways when the stage number changes,
   and the live number in --text (13.4:1) rather than silver. */
.sys-count { font-size: 15px; font-weight: 400; letter-spacing: .1em; color: var(--accent-mid); margin-bottom: 14px; font-variant-numeric: tabular-nums; }
.sys-count #sysNum { color: var(--text); font-weight: 600; }
.sys-of { color: var(--tertiary); }

/* 268px was ~100px taller than any real pane, which lifted the whole block and
   left a hole under the tags. Tallest measured: 167px at 1600, 210px at 1024
   (where the tags wrap to two rows). 216px clears both. */
.sys-panes { position: relative; min-height: 226px; }
.sys-pane h3 { font-size: clamp(36px, 3.6vw, 52px); font-weight: 800; line-height: .95; text-transform: uppercase; margin-bottom: 14px; }
/* the promise line and the tags were 20/300/--muted vs 17/300/--muted — two of
   three signals identical. Separated by colour alone: 6.91 -> 13.36:1. */
.sys-pane > p { font-size: 20px; font-weight: 300; color: var(--text); max-width: 46ch; margin-bottom: 26px; }
/* DETERMINISTIC WRAPPING. Convert needs 829.1px for one row and 1280px can never
   offer more than 785, so a single row is unreachable at laptop widths — the fix
   is to make every stage wrap the SAME way instead of only one of them. The safe
   window is 466.7px (below it Convert breaks to three rows) to 590.0px (above it
   Improve fits one row and becomes the odd one out); 528 is the midpoint, +/-11.7%,
   which survives font-metric drift. Above 1400 there is room for all five on one
   row, so the cap is lifted there instead. */
.sys-tags { display: flex; flex-wrap: wrap; gap: 10px; max-width: 528px; }
.sys-tags li {
  font-size: 17px; font-weight: 300; color: var(--muted);
  border: 1px solid var(--hairline); border-radius: var(--r-round);
  padding: 10px 22px; background: var(--card);
  transition: transform .3s var(--e-out), border-color .3s linear, color .3s linear;
}
.sys-pane.is-active .sys-tags li:hover { transform: translateY(-3px); border-color: var(--accent); color: var(--accent); }

/* Panes stack only while JS drives them; with JS off they flow as five blocks. */
.js-enabled .sys-pane {
  position: absolute; inset: 0;
  opacity: 0; transform: translateY(14px); pointer-events: none;
  transition: opacity .45s var(--e-out), transform .45s var(--e-out);
}
.js-enabled .sys-pane.is-active {
  opacity: 1; transform: none; pointer-events: auto;
  transition: opacity .34s linear .1s, transform .5s var(--e-out) .06s;
}
.js-enabled .sys-pane.is-prev { transform: translateY(-14px); }

.sys-rail { position: relative; padding-left: 38px; }
.sys-line { position: absolute; left: 5px; top: 10px; bottom: 10px; width: 2px; background: var(--hairline); }
/* Was --grad-a -> --accent. --grad-a is #f8fafc: 1.07:1 on this section's
   #f1f2f4 band, so the COMPLETED part of the progress line was invisible and
   only its leading edge read as progress. Silver-grey -> steel: 5.58:1 at the
   top, 8.78:1 at the leading edge. */
.sys-line-fill { display: block; width: 100%; height: 0; background: linear-gradient(180deg, var(--accent-mid), var(--accent)); transition: height .18s linear; }
.sys-track { display: grid; gap: 26px; }
.sys-node { position: relative; display: flex; align-items: center; gap: 14px; }
.sn-dot {
  position: absolute; left: -38px; top: 50%; margin-top: -7px;
  /* opaque fill: the .sys-line rail passes UNDER this dot and must be masked */
  --dot-fill: var(--band);
  /* This rail sits on --band, not on white, and the family's "live = metal face"
     renders NEAR-WHITE at 14px there (measured 1.07:1) — it would make the
     active marker the palest thing on the rail while completed dots sat solid.
     So the recipe is kept and its VALUES are re-pointed: idle #868c96 = 3.02:1,
     done --accent-mid = 5.58:1, live --accent = 8.78:1. Same grammar, values
     that survive the surface. */
  --dot-line: #868c96;
}
.sn-lbl { font-size: clamp(20px, 2vw, 26px); font-weight: 600; color: var(--tertiary); transition: color .35s linear, transform .35s var(--e-out); }
.sys-node.is-done .sn-dot { --dot-fill: var(--accent-mid); --dot-line: var(--accent-mid); }
/* Completed stages read as "done, de-emphasised" via COLOUR, never opacity —
   text must always compute to opacity 1. */
.sys-node.is-done .sn-lbl { color: var(--muted); }
.sys-node.is-active .sn-dot {
  --dot-fill: var(--accent);
  transform: scale(1.18);
  /* the ring and the halo in one declaration — a repaint, no geometry moves */
  box-shadow: inset 0 0 0 var(--dot-ring) var(--accent), 0 0 0 6px rgba(61, 68, 78, .13);
}
.sys-node.is-active .sn-lbl { color: var(--text); }
.sys-node:hover .sn-lbl { color: var(--accent); transform: translateX(3px); }
.sys-node:hover .sn-dot { --dot-line: var(--accent); }

/* Tag cascade — TRANSFORM ONLY. The tags never get their own opacity, so this
   adds ZERO new hidden-text surface: their visibility is entirely the pane's.
   Desktop-gated because below 1024px the scroll engine never sets is-active. */
@media (min-width: 1024px) {
  .js-enabled .sys-tags li {
    transform: translateY(12px);
    transition: transform .46s var(--e-out), border-color .3s linear, color .3s linear;
  }
  .js-enabled .sys-pane.is-active .sys-tags li { transform: none; }
  .js-enabled .sys-pane.is-active .sys-tags li:nth-child(1) { transition-delay: .12s; }
  .js-enabled .sys-pane.is-active .sys-tags li:nth-child(2) { transition-delay: .18s; }
  .js-enabled .sys-pane.is-active .sys-tags li:nth-child(3) { transition-delay: .24s; }
  .js-enabled .sys-pane.is-active .sys-tags li:nth-child(4) { transition-delay: .30s; }
  /* (0,5,1) beats the rule above (0,4,1), so hover always wins */
  .js-enabled .sys-pane.is-active .sys-tags li:hover { transform: translateY(-3px); transition-delay: 0s; }
}

@media (min-width: 1400px) {
  .sys-tags  { max-width: none; }
  .sys-panes { min-height: 170px; }
}

.sys-done { margin-top: 34px; padding-top: 22px; border-top: 1px solid var(--hairline); font-size: 18px; font-weight: 600; color: var(--accent); }
.js-enabled .sys-done { opacity: 0; transform: translateY(8px); transition: opacity .5s var(--e-out), transform .5s var(--e-out); }
.js-enabled .sys-scroll.is-complete .sys-done { opacity: 1; transform: none; }

/* =============================================================================
   5. SELECTED CLIENT WORK
   ========================================================================== */
.case-lead { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--r-card); padding: 44px; }
.case-lead-top { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 18px; padding-bottom: 28px; margin-bottom: 30px; border-bottom: 1px solid var(--hairline); }
.case-lead-top h3 { font-size: 30px; font-weight: 600; }
.case-meta { font-size: 16px; font-weight: 300; color: var(--tertiary); }

.case-cols { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--grid-gap); }
.case-col .k { font-size: 16px; font-weight: 600; color: var(--accent-mid); margin-bottom: 10px; }
.case-col p { font-size: 17px; font-weight: 300; line-height: 1.5; color: var(--muted); }

.case-cta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 18px; margin: var(--grid-gap) 0; padding: 26px 34px;
  border: 1px dashed var(--hairline); border-radius: var(--r-card);
}
.case-cta p { font-size: 17px; font-weight: 300; color: var(--muted); }

.case-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--grid-gap); }
.case-mini { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--r-card); padding: 40px; }
.case-sector { font-size: 16px; font-weight: 300; color: var(--accent-mid); margin-bottom: 8px; transition: color .3s linear; }
.case-mini h4 { font-size: 26px; font-weight: 600; margin-bottom: 24px; transition: transform .4s var(--e-out); }
.case-line { padding: 16px 0; border-top: 1px solid var(--hairline); }
.case-line .l { display: block; font-size: 15px; font-weight: 400; color: var(--tertiary); margin-bottom: 6px; }
.case-line .case-body { font-size: 17px; font-weight: 300; line-height: 1.5; color: var(--muted); }
.case-mini:hover h4 { transform: translateX(6px); }

/* =============================================================================
   6. PROCESS — sticky heading, progress line, step activation
   ========================================================================== */
.proc-grid { display: grid; grid-template-columns: .9fr 1.1fr; gap: 72px; align-items: start; }
.proc-head { position: sticky; top: 140px; }

.proc-steps { position: relative; }
.proc-line { position: absolute; left: 31px; top: 40px; bottom: 40px; width: 2px; background: var(--hairline); }
.proc-line-fill { display: block; width: 100%; height: 0; background: linear-gradient(180deg, var(--grad-a), var(--accent)); transition: height .2s linear; }

.pstep {
  position: relative; display: flex; gap: 28px; align-items: flex-start;
  padding: 30px 14px 30px 0; border-top: 1px solid var(--hairline);
  transition: background-color .35s linear;
}
.pstep:first-child { border-top: 0; }
.pnum {
  position: relative; z-index: 1; flex: none;
  width: 64px; height: 64px; border-radius: var(--r-round);
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; font-weight: 700;
  background: var(--band); color: var(--tertiary);
  box-shadow: inset 0 0 0 var(--dot-ring) var(--dot-line, var(--dot-idle));
  transition: background-color .4s var(--e-out), box-shadow .4s var(--e-out),
              color .4s var(--e-out), transform .45s var(--e-back);
}
.pbody h4 { font-size: 24px; font-weight: 600; margin-bottom: 10px; transition: color .35s linear, transform .35s var(--e-out); }
.pbody p { font-size: 17px; font-weight: 300; line-height: 1.5; color: var(--muted); transition: opacity .35s linear, transform .35s var(--e-out); }

.js-enabled .pstep .pnum { transform: scale(.88); }
.js-enabled .pbody h4 { color: var(--tertiary); }
/* was `opacity: .55` — 2.51:1 on real body text, while fully on screen. Only
   scroll progress cleared it, so "Improve" (the step that says we keep working
   after launch) was the greyest thing in the section. The step still arrives:
   the tell is the 10px rise plus the number and heading, all of which already
   animate. Text opacity is not a design tool in this build. */
.js-enabled .pbody p { transform: translateY(10px); }
/* family rule: a live ring is --accent everywhere. --accent is DARKER than the
   --accent-mid v17b used, so the edge against the lightened face gets stronger. */
.js-enabled .pstep.is-on .pnum { background: var(--grad); --dot-line: var(--accent); color: var(--on-metal); transform: scale(1); }
.js-enabled .pstep.is-on .pbody h4 { color: var(--text); }
.js-enabled .pstep.is-on .pbody p { transform: none; }

.pstep:hover { background: rgba(61, 68, 78, .04); }
.pstep:hover .pnum { transform: translateX(4px) scale(1); }
.pstep:hover .pbody h4 { transform: translateX(8px); color: var(--text); }
.pstep:hover .pbody p { transform: none; }

/* =============================================================================
   7. FIX / REBUILD / GROW — sticky card stack
   ========================================================================== */
.cur-toggle {
  display: inline-flex; gap: 4px; margin-top: 22px; padding: 4px;
  border: 1px solid var(--edge); border-radius: var(--r-round); background: var(--card);
}
.cur-btn {
  font: inherit; font-size: 15px; font-weight: 400;
  padding: 8px 18px; border: 0; border-radius: var(--r-round);
  background: transparent; color: var(--tertiary); cursor: pointer;
  transition: background-color .25s linear, color .25s linear,
              transform .25s var(--e-back);
}
/* the unselected segment had no hover state at all — the one genuinely dead
   control on the page. Wash keeps the label at 13.35:1. */
.cur-btn:hover { background: rgba(61, 68, 78, .07); color: var(--text); }
/* scale(.96) for the same reason as .burger: .985 on a ~64px segment is 1px. */
.cur-btn:active { transform: scale(.96); transition-duration: .12s; }
/* The flat --accent fill STAYS. Metal would be the "consistent" answer
   (.pnum.is-on and .pc-badge both use var(--grad) for selected) and was rejected
   on measurement: a metal segment against the white track is 1.42:1, the flat
   fill is 9.83:1. On a segmented control the selected state IS the information. */
.cur-btn.is-on { background: var(--accent); color: #fff; }
.cur-btn.is-on:hover { background: var(--accent-dk); color: #fff; }

.price-stack { padding-bottom: 4vh; }
/* Holds the LAST card (Grow) pinned for a beat instead of letting it scroll
   straight past. Must be an in-flow box, not parent padding — sticky is
   constrained by the containing block's CONTENT box. */
.price-tail { height: 46vh; }
.price-slot { position: sticky; top: 132px; }
.price-slot + .price-slot { margin-top: 52vh; }

.price-card {
  position: relative;
  max-width: 1020px; margin: 0 auto;
  display: grid; grid-template-columns: 1.35fr .85fr; gap: 52px;
  background: var(--card); border: 1px solid var(--hairline);
  border-radius: var(--r-card); padding: 44px 48px;
  box-shadow: 0 24px 60px -40px rgba(20, 30, 70, .5);
  transform-origin: center top;
  transition: transform .45s var(--e-out), border-color .3s linear, box-shadow .4s var(--e-out);
}
.price-card.is-featured { border-color: var(--accent); }
/* ---------- cursor spotlight -------------------------------------------------
   A soft cobalt highlight that follows the pointer inside every card surface.
   Purely decorative: it is opacity on a pseudo-element, never on text, and it
   is skipped entirely on touch and under reduced motion. --------------------- */
.flow-card, .case-lead, .case-mini, .price-card, .review, .team-panel, .quote-block { position: relative; }
.flow-card::after, .case-lead::after, .case-mini::after,
.price-card::after, .review::after, .team-panel::after, .quote-block::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; opacity: 0;
  transition: opacity .4s linear;
  background: radial-gradient(280px circle at var(--mx, 50%) var(--my, 50%),
              rgba(61, 68, 78, .09), transparent 62%);
}
.flow-card:hover::after, .case-lead:hover::after, .case-mini:hover::after,
.price-card:hover::after, .review:hover::after, .team-panel:hover::after,
.quote-block:hover::after { opacity: 1; }

/* Covered-card veil for the sticky stack. Darkens the card WITHOUT touching the
   opacity of any text node — text always computes to opacity 1. */
.price-card::before {
  content: ''; position: absolute; inset: 0; z-index: 3;
  border-radius: inherit; pointer-events: none;
  background: #0e1118; opacity: var(--cover, 0);
}
.price-card:hover { border-color: #aeb6c1; box-shadow: 0 28px 64px -34px rgba(20, 30, 70, .42); }
.price-card.is-featured:hover { border-color: var(--accent); }

.pc-top { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.pc-top h3 { font-size: 34px; font-weight: 700; text-transform: uppercase; }
.pc-badge { font-size: 14px; font-weight: 500; color: var(--on-metal); background: var(--grad); box-shadow: var(--metal-inset), 0 1px 3px rgba(20, 22, 26, .22); border: 1px solid var(--metal-edge); border-radius: var(--r-round); padding: 6px 14px; }
.pc-for { font-size: 17px; font-weight: 300; color: var(--muted); margin-bottom: 24px; }
.pc-list { display: grid; }
.pc-list li {
  font-size: 17px; font-weight: 300; color: var(--muted);
  padding: 12px 0 12px 20px; border-top: 1px solid var(--hairline);
  position: relative; transition: transform .3s var(--e-out), color .3s linear;
}
/* Chevron drawn with borders, not a glyph, so assistive tech has nothing to read. */
.pc-list li::before {
  content: ''; position: absolute; left: 1px; top: 21px;
  width: 5px; height: 5px;
  border: solid var(--accent); border-width: 1.5px 1.5px 0 0;
  transform: rotate(45deg);
}
.price-card:hover .pc-list li { transform: translateX(3px); color: var(--text); }

.pc-side { display: flex; flex-direction: column; justify-content: flex-end; gap: 22px; }
.pc-price { text-align: right; }
.pc-from { display: block; font-size: 15px; font-weight: 300; color: var(--tertiary); margin-bottom: 8px; }
.pc-amt { display: block; font-size: 44px; font-weight: 800; line-height: 1; color: var(--accent); transition: color .3s linear; }
.pc-per { font-size: 18px; font-weight: 300; color: var(--tertiary); }
.pc-alt { display: block; margin-top: 8px; font-size: 13px; font-weight: 300; color: var(--tertiary); }
.price-card:hover .pc-amt { color: var(--accent-dk); }

.price-note { margin-top: 34px; max-width: 82ch; font-size: 16px; font-weight: 300; color: var(--tertiary); }

/* =============================================================================
   8. ONE ACCOUNTABLE TEAM
   ========================================================================== */
.team-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 60px var(--grid-gap); align-items: start; }
.team-copy .lead { margin-top: 18px; max-width: 62ch; }
.team-cta { margin-top: 30px; }

.check-list { margin-top: 30px; display: grid; gap: 14px; }
.check-row { display: flex; gap: 14px; align-items: flex-start; transition: transform .3s var(--e-out); }
.check-row p { font-size: 18px; font-weight: 300; line-height: 1.5; color: var(--muted); transition: color .3s linear; }
.check-row:hover { transform: translateX(3px); }
.check-row:hover p { color: var(--text); }

.team-panel { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--r-card); padding: 44px; position: relative; }
.tp-head { font-size: 16px; font-weight: 600; color: var(--accent-mid); text-transform: uppercase; margin-bottom: 24px; }

.tp-rows { position: relative; padding-left: 24px; }
.tp-line { position: absolute; left: 3px; top: 16px; bottom: 16px; width: 2px; background: var(--hairline); }
.tp-line-fill { display: block; width: 100%; height: 0; background: linear-gradient(180deg, var(--grad-a), var(--accent)); transition: height 1.1s var(--e-out) .25s; }
.tp-rows.is-on .tp-line-fill { height: 100%; }

.tp-row {
  position: relative; overflow: hidden;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px;
  padding: 20px 14px; border-top: 1px solid var(--hairline);
  transition: opacity .4s linear, padding .4s var(--e-out);
}
.tp-row::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(90deg, rgba(61, 68, 78, .10), rgba(61, 68, 78, 0));
  transform: scaleX(0); transform-origin: left center;
  transition: transform .45s var(--e-out);
}
.tp-k { position: relative; z-index: 1; font-size: 22px; font-weight: 600; min-width: 150px; transition: transform .4s var(--e-out), color .3s linear; }
.tp-d { position: relative; z-index: 1; font-size: 17px; font-weight: 300; color: var(--muted); transition: transform .4s var(--e-out), color .3s linear; }
.tp-arw {
  position: relative; z-index: 1; margin-left: auto;
  width: 18px; height: 18px; flex: none;
  border: solid var(--accent); border-width: 1.6px 1.6px 0 0;
  transform: rotate(45deg) translate3d(-6px, 6px, 0); opacity: 0;
  transition: transform .4s var(--e-out), opacity .3s linear;
}
/* was `.tp-rows:hover .tp-row { opacity: .62 }` — the last dim-text hover on
   the site, and it dimmed .tp-k and .tp-d, both real text. The recede now runs
   on the row's hairline only; every label keeps opacity 1 and its own colour,
   and the hovered row's text still goes DARKER (see .tp-k / .tp-d below). */
.tp-rows:hover .tp-row { border-color: var(--dot-mute); }
.tp-rows .tp-row:hover { padding-left: 22px; padding-right: 6px; }
.tp-row:hover::before { transform: scaleX(1); }
.tp-row:hover .tp-k { transform: translateX(8px); color: var(--accent); }
.tp-row:hover .tp-d { transform: translateX(-3px); color: var(--text); }
.tp-row:hover .tp-arw { transform: rotate(45deg) translate3d(0, 0, 0); opacity: 1; }

/* rotating circular badge */
.badge-wrap { position: relative; width: 144px; height: 144px; margin-top: 38px; }
.rot-badge { display: block; width: 144px; height: 144px; }
/* letter-spacing tuned so the string measures 335px against the 339px circular
   path — any longer and the text truncates mid-word. Re-measure if edited. */
.badge-text { font-size: 11px; font-weight: 600; fill: var(--accent); letter-spacing: 0.75px; }
.badge-ring { fill: none; stroke: var(--hairline); stroke-width: 1; }
/* the badge core and an active stage node are now literally the same object */
.badge-core { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); }
.badge-core::after { opacity: 1; }

/* =============================================================================
   9. TESTIMONIALS — static 3-up grid (three real reviews; no autoplay carousel)
   ========================================================================== */
.review-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--grid-gap); }
.review {
  margin: 0; position: relative;
  background: var(--card); border: 1px solid var(--hairline);
  border-radius: var(--r-card); padding: 40px;
  display: flex; flex-direction: column; justify-content: space-between; gap: 24px;
  transition: transform .4s var(--e-back), border-color .3s linear, box-shadow .4s var(--e-out);
}
.rv-mark { font-size: 56px; line-height: .6; font-weight: 700; color: var(--hairline); transition: color .3s linear; }
/* flex: 1 1 auto — the cards are all exactly the same height, but
   space-between was centring the middle child, so the three quotes started at
   99 / 124 / 139px and their attributions at 268 / 290 / 290px. Nothing aligned
   across three identically-sized cards. */
.review blockquote { flex: 1 1 auto; margin: 0; font-size: 20px; font-weight: 300; line-height: 1.45; color: var(--text); }
.review figcaption { display: grid; gap: 4px; padding-top: 24px; border-top: 1px solid var(--hairline); }
.rv-who  { font-size: 17px; font-weight: 600; }
.rv-role { font-size: 15px; font-weight: 300; color: var(--tertiary); }
.rv-src  { font-size: 15px; font-weight: 300; color: var(--accent-mid); transition: color .3s linear; }
.review:hover { transform: translateY(-4px); border-color: #aeb6c1; box-shadow: 0 16px 36px -22px rgba(20, 30, 70, .3); }
.review:hover .rv-mark { color: var(--accent); }
.review:hover .rv-src { color: var(--accent-dk); }

/* =============================================================================
   10. FAQ
   ========================================================================== */
.faq-grid { display: grid; grid-template-columns: .8fr 1.2fr; gap: 72px; align-items: start; }
.faq-head { position: sticky; top: 140px; }
.faq-list { border-top: 1px solid var(--hairline); }
.faq-row { border-bottom: 1px solid var(--hairline); transition: border-color .3s linear; }
.faq-row.is-open { border-bottom-color: var(--accent); }
.faq-row h3 { margin: 0; font-weight: inherit; }
.faq-q {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding: 26px 0; background: none; border: 0; text-align: left; cursor: pointer;
  font: inherit; font-size: 22px; font-weight: 600; color: var(--text);
  transition: padding-left .32s var(--e-out), color .3s linear;
}
.faq-q:hover, .faq-q:focus-visible { padding-left: 6px; color: var(--accent); }
/* the row is a <button> and had no press at all */
.faq-q:active { color: var(--accent-dk); transition-duration: .12s; }
.faq-ico {
  position: relative; flex: none; width: 40px; height: 40px;
  border-radius: var(--r-round); border: 1px solid var(--edge); background: transparent;
  transition: transform .4s var(--e-out), background-color .3s linear, border-color .3s linear;
}
.faq-ico::before, .faq-ico::after {
  content: ''; position: absolute; left: 50%; top: 50%; background: currentColor;
  transition: opacity .3s linear;
}
.faq-ico::before { width: 13px; height: 1.6px; margin: -.8px 0 0 -6.5px; }
.faq-ico::after  { width: 1.6px; height: 13px; margin: -6.5px 0 0 -.8px; }
/* keyboard got the row treatment but not the icon treatment — now both */
.faq-q:hover .faq-ico, .faq-q:focus-visible .faq-ico { background: rgba(61, 68, 78, .09); border-color: var(--accent); }
/* deepen rather than move: .faq-row.is-open already owns .faq-ico's transform */
.faq-q:active .faq-ico { background: rgba(61, 68, 78, .18); transition-duration: .12s; }
.faq-row.is-open .faq-ico { transform: rotate(45deg); border-color: var(--accent); color: var(--accent); }

.faq-a { overflow: hidden; }
.js-enabled .faq-a { height: 0; }
.faq-a-inner { padding: 0 60px 30px 0; }
.faq-a-inner p { font-size: 17px; font-weight: 300; line-height: 1.55; color: var(--muted); }

/* =============================================================================
   11. GROWTH REVIEW / CONTACT — dark panel that widens as it enters
   ========================================================================== */
.cta-band { position: relative; margin-top: 80px; padding: 110px 0 100px; color: #fff; }
.cta-panel-bg {
  position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(160deg, #1b2030 0%, var(--ink) 55%, #101219 100%);
  border-radius: var(--r-card) var(--r-card) 0 0;
  /* Bottom origin: the panel's bottom edge is welded to the footer (its #101219
     stop against the footer's --ink #131519 — the seam is invisible), so scaling
     from the centre would open a light gap underneath. From the bottom the box
     grows UP out of the footer and only the top edge travels. */
  transform-origin: 50% 100%;
}
/* The closed state is JS-ONLY. It used to be an ungated `transform: scaleX(.92)`,
   which with JS disabled left the panel permanently 8% narrow — measured 3px
   clear of the white headline at 390px, i.e. one padding tweak from white text
   on a white page. */
.js-enabled .cta-panel-bg { transform: scale(.915, .938); }
/* Below 1024px the gutter (--pad 24/18px) is smaller than any useful inset, so
   the panel does not breathe at all — which removes that 3px near-miss. */
@media (max-width: 1023px) { .cta-panel-bg { transform: none !important; } }
.cta-band > .container { position: relative; z-index: 1; }
.cta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 60px var(--grid-gap); align-items: start; }
.cta-copy .lead { margin-top: 18px; max-width: 60ch; color: rgba(255,255,255,.72); }
.cta-band h2 { color: #fff; }
.cta-band .eyebrow { color: var(--metal-lite); }
.cta-band .check-row p { color: rgba(255,255,255,.76); }
.cta-band .check-row:hover p { color: #fff; }
.cta-band .tick { background: rgba(216, 222, 230, .20); }
.cta-band .tick::after { border-color: var(--metal-lite); }

.form-wrap { position: relative; }
.qform { background: var(--card); border: 1px solid rgba(255,255,255,.14); border-radius: var(--r-card); padding: 40px; display: grid; gap: 18px; color: var(--text); }
.fstep { display: grid; gap: 18px; }
.js-enabled .qform[data-step="1"] .fstep-2 { display: none; }
.js-enabled .qform[data-step="2"] .fstep-next { display: none; }
.fstep-next { display: none; }
.js-enabled .fstep-next { display: inline-flex; }

.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.field { display: grid; gap: 8px; }
.field label { font-size: 15px; font-weight: 400; color: var(--muted); transition: color .25s linear; }
.field .opt { color: var(--tertiary); font-weight: 300; }
.field input, .field textarea, .field select {
  font-family: inherit; font-size: 17px; font-weight: 300; color: var(--text);
  background: var(--bg);
  border: 1px solid var(--edge); border-radius: 18px;    /* 3.36 / 3.47 */
  padding: 14px 18px; width: 100%;
  transition: border-color .25s linear, box-shadow .25s linear;
}
.field textarea { min-height: 104px; resize: vertical; }
/* HOVER DARKENS. It used to LIGHTEN to #a9aeb6 — 2.16:1 — so pointing at a field
   pushed its boundary BELOW the 3:1 that identifies it as a control. Every other
   hover on this site adds definition; this one removed it. 3.36 -> 4.56. */
.field input:hover, .field textarea:hover, .field select:hover { border-color: var(--edge-on); }
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--accent); outline: none;
  box-shadow: 0 0 0 4px rgba(61, 68, 78, .16);
}
/* The quiet border+halo above is right for a mouse click, but `outline: none` on
   :focus also removed the global ring for KEYBOARD users (0-2-1 beats 0-1-0).
   Keyboard focus now gets the identical ring every other control has. */
.field input:focus-visible, .field textarea:focus-visible, .field select:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 3px;
}
.field:focus-within label { color: var(--text); }
.field input::placeholder, .field textarea::placeholder { color: var(--tertiary); }
/* was #9aa2ad = 2.49:1 — a field became HARDER to see by validating. Now 4.56:1. */
.field.is-valid input, .field.is-valid textarea { border-color: var(--edge-on); }

.form-msg { font-size: 16px; font-weight: 300; color: var(--muted); min-height: 1px; }
.form-msg.ok  { color: var(--accent); }
.form-msg.err { color: #b3261e; }
.form-done { text-align: center; padding: 30px 10px; }
.form-done h3 { font-size: 24px; margin-bottom: 12px; }
.form-done p { font-size: 17px; font-weight: 300; color: var(--muted); }

/* =============================================================================
   FOOTER
   ========================================================================== */
.footer { background: var(--ink); color: rgba(255,255,255,.72); padding: 72px 0 40px; }
.footer .brand { color: #fff; }
.foot-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1.2fr; gap: var(--grid-gap); }
.foot-brand p { margin-top: 16px; max-width: 40ch; font-size: 16px; font-weight: 300; }
.foot-social { display: flex; gap: 10px; margin-top: 20px; }
.foot-social a {
  width: 40px; height: 40px; border-radius: var(--r-round);
  border: 1px solid rgba(255,255,255,.18); background: rgba(255,255,255,.04);
  display: flex; align-items: center; justify-content: center; color: rgba(255,255,255,.75);
  transition: color .25s linear, border-color .25s linear, transform .3s var(--e-out);
}
.foot-social a:hover, .foot-social a:focus-visible { color: #fff; border-color: rgba(255,255,255,.55); transform: translateY(-2px); }
.foot-social a:active { transform: translateY(0); transition-duration: .12s; }

.foot-col h5 { font-size: 16px; font-weight: 600; text-transform: uppercase; margin-bottom: 16px; color: #fff; }
.foot-col li { margin-bottom: 10px; font-size: 16px; font-weight: 300; }
.foot-col a { transition: transform .3s var(--e-out), color .25s linear; display: inline-block; }
.foot-col a:hover, .foot-col a:focus-visible { color: #fff; transform: translateX(3px); }
.foot-col a:active { transform: translateX(0); transition-duration: .12s; }

/* Registered so --glow can be TRANSITIONED. Only the fade uses it; the spot's
   position comes from --mx/--my and tracks the pointer with no lag, which is
   what "followed" means. Unsupported browsers just snap the fade on and off. */
@property --glow { syntax: '<number>'; inherits: false; initial-value: 0; }

.foot-word {
  margin: calc(64px - .085em) 0 calc(64px - .10em); overflow: hidden;
  /* CENTRED — the owner's instinct, and the measurement agrees. "AUSTRA DIGITAL"
     needs 8.48em of measure and the container gives at most 6.57em, so this has
     always been a TWO-LINE LOCKUP at every viewport from 390 to 2560, and the two
     lines are within 5% of each other in width. That is a near-square MARK, not a
     heading — and a mark centres. Left-aligned it left 438px (35%) of the footer
     empty on the right, under a CONTACT column that fills it.
     text-align centres the ADVANCE box, not the ink: -.02em tracking leaves
     non-ink trailing space and the sidebearings are unequal (A opens at 1px, D at
     13px), so the ink landed +2.03px / +6.71px right of centre — a 13.4px error on
     line two. box-sizing is border-box, so padding-right moves the centring axis
     left by HALF its value: .04em corrects to a 5.8px residual on a 1248px
     measure (0.47%). The em margins correct the vertical the same way — line-height
     .9 crops the leading, so a flat 56/40 read as 72/59; this measures optical
     64px above and below at every viewport.
     The glow still tracks: --mx/--my map to the padding box, which stays 1248px
     either way, and the no-pointer 50% 50% fallback now lands dead centre of the
     mark instead of inside "AUSTRA". */
  /* SPLIT TO BOTH EDGES — Austra left, Digital right. This is the answer to the
     centre-vs-fill argument: "AUSTRA DIGITAL" needs 8.48em against a 6.57em
     container, so it was always going to be two lines. Centred, it balanced the
     void but abandoned the x=169 rail every other section holds. Justified to the
     edges it FILLS the measure and anchors BOTH sides — and it stops being a
     heading that happens to wrap and becomes a designed lockup.
     No optical padding needed: each word is flushed to a real edge rather than
     centred on an advance box, so the sidebearing error has nowhere to land. */
  /* Two lines by necessity (8.48em of measure into a 6.57em container), so each
     word gets its own line and its own edge: AUSTRA flush left on the x=169 rail,
     DIGITAL flush right on the container edge. Flex + space-between does NOT do
     this — once the words wrap, each flex LINE justifies independently and both
     land left. Block lines with the second right-aligned is the only thing that
     actually reaches both edges. */
  display: block;
  font-size: clamp(46px, 12vw, 190px); line-height: .9;
  font-weight: 800; text-transform: uppercase; letter-spacing: -.02em;
  color: transparent;
  /* Two layers, both clipped to the glyphs. The base gradient is UNCHANGED, and
     the radial only ever ADDS white — at --glow: 0 its alpha is 0 and every
     pixel is byte-identical to the old rest state. The wordmark can never get
     darker than it was. */
  background-image:
    radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, calc(.40 * var(--glow, 0))) 0%,
      rgba(255, 255, 255, calc(.15 * var(--glow, 0))) 45%,
      rgba(255, 255, 255, 0) 74%),
    linear-gradient(100deg, rgba(255,255,255,.16), rgba(255,255,255,.07) 46%, rgba(255,255,255,.24));
  -webkit-background-clip: text; background-clip: text;
  /* Only the custom property animates. Transitioning `background` would smear
     the spot 600ms behind the pointer — the opposite of following it. */
  transition: --glow .5s linear;
}
.foot-word > span { display: block; }
.foot-word > span:last-child { text-align: right; }
.foot-word:hover { --glow: 1; }

.foot-bar {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px;
  margin-top: 40px; padding-top: 26px; border-top: 1px solid rgba(255,255,255,.14);
  font-size: 15px; font-weight: 300; color: rgba(255,255,255,.55);
}

/* underline sweep on nav + footer links */
.nav-links a, .foot-col a, .drawer a:not(.btn) { position: relative; }
.nav-links a::after, .foot-col a::after, .drawer a:not(.btn)::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px;
  background: currentColor;
  transform: scaleX(0); transform-origin: left center;
  transition: transform .32s var(--e-out);
}
.nav-links a:hover::after, .nav-links a:focus-visible::after, .nav-links a.is-active::after,
.foot-col a:hover::after, .foot-col a:focus-visible::after,
.drawer a:not(.btn):hover::after,
.drawer a:not(.btn):focus-visible::after { transform: scaleX(1); }

/* =============================================================================
   MOTION LAYER — pre-animation hidden states (JS-gated)
   ========================================================================== */
.js-enabled [data-reveal]:not([data-revealed]) { opacity: 0; transform: translateY(24px); }
.js-enabled [data-reveal-lines]:not([data-revealed]) { opacity: 0; }
.js-enabled [data-hero-step]:not([data-revealed]):not([data-reveal-lines]) { opacity: 0; transform: translateY(14px); }

/* line-mask scaffolding for heading reveals — unwrapped again once revealed,
   so reflow on resize can never clip a line. */
.line-mask { display: block; overflow: hidden; padding-bottom: .09em; margin-bottom: -.09em; }
.line-inner { display: block; will-change: transform; }

/* hover: card lift */
.case-mini, .case-lead {
  transition: transform .45s var(--e-back), box-shadow .45s var(--e-out), border-color .25s linear;
}
.case-mini:hover, .case-lead:hover {
  transform: translateY(-5px);
  box-shadow: 0 18px 40px -22px rgba(20, 30, 70, .3);
  border-color: #aeb6c1;
}
.case-mini:hover .case-sector { color: var(--accent-dk); }

.flow-card, .hero-bg { will-change: transform; }

/* Net #5 — the head script un-gates everything if the module never signals ready. */
.js-fallback [data-reveal]:not([data-revealed]),
.js-fallback [data-reveal-lines]:not([data-revealed]),
.js-fallback [data-hero-step]:not([data-revealed]),
.js-fallback .sys-done { opacity: 1 !important; transform: none !important; }
.js-fallback .sys-pane { position: static !important; opacity: 1 !important; transform: none !important; pointer-events: auto !important; }
.js-fallback .sys-tags li { transform: none !important; }
.js-fallback .faq-a { height: auto !important; }
.js-fallback .pbody h4 { color: var(--text) !important; }
.js-fallback .pbody p { opacity: 1 !important; transform: none !important; }
.js-fallback .pstep .pnum { transform: none !important; }
.js-fallback .cta-panel-bg { transform: none !important; }
.js-fallback .flow-fill { transform: scaleX(1) !important; }
.js-fallback body { cursor: auto !important; }


/* =============================================================================
   v13 ADDITIONS — before/after comparison, case links, radio choices,
   sticky mobile CTA. Placed BEFORE the responsive + reduced-motion blocks so
   those still win.
   ========================================================================== */

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- before / after comparison ---------- */
.compare { border-top: 1px solid var(--hairline); }
.cmp-head { display: grid; grid-template-columns: 1fr 1fr; gap: var(--grid-gap); padding: 18px 18px 14px; }
.cmp-h { font-size: 14px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; }
.cmp-h-old { color: var(--tertiary); }
.cmp-h-new { color: var(--text); }

.cmp-rows { display: grid; }
/* row-gap: 0 is load-bearing. `gap` was set on BOTH axes though it was only ever
   the column gutter; with one grid row it never showed. The label adds a second
   row, and the inherited 20px then costs 120px of page height across six rows —
   measured 122px per row with it, 102px without. */
.cmp-row {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--grid-gap);
  border-top: 1px solid var(--hairline);
  transition: background-color .3s linear;
}
.cmp-row:hover { background: rgba(61, 68, 78, .035); }
/* One line per cell, so the x and the tick share a baseline and the row reads
   ACROSS again. The left cell is now an h3 — it carries the symptom in the
   reader's words, which is both the heading and the content, so there is nothing
   left to duplicate underneath it. */
.cmp-old, .cmp-new {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 18px; font-size: 17px; line-height: 1.5;
  transition: transform .35s var(--e-out), color .3s linear;
}
.cmp-old { font-weight: 600; font-size: 18px; }
.cmp-new { font-weight: 300; }
.cmp-old { color: var(--tertiary); }
.cmp-new { color: var(--text); background: var(--card); border-radius: 20px; }
/* Both marks respond; only the connected column moves. Before this, .tick had a
   hover pop and .cmp-x — its opposite number, 20px away in the same row — had
   no transition at all. */
.cmp-row:hover .cmp-new { transform: translateX(3px); }
.cmp-row:hover .cmp-new .tick { transform: scale(var(--mark-pop)); }
.cmp-row:hover .cmp-old .cmp-x { transform: scale(var(--mark-pop)); }

/* .cmp-label and .cmp-n retired. Once the label moved INTO the left cell the
   ordinal and the x were fighting for the same 24px gutter — and the x carries
   meaning (for a protanope the red and green measure 1.11:1, so the glyph IS the
   meaning) while the ordinal carried ceremony only. */

/* THE RED AND THE GREEN. Scoped to this component only: these are the only two
   hues on the site, and they exist where a red and a green are adjacent and
   opposed. The green is NOT applied to .tick globally — the tick is reused in
   the check-list and on the charcoal CTA band, where #1c5340 is 2.05:1 and
   would force a second green into existence. */
.cmp-x { margin-top: 2px; background: rgba(163, 69, 59, .10); }
.cmp-x::before, .cmp-x::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 9px; height: 1.6px; background: var(--mark-no); margin: -.8px 0 0 -4.5px;
}
.cmp-x::before { transform: rotate(45deg); }
.cmp-x::after  { transform: rotate(-45deg); }
.cmp-new .tick { background: rgba(28, 83, 64, .12); }
.cmp-new .tick::after { border-color: var(--mark-yes); }

/* ---------- case-study outbound links ---------- */
.case-link {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 15px; font-weight: 400; color: var(--accent);
  border-bottom: 1px solid transparent;
  transition: border-color .25s linear, color .25s linear;
}
.case-link:hover, .case-link:focus-visible { border-bottom-color: var(--accent); color: var(--accent-dk); }
.case-mini h4 { min-height: 2.4em; }
.case-slot { display: block; min-height: 24px; margin: -14px 0 18px; }
.case-mini .case-link { margin: 0; }

/* ---------- pricing scope note ---------- */
.pc-note {
  margin-top: 14px; text-align: right;
  font-size: 14px; font-weight: 300; line-height: 1.45; color: var(--tertiary);
}

/* ---------- form: step counter, radio choices, trust line ---------- */
.fstep-count {
  font-size: 12px; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--accent-mid);
}
.choices { border: 0; padding: 0; margin: 0; min-width: 0; }
.choices legend { font-size: 15px; font-weight: 400; color: var(--muted); padding: 0; margin-bottom: 10px; }
.choice {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 16px; margin-bottom: 8px;
  border: 1px solid var(--edge); border-radius: 16px;
  font-size: 16px; font-weight: 300; line-height: 1.4; cursor: pointer;
  transition: border-color .2s linear, background-color .2s linear,
              transform .2s var(--e-out);
}
.choice:last-child { margin-bottom: 0; }
/* same inversion as the inputs: hover was #a9aeb6 = 2.16:1. Now 3.36 -> 4.56. */
.choice:hover { border-color: var(--edge-on); }
.choice:active { transform: scale(.99); transition-duration: .12s; }
.choice input {
  appearance: none; -webkit-appearance: none;
  flex: none; width: 18px; height: 18px; margin: 1px 0 0;
  border: 1px solid var(--edge); border-radius: var(--r-round);
  background: var(--card);
  transition: border-color .2s linear, box-shadow .2s linear;
}
.choice input:checked { border-color: var(--accent); box-shadow: inset 0 0 0 4px var(--accent); }
.choice:hover input { border-color: var(--edge-on); }
.choice:has(input:checked) { border-color: var(--accent); background: rgba(61, 68, 78, .06); }
/* keyboard: the whole card shows where you are, not just the 18px dot — so
   arrowing through the group is legible at a glance. */
.choice:has(input:focus-visible) { border-color: var(--accent); background: rgba(61, 68, 78, .06); }
.form-trust {
  font-size: 14px; font-weight: 300; line-height: 1.5;
  color: var(--tertiary); text-align: center;
}

/* ---------- sticky mobile CTA ---------- */
.sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 95;
  display: none;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
  transform: translateY(130%);
  transition: transform .38s var(--e-out);
}
.sticky-cta.is-on { transform: none; }
.sticky-cta[hidden] { display: none !important; }
.sticky-cta-in {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 10px 10px 10px 20px;
  background: rgba(252, 252, 252, .95);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border: 1px solid var(--hairline); border-radius: var(--r-round);
  box-shadow: 0 14px 34px -18px rgba(20, 30, 70, .45);
}
.sticky-cta-txt { display: grid; line-height: 1.25; min-width: 0; }
.sticky-cta-txt strong { font-size: 15px; font-weight: 600; }
.sticky-cta-txt span {
  font-size: 12px; color: var(--tertiary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sticky-cta .btn-sm { flex: none; white-space: nowrap; }


/* ---------- cursor: dot + lagging ring -------------------------------------
   Ported from the v1 build. The dot uses mix-blend-mode: difference, so it
   inverts whatever is under it — dark on the warm-white sections, light on the
   charcoal CTA band and footer — without any JS colour switching. The native
   cursor is hidden on desktop pointer devices only, and restored over text
   inputs so the I-beam is never lost. ------------------------------------- */
.cursor-dot, .cursor-ring {
  position: fixed; left: 0; top: 0; z-index: 300;
  border-radius: var(--r-round);
  pointer-events: none; opacity: 0;
  transform: translate3d(-50%, -50%, 0);
  will-change: transform;
}
.cursor-dot {
  width: 12px; height: 12px;
  background: #fff; mix-blend-mode: difference;
  transition: width .28s var(--e-out), height .28s var(--e-out), opacity .3s linear;
}
.cursor-ring {
  width: 38px; height: 38px; z-index: 299;
  border: 1px solid rgba(61, 68, 78, .55);
  transition: width .3s var(--e-out), height .3s var(--e-out),
              border-color .3s linear, opacity .3s linear;
}
.cursor-dot.is-on { opacity: 1; }
.cursor-ring.is-on { opacity: .9; }
.cursor-dot.is-link { width: 46px; height: 46px; }
.cursor-ring.is-link { width: 12px; height: 12px; border-color: rgba(61, 68, 78, .95); }

/* MUST be JS-gated: with JS disabled there is no replacement dot, so hiding the
   native cursor would leave the page with no pointer at all. */
@media (min-width: 1024px) and (hover: hover) and (pointer: fine) {
  .js-enabled body { cursor: none; }
  /* never take the I-beam away from something you type into */
  input, textarea, select, [contenteditable] { cursor: auto; }
}
@media (max-width: 1023px), (hover: none) {
  .cursor-dot, .cursor-ring { display: none !important; }
}


/* =============================================================================
   INTERIOR PAGES — case studies, about, client-work index
   ========================================================================== */
.page-head { position: relative; overflow: hidden; padding: 156px 0 var(--sec-gap); }
.page-head > .container { position: relative; z-index: 1; }
.crumb { font-size: 15px; font-weight: 300; color: var(--tertiary); margin-bottom: 22px; }
.crumb a { color: var(--muted); border-bottom: 1px solid transparent; transition: border-color .25s linear, color .25s linear; }
.crumb a:hover, .crumb a:focus-visible { color: var(--accent); border-bottom-color: var(--accent); }
.crumb span { margin: 0 8px; color: var(--hairline); }

.page-title {
  font-size: clamp(32px, 3.4vw, 50px); font-weight: 600; line-height: 1.12;
  max-width: 20ch; margin: 4px 0 24px;
}
.page-lead { font-size: 19px; max-width: 62ch; }
.page-note { margin-top: 20px; max-width: 62ch; font-size: 16px; font-weight: 300; color: var(--tertiary); }
.page-link { margin-top: 24px; font-size: 16px; }
.page-head .trust-strip { margin-top: 34px; margin-bottom: 0; }

/* key facts */
.fact-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--grid-gap); margin-bottom: 56px; }
.fact {
  display: grid; gap: 8px; align-content: start;
  background: var(--card); border: 1px solid var(--hairline);
  border-radius: 24px; padding: 24px 26px;
  transition: transform .35s var(--e-back), border-color .25s linear;
}
.fact:hover { transform: translateY(-4px); border-color: #aeb6c1; }
.fact-k { font-size: 13px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--accent-mid); }
.fact-v { font-size: 17px; font-weight: 300; line-height: 1.45; color: var(--text); }

/* article body */
.prose { max-width: 74ch; }
.prose h2 { font-size: clamp(26px, 2.4vw, 34px); line-height: 1.15; margin: 56px 0 18px; }
.prose h2:first-child { margin-top: 0; }
.prose p { font-size: 18px; font-weight: 300; line-height: 1.65; color: var(--muted); margin-bottom: 18px; }
.prose p strong, .prose li strong { color: var(--text); font-weight: 600; }
.prose em { font-style: italic; }
.prose-list { display: grid; gap: 14px; margin: 6px 0 20px; }
.prose-list li {
  position: relative; padding-left: 26px;
  font-size: 18px; font-weight: 300; line-height: 1.6; color: var(--muted);
}
.prose-list li::before {
  content: ''; position: absolute; left: 2px; top: 11px;
  width: 6px; height: 6px; border: solid var(--accent); border-width: 1.6px 1.6px 0 0;
  transform: rotate(45deg);
}
.prose-note {
  margin-top: 26px; padding: 22px 26px;
  background: var(--card); border: 1px solid var(--hairline); border-radius: 24px;
  font-size: 17px; color: var(--muted);
}

.quote-block {
  margin: 34px 0; padding: 32px 34px;
  background: var(--card); border: 1px solid var(--hairline); border-radius: var(--r-card);
  transition: transform .4s var(--e-back), border-color .25s linear;
}
.quote-block:hover { transform: translateY(-4px); border-color: #aeb6c1; }
.quote-block blockquote { margin: 0 0 18px; font-size: 20px; font-weight: 300; line-height: 1.5; color: var(--text); }
.quote-block figcaption { display: grid; gap: 4px; padding-top: 18px; border-top: 1px solid var(--hairline); }

/* client-work index cards */
.cs-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--grid-gap); }
.cs-card {
  position: relative; display: flex; flex-direction: column; gap: 14px;
  background: var(--card); border: 1px solid var(--hairline);
  border-radius: var(--r-card); padding: 40px;
  transition: transform .45s var(--e-back), border-color .25s linear, box-shadow .45s var(--e-out);
}
.cs-card::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0;
  transition: opacity .4s linear;
  background: radial-gradient(280px circle at var(--mx, 50%) var(--my, 50%), rgba(6, 95, 70, .08), transparent 62%);
}
.cs-card:hover { transform: translateY(-5px); border-color: #aeb6c1; box-shadow: 0 18px 40px -22px rgba(20, 30, 70, .3); }
.cs-card:hover::after { opacity: 1; }
.cs-sector { font-size: 15px; font-weight: 300; color: var(--accent-mid); }
.cs-card h2 { font-size: 28px; font-weight: 600; text-transform: none; line-height: 1.15; transition: transform .4s var(--e-out); }
.cs-card:hover h2 { transform: translateX(5px); }
.cs-body { font-size: 17px; font-weight: 300; line-height: 1.55; color: var(--muted); }
.cs-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.cs-tags li { font-size: 14px; font-weight: 300; color: var(--tertiary); border: 1px solid var(--hairline); border-radius: var(--r-round); padding: 6px 14px; }
.cs-go { margin-top: auto; padding-top: 18px; display: inline-flex; align-items: center; gap: 8px; font-size: 16px; color: var(--accent); }

/* prev / next */
.page-next {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px;
  margin-top: 48px; padding-top: 26px; border-top: 1px solid var(--hairline);
}
.page-next a {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 17px; color: var(--muted);
  transition: color .25s linear, transform .3s var(--e-out);
}
.page-next a:hover { color: var(--accent); transform: translateX(3px); }

.about-panel { margin: 24px 0 12px; }


/* =============================================================================
   SCREENSHOTS — real client sites in a light browser frame
   The frame is CSS, not baked into the JPEG, so it stays crisp and can be
   restyled without recapturing. Images are 1200x750 (2x the card slot).
   ========================================================================== */
.shot {
  margin: 0; position: relative;
  border: 1px solid var(--hairline); border-radius: 22px;
  background: var(--card); overflow: hidden;
  transition: border-color .3s linear, box-shadow .45s var(--e-out), transform .45s var(--e-back);
}
.shot-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 11px 14px; border-bottom: 1px solid var(--hairline);
  background: linear-gradient(180deg, #fbfbfc, #f4f5f7);
}
.shot-bar i { width: 8px; height: 8px; border-radius: var(--r-round); background: #d8dade; display: block; }
.shot-bar em {
  margin-left: 10px; font-style: normal;
  font-size: 12px; font-weight: 300; color: var(--tertiary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.shot-img { position: relative; overflow: hidden; line-height: 0; }
.shot-img img { display: block; width: 100%; height: auto; }

/* mask reveal — JS-gated, so with JS off the image is simply visible */
.js-enabled .shot-img img { transform: scale(1.06); clip-path: inset(100% 0 0 0); }
.js-enabled .shot.is-in .shot-img img {
  transform: none; clip-path: inset(0 0 0 0);
  transition: transform 1.15s var(--e-out), clip-path 1s var(--e-out);
}
.js-fallback .shot-img img { transform: none !important; clip-path: none !important; }

.shot:hover { border-color: #aeb6c1; box-shadow: 0 18px 40px -24px rgba(20, 30, 70, .35); }
.js-enabled .shot.is-in:hover .shot-img img { transform: scale(1.035); transition: transform .6s var(--e-out); }

/* placement */
.case-lead .shot, .case-mini .shot, .cs-card .shot { margin-bottom: 26px; }
.cs-card .shot { margin-bottom: 4px; }
.page-shot { margin: 0 0 56px; }
.page-shot .shot-img img { max-height: 620px; object-fit: cover; object-position: top center; }

.work-strip { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--grid-gap); margin: 22px 0 12px; }
.work-strip a { display: grid; gap: 10px; }
.work-strip span { font-size: 15px; font-weight: 400; color: var(--muted); transition: color .25s linear; }
.work-strip a:hover span { color: var(--accent); }

/* =============================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 1180px) {
  :root { --pad: 56px; }
  .recog-grid { grid-template-columns: repeat(2, 1fr); }
  .fact-grid { grid-template-columns: repeat(2, 1fr); }
  .case-cols { grid-template-columns: repeat(2, 1fr); }
  .review-grid { grid-template-columns: 1fr; }
  .foot-grid { grid-template-columns: 1fr 1fr; }
  .sys-inner { gap: 44px; }
  .price-card { grid-template-columns: 1fr; gap: 30px; padding: 38px 34px; }
  .pc-side { justify-content: flex-start; }
  .pc-price { text-align: left; }
  .pc-note { text-align: left; }
  .case-row { grid-template-columns: repeat(2, 1fr); }
}

/* Below 1024px every pinned/sticky sequence becomes ordinary vertical content. */
@media (max-width: 1023px) {
  .sys-scroll { height: auto; }
  .sys-sticky { position: static; height: auto; display: block; }
  .sys-inner { grid-template-columns: 1fr; gap: 0; }
  /* Hide the RAIL, not the whole right column — .sys-done ("One connected
     customer journey") lives inside .sys-right, so `.sys-right { display: none }`
     deleted the section's closing line from every phone AND from anyone with
     reduced motion on. With one column it simply becomes the last row. */
  .sys-rail { display: none; }
  .sys-done { margin-top: 8px; padding-top: 24px; }
  .sys-count { display: none; }
  .sys-panes { position: relative; min-height: 0; padding-left: 30px; }
  /* fade both ends rather than letting the line dangle past the first/last dot */
  .sys-panes::before {
    content: ''; position: absolute; left: 5px; top: 0; bottom: 0; width: 2px;
    background: linear-gradient(180deg, transparent 0,
                var(--hairline) 34px, var(--hairline) calc(100% - 34px), transparent 100%);
  }
  .js-enabled .sys-pane,
  .sys-pane { position: relative; inset: auto; opacity: 1; transform: none; pointer-events: auto; padding: 26px 0; }
  /* the mobile stand-in for an active .sn-dot — same object, same live look */
  .sys-pane::before {
    content: ''; position: absolute; left: -30px; top: 34px;
    width: var(--dot-md); height: var(--dot-md); border-radius: var(--r-round);
    background-image: var(--grad);
    box-shadow: inset 0 0 0 var(--dot-ring) var(--accent), 0 0 0 3px rgba(61, 68, 78, .13);
  }
  .sys-pane h3 { font-size: 34px; }
  .sys-pane > p { font-size: 18px; margin-bottom: 18px; }
  .js-enabled .sys-done { opacity: 1; transform: none; }

  .proc-grid, .faq-grid { grid-template-columns: 1fr; gap: 36px; }
  .proc-head, .faq-head { position: static; }

  .price-slot { position: static; }
  .price-slot + .price-slot { margin-top: var(--grid-gap); }
  .price-tail { display: none; }
  .price-card { transform: none !important; max-width: none; }
}

@media (max-width: 900px) {
  :root { --pad: 24px; --sec-gap: 56px; --lift: 32px; }
  .nav-links { display: none; }
  .burger { display: flex; }
  .hero { padding-top: 132px; }
  .team-grid, .cta-grid, .case-row { grid-template-columns: 1fr; }
  .team-panel { order: -1; }
  .flow-track { grid-template-columns: 1fr; gap: 22px; }
  .flow-node { padding-top: 0; padding-left: 24px; }
  .flow-node::before { top: 8px; bottom: -22px; left: 4px; right: auto; width: 1px; height: auto; }
  .flow-node:last-child::before { display: none; }
  .flow-fill, .flow-laser { display: none; }
  .gem { display: none; }   /* large blurred conics are not worth the paint cost on a phone */
  .fn-dot { top: 6px; }
  .flow-track .flow-node:hover { transform: none; }
  .case-cta { flex-direction: column; align-items: flex-start; }
  .page-head { padding-top: 132px; }
  .cs-grid { grid-template-columns: 1fr; }
  .sticky-cta { display: block; }
  /* the bar covers the bottom of the page — keep the footer clear of it */
  .footer { padding-bottom: 108px; }
}

@media (max-width: 620px) {
  :root { --pad: 18px; }
  body { font-size: 17px; }
  h2 { font-size: 30px; }
  .recog-grid, .case-cols, .foot-grid, .row2 { grid-template-columns: 1fr; }
  .hero-title { font-size: clamp(32px, 9vw, 46px); }
  .flow-card, .case-lead, .case-mini, .price-card, .team-panel, .qform, .review, .recog-item, .case-cta {
    padding: 26px 22px; border-radius: 28px;
  }
  .pstep { gap: 18px; padding: 24px 0; }
  .pnum { width: 52px; height: 52px; font-size: 17px; }
  .proc-line { left: 25px; }
  .faq-q { font-size: 19px; gap: 14px; }
  .fact-grid, .work-strip { grid-template-columns: 1fr; }
  .fact, .quote-block, .prose-note, .cs-card { padding: 24px 22px; border-radius: 24px; }
  .prose p, .prose-list li { font-size: 17px; }
  .page-next { flex-direction: column; }
  .cmp-head { display: none; }
  .cmp-row { grid-template-columns: 1fr; gap: 0; padding-bottom: 14px; }
  .cmp-old, .cmp-new { padding: 14px; font-size: 16px; }
  .cmp-old { font-size: 17px; }
  /* At this width the bar has one job: one tap. Drop the sub-line rather than
     let it ellipsise, and let the label use the space it needs. */
  .sticky-cta { padding: 8px 12px calc(8px + env(safe-area-inset-bottom, 0px)); }
  .sticky-cta-in { padding: 8px 8px 8px 16px; gap: 10px; }
  /* Below 620 the label has nowhere to go without wrapping badly — drop it and
     let the bar be exactly one full-width tap target. */
  .sticky-cta-txt { display: none; }
  .sticky-cta-in { padding: 8px; }
  .sticky-cta .btn-sm { width: 100%; justify-content: center; font-size: 16px; padding: 14px 18px; }
  .faq-a-inner { padding-right: 10px; }
  .hero-cta .btn, .qform .btn, .drawer .drawer-cta { width: 100%; justify-content: center; }
  .hero-cta { width: 100%; }
  .brand { white-space: nowrap; }
  .nav-right .btn-sm { display: none; }   /* the CTA lives in the drawer at this width */
  .nav-shell { padding-left: 16px; }
  .drawer { padding: 0 18px; }
  .tp-row { padding: 18px 8px; }
  .tp-k { min-width: 0; }
}

/* Touch devices: hover-only affordances must be permanently visible. */
@media (hover: none) {
  .flow-card::after, .case-lead::after, .case-mini::after,
  .price-card::after, .review::after, .team-panel::after { display: none; }
  .tp-rows:hover .tp-row { opacity: 1; }
  .tp-arw { opacity: .55; transform: rotate(45deg); }
}

/* =============================================================================
   REDUCED MOTION — everything stays visible, nothing animates
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
  .rot-badge, .hero-bg, .flow-card, .cta-panel-bg { transform: none !important; }

  .js-enabled [data-reveal]:not([data-revealed]),
  .js-enabled [data-reveal-lines]:not([data-revealed]),
  .js-enabled [data-hero-step]:not([data-revealed]),
  .js-enabled .sys-done,
  .btn:active { opacity: 1 !important; transform: none !important; }

  /* sticky sequences collapse to plain vertical content */
  .sys-scroll { height: auto !important; }
  .sys-sticky { position: static !important; height: auto !important; display: block !important; }
  .sys-inner { grid-template-columns: 1fr !important; }
  .sys-rail { display: none !important; }
  .js-enabled .sys-tags li { transform: none !important; }
  .sys-count { display: none !important; }
  .js-enabled .sys-pane, .sys-pane {
    position: relative !important; inset: auto !important;
    opacity: 1 !important; transform: none !important; pointer-events: auto !important;
    padding: 22px 0 !important;
  }
  .price-slot { position: static !important; }
  .price-slot + .price-slot { margin-top: var(--grid-gap) !important; }
  .price-tail { display: none !important; }
  .price-card { transform: none !important; }
  .proc-head, .faq-head { position: static !important; }
  .js-enabled .pbody h4 { color: var(--text) !important; }
  .js-enabled .pbody p { opacity: 1 !important; transform: none !important; }
  .js-enabled .pstep .pnum { transform: none !important; }
  .faq-a { height: auto !important; }
  .sys-line-fill, .proc-line-fill, .tp-line-fill { height: 100% !important; }
  .flow-fill { transform: scaleX(1) !important; }
  .flow-laser { display: none !important; animation: none !important; }
  .cursor-dot, .cursor-ring { display: none !important; }
  body { cursor: auto !important; }
  .gem { display: none !important; }
  /* the press is a transition, not an animation, but it still needs pinning */
  /* The press is a state change, not an animation, but it still needs pinning.
     Hover keeps its brightening (static, instant, visible — the same call this
     file already makes for nav underlines) and loses its lift. */
  .btn-primary::before, .btn-primary::after { transition: none !important; }
  .btn-primary:hover, .btn-primary:focus-visible { transform: none !important; }
  .btn-primary:hover::after, .btn-primary:focus-visible::after { opacity: 1; }
  .btn-primary:active { transform: none !important; }
  .js-enabled .shot-img img { transform: none !important; clip-path: none !important; }
  .shot:hover { box-shadow: none !important; }
  .flow-card::after, .case-lead::after, .case-mini::after,
  .price-card::after, .review::after, .team-panel::after { opacity: 0 !important; }

  /* hover lifts are removed, not left mid-transform */
  .case-mini:hover, .case-lead:hover, .review:hover, .recog-item:hover, .price-card:hover {
    transform: none !important; box-shadow: none !important;
  }
  /* every transform introduced by the control-system pass, pinned. The washes,
     borders and colour changes all survive — static and instant, the same call
     this file already makes for the primary's sheen and the nav underlines. */
  .flow-track .flow-node:hover { transform: none !important; }
  .flow-rail.is-live .flow-laser,
  .flow-rail.is-live .fn-dot::after { animation: none !important; }
  .btn-ghost:hover, .btn-ghost:focus-visible { transform: none !important; box-shadow: none !important; }
  .btn-ghost:active, .choice:active, .cur-btn:active, .burger:active,
  .nav-links a:active, .foot-col a:active, .foot-social a:active { transform: none !important; }
  .trust-strip li:hover > *:not(.ts-dot) { transform: none !important; }
  .arw::before, .arw::after { transition: none !important; }
  .btn:hover .arw::before, .btn:focus-visible .arw::before { transform: none !important; opacity: 1 !important; }
  .btn:hover .arw::after, .btn:focus-visible .arw::after { opacity: 0 !important; }
  .nav-links a::after, .foot-col a::after, .drawer a:not(.btn)::after { transform: scaleX(0) !important; }
  .nav-links a:hover::after, .nav-links a.is-active::after,
  .foot-col a:hover::after, .drawer a:not(.btn):hover::after { transform: scaleX(1) !important; }
}

/* =============================================================================
   SELECTED CLIENT WORK — four application windows            (v17c)
   MUST BE LAST IN THE FILE. This block carries its own responsive and
   reduced-motion tails, and they only work if nothing follows them.

   Three ideas:
     1. the window controls are the .btn-primary material at control size —
        same --grad face, same --metal-inset bevel, same lift/sheen/press
        grammar, just 28px across. The decorative dots stay FLAT, so "raised
        metal = you can press it" is the affordance, not an icon convention.
     2. maximise is a grid takeover, not an overlay. Nothing is ever hidden,
        so the section reads identically to a crawler in every state.
     3. the disintegrate is TRANSFORM ONLY. The panel is painted out by a
        gradient in the section's own background colour sliding across it —
        erasing to --bg looks exactly like dissolving and costs one composited
        translate, where mask-position/mask-size/filter all cost a repaint.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   MACHINED CONTROLS — .btn-primary's material, control-sized.
   The bevel is in em (as in :root), so font-size alone scales the whole thing.
   ------------------------------------------------------------------------ */
.mctl {
  position: relative; isolation: isolate;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: inherit; font-size: 15px; font-weight: 500; line-height: 1;
  color: var(--on-metal);
  padding: 12px 20px;
  border: 0; border-radius: var(--r-round);
  background: var(--metal-back);           /* fallback body only — never seen */
  cursor: pointer;
  box-shadow: 0 .06em .10em -.045em var(--sh-tight),
              0 .40em .90em -.55em  var(--sh);
  transition: transform .35s var(--e-back), box-shadow .35s var(--e-out);
}
.mctl::before {                            /* THE FACE */
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background-image: var(--grad);
  box-shadow: var(--metal-inset);
  transition: box-shadow .35s var(--e-out);
}
.mctl::after {                             /* THE SHEEN — opacity only */
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background-image: var(--metal-sheen);
  opacity: 0; pointer-events: none;
  transition: opacity .35s linear;
}
.mctl:hover, .mctl:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 .06em .10em -.045em var(--sh-ghost),
              0 .60em 1.20em -.60em  var(--sh-deep);
}
.mctl:hover::after, .mctl:focus-visible::after { opacity: 1; }
.mctl:active           { transform: scale(.94);                     transition-duration: .12s; }
.mctl:active::before   { box-shadow: var(--metal-inset-in);         transition-duration: .12s; }
.mctl:active::after    { opacity: .3;                               transition-duration: .12s; }

/* Icon variant: 44px hit area, 28px disc. The face lives on ::before, so the
   pressable box can be bigger than the control the eye sees — a 28px circle
   with a 44px target, without faking it with margins. */
.mctl-ico {
  width: 44px; height: 44px; padding: 0; font-size: 28px;
  background: transparent; box-shadow: none;
}
.mctl-ico::before, .mctl-ico::after { inset: 8px; }
.mctl-ico::before {
  box-shadow: var(--metal-inset),
              0 .06em .10em -.045em var(--sh-tight),
              0 .40em .90em -.55em  var(--sh);
}
.mctl-ico:hover, .mctl-ico:focus-visible { box-shadow: none; }
.mctl-ico:hover::before, .mctl-ico:focus-visible::before {
  box-shadow: var(--metal-inset),
              0 .06em .10em -.045em var(--sh-ghost),
              0 .60em 1.20em -.60em  var(--sh-deep);
}
.mctl-ico:active::before { box-shadow: var(--metal-inset-in); }
.mctl-ico:focus-visible  { outline-offset: -6px; }   /* ring hugs the disc, not the target */

/* glyphs — CSS only, --on-metal on the metal face is 12.4:1 at worst */
.mg { position: relative; display: block; }
.mg-max { width: .40em; height: .34em; border: .055em solid var(--on-metal); border-radius: .07em; }
[aria-expanded="true"] > .mg-max::after {            /* restore-down */
  content: ''; position: absolute; left: 0; right: 0; top: 0;
  height: .075em; background: var(--on-metal);
}
.mg-x { width: .44em; height: .44em; }
.mg-x::before, .mg-x::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: .44em; height: .06em; margin: -.03em 0 0 -.22em;
  border-radius: .03em; background: var(--on-metal);
}
.mg-x::before { transform: rotate(45deg); }
.mg-x::after  { transform: rotate(-45deg); }

/* ---------------------------------------------------------------------------
   THE BOARD
   Six columns so one grid serves three states: 4-up at span 3 (2x2),
   3-up at span 2 once something is maximised, and full width at 1 / -1.
   ------------------------------------------------------------------------ */
.wins {
  /* MUST MATCH THE SECTION BACKGROUND. #results is .sec-lift => var(--bg),
     verified computed as rgb(251,251,252). If this section ever moves onto
     --band, change these three and nothing else. */
  --win-erase:      #fbfbfc;
  --win-erase-soft: rgba(251, 251, 252, .62);
  --win-erase-none: rgba(251, 251, 252, 0);

  display: flex; flex-wrap: wrap; gap: var(--grid-gap);

  /* The FLIP inverts positions with translate3d. In a ONE-ROW layout a dismissed
     cell travels from x=634 to x=0, so its 1248px dock bar is translated 634px
     back out to the right for half a second. MEASURED: 281px of document overflow
     mid-dismiss without this line, 0px with it. The 2x2 grid measured 0 — the row
     introduced it.
     `clip`, not `hidden`: it creates no scroll container and no new containing
     block, and both shadows in this component have NEGATIVE horizontal extent
     (blur/2 - spread = -2px), so nothing visible is cut. */
  overflow-x: clip;
}
/* Flex, not a 4-track grid: when a window is dismissed its cell becomes a
   full-width dock bar and the survivors must re-share the row. A fixed grid
   leaves a hole; `flex: 1 1 0` re-shares for free (3 survivors at 403px each). */
.win-cell { flex: 1 1 0; min-width: 0; position: relative; display: flex; flex-direction: column; }
.wins .win-cell.is-max { flex: 1 1 100%; order: -1; }
.win-cell.is-min       { align-self: flex-start; }
.win-cell.is-gone      { flex: 1 1 100%; order: 2; }
.wins.has-max .win-cell.is-gone { grid-column: 1 / -1; order: 2; }
.win-cell.is-gone .win          { display: none; }

.win {
  position: relative; isolation: isolate;
  flex: 1; display: flex; flex-direction: column; min-width: 0;
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  overflow: hidden;               /* clips the eraser AND the shot's corners */
  transition: transform .45s var(--e-back), box-shadow .45s var(--e-out), border-color .25s linear;
}
.win:hover { transform: translateY(-5px); box-shadow: 0 18px 40px -22px rgba(20, 30, 70, .3); border-color: #a8bdb4; }
.win.is-max:hover, .win.is-going:hover { transform: none; }

/* cursor spotlight, same treatment as every other card surface */
.win::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; opacity: 0; z-index: 4;
  transition: opacity .4s linear;
  background: radial-gradient(280px circle at var(--mx, 50%) var(--my, 50%),
              rgba(61, 68, 78, .09), transparent 62%);
}
.win:hover::after { opacity: 1; }

/* ---------------------------------------------------------------------------
   WINDOW CHROME
   Same gradient and hairline as .shot-bar, so the frame is the site's own
   browser frame promoted to the panel. The dots stay flat and inert; only the
   two controls are raised metal.
   ------------------------------------------------------------------------ */
.win-bar {
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: 12px;
  min-height: 62px; padding: 6px 10px 6px 30px;
  border-bottom: 1px solid var(--hairline);
  background: linear-gradient(180deg, #fbfbfc, #f4f5f7);
}
.win-dots { display: inline-flex; gap: 6px; flex: none; }
.win-dots i, .wd-dots i { display: block; width: 8px; height: 8px; border-radius: var(--r-round); background: #d8dade; }
.win-url {
  flex: 1 1 auto; min-width: 0; font-style: normal;
  font-size: 13px; font-weight: 300; color: var(--tertiary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* JS gate — with JS off, or if the module never loads, there are no dead buttons */
/* Gate: the controls appear only once initWorkWindows() has finished wiring them
   (it adds .is-live on its last line). JS off, module missing, or a throw inside
   the initialiser all leave zero buttons — a dead button is worse than none. */
.win-ctl { display: none; flex: none; align-items: center; gap: 2px; }
.wins.is-live .win-ctl { display: inline-flex; }
.js-fallback .win-ctl  { display: none; }

/* minimise: one bar at the bottom edge. Collapsed (aria-expanded="false") a second
   bar appears at the top — the same "state drawn on the glyph" trick .mg-max uses. */
.mg-min { width: .40em; height: .34em; }
.mg-min::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: .02em;
  height: .075em; background: var(--on-metal);
}
[aria-expanded="false"] > .mg-min::after {
  content: ''; position: absolute; left: 0; right: 0; top: .02em;
  height: .075em; background: var(--on-metal);
}

/* ---------------------------------------------------------------------------
   THE FOLD — the compact state's only clip, and the whole SEO argument.
   NOT display:none, NOT visibility:hidden, NOT [hidden], NOT off-screen. The
   paragraphs keep a real layout box: measured 128px tall inside a 0px wrapper,
   computed display:block / visibility:visible / opacity:1, and #results.innerText
   still returns all 13 of them. Same mechanism this file already ships for .faq-a.
   Never transitioned — the state change is instant and the FLIP on the siblings
   carries the motion, so no height is ever animated.
   ------------------------------------------------------------------------ */
.win-fold { overflow: hidden; min-width: 0; }
.js-enabled .win-cell:not(.is-max) .win-fold { max-height: 0; }
/* !important is load-bearing here. In fallback BOTH .js-enabled and .js-fallback
   sit on <html>, and `.js-enabled .win-cell:not(.is-max) .win-fold` is (0,4,0)
   against this rule's (0,2,0) — so without it the prose stays clipped at 0 for a
   visitor whose script failed, with no controls to open it. Measured: folds
   0px before, full height after. */
.js-fallback .win-fold { max-height: none !important; }

/* MINIMISED — the body clips away and the title bar IS the window. */
.js-enabled .win-cell.is-min .win-body { max-height: 0; overflow: hidden; }
.win-cell.is-min .win     { border-radius: var(--r-round); }
.win-cell.is-min .win-bar { border-bottom-color: transparent; }

/* COMPACT CHROME — 297px is a real browser window, not a shrunk card. */
.win-cell:not(.is-max) .win-bar   { min-height: 54px; padding: 5px 6px 5px 16px; gap: 6px; }
.win-cell:not(.is-max) .win-dots  { display: none; }   /* three controls need the room */
.win-cell:not(.is-max) .mctl-ico  { width: 40px; height: 40px; font-size: 26px; }
.win-cell:not(.is-max) .mctl-ico::before,
.win-cell:not(.is-max) .mctl-ico::after { inset: 7px; }
.win-cell:not(.is-max) .win-copy   { padding: 20px 22px 24px; }
.win-cell:not(.is-max) .win-sector { font-size: 14px; margin-bottom: 4px; }
.win-cell:not(.is-max) .win-copy h3 { font-size: 21px; margin-bottom: 0; }
.win-cell:not(.is-max) .win-copy .case-link { padding-top: 18px; font-size: 15px; }

/* ---------------------------------------------------------------------------
   WINDOW BODY
   ------------------------------------------------------------------------ */
.win-body { flex: 1; display: flex; flex-direction: column; min-width: 0; }

/* the window IS the frame now — strip .shot's own frame but keep its machinery
   (.shot-img + the .is-in clip-path reveal that initShots() already drives) */
.win-shot { margin: 0; border: 0; border-radius: 0; background: none; }
.win-shot, .win-shot:hover { box-shadow: none; border-color: transparent; }

.win-copy { flex: 1; display: flex; flex-direction: column; padding: 30px 34px 34px; min-width: 0; }
.win-sector { font-size: 16px; font-weight: 300; color: var(--accent-mid); margin-bottom: 6px; transition: color .3s linear; }
.win:hover .win-sector { color: var(--accent-dk); }
.win-copy h3 { font-size: 26px; font-weight: 600; margin-bottom: 18px; transition: transform .4s var(--e-out); }
.win:hover .win-copy h3 { transform: translateX(6px); }
.win-lines { min-width: 0; }
.win-copy .case-link { margin-top: auto; padding-top: 22px; align-self: flex-start; }

/* ---------------------------------------------------------------------------
   MAXIMISED — a second layout, not a scale. Nothing is hidden; the three
   siblings drop to a 3-up row and keep every word.
   ------------------------------------------------------------------------ */
.win.is-max { border-color: #a8bdb4; box-shadow: 0 26px 64px -34px var(--sh); }
.win.is-max .win-body   { display: grid; grid-template-columns: 1.04fr .96fr; align-items: stretch; }
.win.is-max .win-shot .shot-img img { max-height: 520px; object-fit: cover; object-position: top center; }
.win.is-max .win-copy   { border-left: 1px solid var(--hairline); padding: 36px 42px 42px; }
.win.is-max .win-sector { font-size: 17px; }
.win.is-max .win-copy h3 { font-size: 34px; }
.win.is-max .win-lines  { display: grid; grid-template-columns: 1fr 1fr; gap: 0 32px; }

/* THE ZOOM. No fill mode: the last frame is scale(1), which is exactly the
   value .shot.is-in already holds, so hover transitions take over cleanly
   afterwards with no snap. */
@keyframes winZoom { from { transform: scale(1.075); } to { transform: none; } }
.js-enabled .win.is-max .shot.is-in .shot-img img { animation: winZoom .85s var(--e-out); }

/* ---------------------------------------------------------------------------
   DISINTEGRATE — layer 1, the eraser.
   300% x 300%, centred, gradient at 225deg so the opaque end is the TOP-RIGHT
   corner — erosion starts under the X and runs to the bottom-left.
   Gradient length L = 0.707(3W + 3H). Requiring the panel's top-right corner
   past the 62% stop at rest and its bottom-left corner inside the 40% stop at
   the end gives |tx - ty| >= 0.86(W+H); +-32% of a 300% box delivers
   0.96(W+H), ~12% headroom at each end, for any aspect ratio.
   ------------------------------------------------------------------------ */
.win-erase {
  position: absolute; left: -100%; top: -100%; width: 300%; height: 300%;
  z-index: 5; pointer-events: none;
  background-image: linear-gradient(225deg,
      var(--win-erase) 0 40%,
      var(--win-erase-soft) 50%,
      var(--win-erase-none) 62%);
  transform: translate3d(32%, -32%, 0);
  transition: transform .62s var(--e-out);
}
/* A transition, not an animation — so restore is the same declaration played
   backwards, for free, with no second keyframe and no fill-mode games. */
.win.is-going { pointer-events: none; border-color: transparent; box-shadow: none;
  transition: border-color .28s linear .34s, box-shadow .28s linear .34s; }
.win.is-going .win-erase { transform: translate3d(-32%, 32%, 0); will-change: transform; }

/* DISINTEGRATE — layer 2, the shards.
   overflow:hidden is load-bearing: a chip can never leave the window's
   footprint, so the disintegrate CANNOT produce horizontal overflow. Travel is
   short and opacity reaches 0 well before the boundary, so the clip is unseen. */
.win-dust {
  position: absolute; inset: 0; z-index: 6;
  overflow: hidden; border-radius: var(--r-card); pointer-events: none;
}
.win-dust i {
  position: absolute; display: block; border-radius: 1px;
  animation: winDust 640ms var(--e-out) var(--dd, 0ms) both;
  will-change: transform, opacity;
}
@keyframes winDust {
  0%   { opacity: 0;   transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  16%  { opacity: .92; }
  100% { opacity: 0;   transform: translate3d(var(--dx, 0px), var(--dy, 0px), 0)
                                  rotate(var(--dr, 0deg)) scale(var(--ds, .35)); }
}

/* ---------------------------------------------------------------------------
   THE DOCK — a dismissed window collapses to a bar in its own cell, and the
   cell sorts below the live ones. Dashed hairline borrows .case-cta's "this
   slot is empty" language.
   ------------------------------------------------------------------------ */
.win-dock {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 12px 12px 24px;
  border: 1px dashed var(--hairline); border-radius: var(--r-round);
  background: var(--card);
}
.win-dock[hidden] { display: none; }   /* a display rule beats [hidden]'s UA display:none */
.wd-dots { display: inline-flex; gap: 6px; flex: none; }
.wd-name {
  flex: 1 1 auto; min-width: 0;
  font-size: 17px; font-weight: 500; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.win-tools {
  display: flex; align-items: center; justify-content: flex-end; gap: 16px;
  margin-bottom: var(--grid-gap);
}
.win-tools[hidden] { display: none; }
.wt-label { font-size: 15px; font-weight: 300; color: var(--tertiary); }

/* ---------------------------------------------------------------------------
   RESPONSIVE
   ------------------------------------------------------------------------ */
@media (max-width: 1023px) {
  .win.is-max .win-body  { grid-template-columns: 1fr; }
  .win.is-max .win-copy  { border-left: 0; border-top: 1px solid var(--hairline); }
  .win.is-max .win-shot .shot-img img { max-height: 420px; }
}

@media (max-width: 1279px) {
  /* Below 1280 a 4-up window is under 250px and the title bar stops working
     (measured 253px at 1280, 229px at 1181). Two-up instead — 526px each. */
  .win-cell { flex: 1 1 calc(50% - var(--grid-gap) / 2); }
}

@media (max-width: 620px) {
  .win-cell, .win-cell.is-min { flex: 1 1 100%; }
}

@media (max-width: 900px) {
  .win.is-max .win-lines { grid-template-columns: 1fr; }
}

@media (max-width: 620px) {
  .win, .win.is-max, .win-dust { border-radius: 28px; }
  .win-bar { padding-left: 18px; min-height: 58px; }
  .win-dots { display: none; }              /* the URL needs the room more */
  .win-copy, .win.is-max .win-copy { padding: 24px 22px 26px; }
  .win-copy h3, .win.is-max .win-copy h3 { font-size: 23px; }
  .win.is-max .win-shot .shot-img img { max-height: 320px; }
  .win-dock { padding: 10px 10px 10px 18px; border-radius: 28px; }
  .win-tools { justify-content: space-between; }
  .mctl-ico { width: 42px; height: 42px; font-size: 26px; }
}

@media (hover: none) { .win::after { display: none; } }

/* ---------------------------------------------------------------------------
   REDUCED MOTION — every state still reachable, nothing animated.
   The file's own block cannot cover a component declared after it, so this is
   the component's own opt-out. JS never adds .is-going under reduced motion,
   so the eraser never leaves its rest position: dismissal is instantaneous.
   ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .win, .win:hover, .win.is-max { transform: none !important; }
  .win:hover { box-shadow: none !important; }
  .win:hover::after { opacity: 0 !important; }
  .win:hover .win-copy h3 { transform: none !important; }
  .mctl, .mctl:hover, .mctl:focus-visible, .mctl:active { transform: none !important; }
  .mctl:hover::after, .mctl:focus-visible::after { opacity: 1; }   /* static, visible, instant */
  .win-erase { transition: none !important; }
  .win-dust  { display: none !important; }
  .js-enabled .win.is-max .shot.is-in .shot-img img { animation: none !important; }
}

/* =============================================================================
   INTERIOR-PAGE COMPONENTS (v17e migration — services, locations, contact)

   Everything here exists because a live ranking page needs it and no existing
   v17e component covered it. Nothing here is used by the home page.

   Contrast measured on --bg #fbfbfc:
     --muted   #4e535b   7.50:1  text
     --edge    #848a94   3.36:1  boundary  (WCAG 1.4.11 minimum is 3:1)
     --edge-on #6d747e   4.56:1  boundary
     --accent  #3d444e   9.51:1  text
   No opacity below 1 on text. Transform + opacity only. Reduced motion at the
   bottom of the block collapses every hover transform.
   ========================================================================== */

/* -- Inline links inside long-form prose. The base rule is
      `a { color: inherit; text-decoration: none }`, so a prose link is
      completely invisible without this. The underline is permanent — never a
      hover-only tell. */
.prose a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: text-decoration-thickness .2s linear, color .2s linear;
}
.prose a:hover, .prose a:focus-visible { color: var(--accent-dk); text-decoration-thickness: 2px; }

/* -- h3 inside prose. Long-form pages need a third level; nothing on the home
      page did. Sentence case, unlike the global uppercase h2, so the hierarchy
      stays legible rather than shouting twice. */
.prose h3 {
  font-size: clamp(20px, 1.6vw, 23px); font-weight: 600; line-height: 1.25;
  text-transform: none; margin: 34px 0 12px;
}

/* -- Numbered variant of .prose-list. Replaces the tick glyph with a numeral. */
ol.prose-list { counter-reset: pl; }
ol.prose-list li::before {
  content: counter(pl); counter-increment: pl;
  border: 0; transform: none; width: auto; height: auto;
  left: 0; top: 0; font-size: 15px; font-weight: 600; color: var(--accent);
}

/* -- .cs-card carrying an h3 instead of an h2: service cards on a location page
      are subordinate to that page's section h2. Same sizing intent as
      `.cs-card h2`, one step down. */
.cs-card h3 {
  font-size: 24px; font-weight: 600; text-transform: none; line-height: 1.18;
  transition: transform .4s var(--e-out);
}
.cs-card:hover h3 { transform: translateX(5px); }

/* -- Chip links: adjacent suburbs, related services, cross-links. These are
      controls, so the boundary is --edge (3.36:1), not --hairline (1.37:1). */
.link-chips { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; }
.link-chips a {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 16px; font-weight: 300; color: var(--muted);
  border: 1px solid var(--edge); border-radius: var(--r-round);
  padding: 9px 18px;
  transition: color .25s linear, border-color .25s linear, transform .3s var(--e-out);
}
.link-chips a:hover, .link-chips a:focus-visible {
  color: var(--accent); border-color: var(--edge-on); transform: translateY(-2px);
}

/* -- A link inside a .fact value (contact details). Without this the base
      `a { color: inherit; text-decoration: none }` makes a mailto: or tel:
      indistinguishable from the text beside it. */
.fact-v a {
  color: var(--accent); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 3px;
  transition: text-decoration-thickness .2s linear, color .2s linear;
}
.fact-v a:hover, .fact-v a:focus-visible { color: var(--accent-dk); text-decoration-thickness: 2px; }

/* -- Meta line under a page-head: postcode / region / pricing. --tertiary is
      5.9:1, above the 4.5:1 floor; the <strong> lifts to --muted at 7.5:1. */
.head-meta { margin-top: 18px; font-size: 16px; font-weight: 300; color: var(--tertiary); }
.head-meta strong { color: var(--muted); font-weight: 400; }

/* -- Spacing for the second and later blocks inside one open .section, on
      pages that stack several headed groups in a single container. A class,
      not a repeated inline style, so the rhythm is defined once. */
.sec-head-next { margin-top: 64px; }

@media (max-width: 620px) {
  .link-chips a { font-size: 15px; padding: 8px 15px; }
  .sec-head-next { margin-top: 48px; }
}

@media (prefers-reduced-motion: reduce) {
  .link-chips a:hover, .link-chips a:focus-visible { transform: none !important; }
  .cs-card:hover h3 { transform: none !important; }
  .prose a { transition: none !important; }
}

/* ---------------------------------------------------------------------------
   SHADOW TEST — the inverted half. On the charcoal CTA band and the footer a
   black shadow is literally invisible, so every raised object there has been
   sitting flat. Same geometry, white light. Scoped to dark surfaces only, so
   nothing on the light page is touched.
   REVERT: delete this block.
   ------------------------------------------------------------------------ */
.cta-band .btn-primary,
.footer .btn-primary {
  box-shadow: 0 0.06em 0.10em -0.045em rgba(255, 255, 255, .34),
              0 0.40em 0.90em -0.55em  rgba(255, 255, 255, .40);
}
.cta-band .btn-primary:hover,
.cta-band .btn-primary:focus-visible,
.footer .btn-primary:hover,
.footer .btn-primary:focus-visible {
  box-shadow: 0 0.06em 0.10em -0.045em rgba(255, 255, 255, .40),
              0 0.72em 1.45em -0.60em  rgba(255, 255, 255, .52);
}
.cta-band .qform,
.cta-band .form-done {
  box-shadow: 0 0.40em 1.10em -0.50em rgba(255, 255, 255, .22),
              0 24px 60px -40px rgba(0, 0, 0, .55);
}
.footer .foot-social a:hover {
  box-shadow: 0 0.30em 0.70em -0.45em rgba(255, 255, 255, .38);
}
@media (prefers-reduced-motion: reduce) {
  .cta-band .btn-primary:hover, .footer .btn-primary:hover { box-shadow: none !important; }
}
