/* ───────────────────────────────────────────────────────────────
   Type — see DESIGN-DNA.md. Display / body / data are three
   distinct roles. Self-hosted latin subsets, no third-party call.
   ─────────────────────────────────────────────────────────────── */
@font-face{font-family:'Gabarito';font-style:normal;font-weight:400 900;font-display:swap;src:url(/assets/fonts/gabarito-400-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Jost';font-style:normal;font-weight:300;font-display:swap;src:url(/assets/fonts/jost-300.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Jost';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/jost-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Jost';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/jost-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Jost';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/jost-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'DM Mono';font-style:normal;font-weight:300;font-display:swap;src:url(/assets/fonts/dmmono-300.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'DM Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/dmmono-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'DM Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/dmmono-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

/* ───────────────────────────────────────────────────────────────
   Palette — relationships from "Before the Heat", not swatches.
   RULE: ember is EDGE LIGHT ONLY. No fills, no washes, no glows.
   The single exception is the primary CTA (the sun = a small fill).
   ─────────────────────────────────────────────────────────────── */
:root{
  --ground:#0E141A;         /* pre-dawn shadow — blue-slate, never black */
  --ground-2:#131B22;
  --ground-3:#1A242C;
  --sage:#8B9387;           /* the missing mid-tone: sawgrass body */
  --sage-dk:#59635A;
  --sage-lt:#B4BAAE;
  --haze:#EDE7DC;           /* warm horizon haze */
  --paper:#FAF8F4;          /* warm bone. #FFFFFF is banned */
  --paper-2:#F2EEE7;
  --ink:#151A1E;
  --ink-soft:#5A6068;
  --ember:#E4590F;          /* edge light — edges + the one CTA fill */
  --ember-lt:#FF7A33;       /* the lit tip — ember text on DARK grounds */
  --ember-ink:#A93C08;      /* ember text on LIGHT grounds. #E4590F is only
                               3.45:1 on bone paper — fails AA under 24px. */
  --line:#DED8CC;
  --line-field:#6F776C;     /* form control edges. --line is 1.23:1 on --paper-2,
                               which WCAG 1.4.11 fails at 3:1. This measures 4.0:1. */
  --line-dk:#25313A;
  --alert:#B3341C;          /* invalid field edge */
  --alert-ink:#8E2A16;      /* error text on light grounds -- 8:1 on bone paper */

  --display:'Gabarito','Segoe UI',system-ui,sans-serif;
  --body:'Jost',system-ui,sans-serif;
  --data:'DM Mono',ui-monospace,monospace;

  --maxw:1180px;
  --tick:repeating-linear-gradient(90deg,#000 0 7px,transparent 7px 10px);

  /* One source of truth for the fixed header's height. It is read by the nav drawer's
     top edge, by scroll-margin on every anchor target, and by the hero's top padding,
     so the phone header can shrink in ONE place without any of those three drifting
     out of sync — which is exactly how the old 96px drawer offset survived. */
  --hdr-h:96px;
  /* height reserved at the foot of the document for the sticky mobile CTA. Set to a
     real value only once the bar actually exists (html.mcta-ready), so a no-JS page
     never carries an empty band. */
  --mcta-h:0px;
}
*{box-sizing:border-box}
/* the scrollbar appearing once the page exceeds the viewport narrows it and shifts
   every centered container left — that horizontal jump was the whole CLS budget */
html{-webkit-text-size-adjust:100%;scrollbar-gutter:stable}
/* the background must not scroll behind an open drawer, or the reader loses the place
   in the document they were already reading. Set on <html> so it holds on iOS too. */
/* Locking the drawer's background.
   `overflow:hidden` on html and body alone does NOT hold: measured with a real wheel event
   and a real touch drag, the page still moved 559px and 568px. The reliable lock is to take
   the body out of flow entirely; JS pins `top` to the saved offset so the reader does not
   lose their place, and restores it on close. */
html.nav-open,html.nav-open body{overflow:hidden}
html.nav-open body{position:fixed;left:0;right:0;width:100%}
/* No global -webkit-font-smoothing:antialiased — on light grounds it THINS dark
   text on macOS, which is the same failure we're fixing here. Apply it only where
   it helps: light text on a dark ground. */
body{margin:0;font-family:var(--body);color:var(--ink);background:var(--paper);font-size:18px;line-height:1.6;
  padding-bottom:var(--mcta-h)}
.bg-dark,.hero,.trigger,.reading,#work,footer{-webkit-font-smoothing:antialiased}
h1,h2,h3,h4{font-family:var(--display);line-height:1.06;letter-spacing:-.021em;margin:0;font-weight:800}

/* ── Weight, not optical size ──────────────────────────────────────
   Gabarito carries one axis (wght 400–900) and even stroke weight, so plain
   font-weight is enough and there is no hairline to lose on a light ground.
   It replaced Bodoni Moda, which still measured 10.0% faint at 47px and 10.9% at
   20px on bone paper even with its optical-size axis driven all the way down;
   Gabarito at 800 measures 6.7% and 7.9% on the identical string. Dropping
   font-variation-settings also removes the trap where naming one axis silently
   reset font-weight to the font default. */
h3,h4,.mval b{font-weight:850}
.bg-dark h3,.bg-dark h4,.hero h3,.trigger h3,.reading h3,.bg-dark .mval b,#work h3,#work .mval b{font-weight:750}
/* <b> defaults to 700 and the Jost stack ships 400 and 600 only, so every bold run in
   prose was being synthesized by the browser, producing a smeared weight rather than a drawn one.
   Specificity keeps the heading and .mval rules above untouched. */
b,strong{font-weight:600}
p{margin:0}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 26px}
section{padding:112px 0;scroll-margin-top:calc(var(--hdr-h) + 10px)}
[id]{scroll-margin-top:calc(var(--hdr-h) + 10px)}

/* ── skip link ──────────────────────────────────────────────────────
   Kept at its full size and moved OFF THE TOP EDGE rather than clipped to 1px:
   a 1x1 clipped link is a real focus target that measures under the WCAG 2.2
   minimum, and it only ever moves vertically so it can never widen the document. */
/* visually hidden but announced — used to give a control a name that CONTAINS its
   visible text, which is what WCAG 2.5.3 Label in Name actually requires. */
.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}
.skip{position:fixed;left:16px;top:-72px;z-index:90;
  display:inline-flex;align-items:center;min-height:44px;padding:11px 18px;
  font-family:var(--data);font-size:.86rem;letter-spacing:.04em;
  background:var(--ember);color:var(--ground);border:1px solid var(--ember);
  transition:top .2s cubic-bezier(.2,.8,.2,1)}
/* Baseline focus ring. Only header controls had an authored one, so the indicator
   changed appearance mid-tab-sequence as you moved down the page. The specific rules
   below still win where they exist. */
:focus-visible{outline:2px solid var(--ember-ink);outline-offset:3px}
.hero :focus-visible,.bg-dark :focus-visible,#work :focus-visible,.trigger :focus-visible,
.disclose :focus-visible,.phero :focus-visible,footer :focus-visible,.form-notice:focus-visible{outline-color:var(--ember-lt)}
.skip:focus{top:12px;outline:2px solid var(--haze);outline-offset:2px}
/* the skip link parks focus on <main> so the next Tab starts inside the content. A
   ring around the whole document would be noise — the target is not a control. */
main[tabindex="-1"]:focus{outline:none}
@media(prefers-reduced-motion:reduce){.skip{transition:none}}

/* the mono label is the brand's voice for anything measured */
.eyebrow{font-family:var(--data);font-weight:500;font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;color:var(--ember-ink);display:inline-flex;align-items:center;gap:11px}
.eyebrow::before{content:"";width:26px;height:9px;background:var(--ember);-webkit-mask:var(--tick);mask:var(--tick);flex:0 0 auto}
/* .hero and .trigger are dark grounds but don't carry .bg-dark — they need the
   light-on-dark ember too, or the eyebrow drops to 2.94:1. */
.bg-dark .eyebrow,.hero .eyebrow,.trigger .eyebrow,#work .eyebrow{color:var(--ember-lt)}
.bg-dark .eyebrow::before,.hero .eyebrow::before,.trigger .eyebrow::before,#work .eyebrow::before{background:var(--ember-lt)}

/* ── buttons: ember is an EDGE, except the one primary fill ── */
.btn{display:inline-flex;align-items:center;gap:.55em;font-family:var(--data);font-weight:500;font-size:.86rem;letter-spacing:.04em;padding:15px 26px;border:1px solid transparent;cursor:pointer;transition:transform .35s cubic-bezier(.2,.8,.2,1),background .25s,border-color .25s,color .25s}
/* ink on ember, not paper on ember: paper-on-ember is 3.45:1 and fails AA.
   Dark-on-warm also reads more industrial, which suits the anchor. */
.btn-primary{background:var(--ember);color:var(--ground);border-color:var(--ember)}
.btn-primary:hover{background:var(--ember-lt);border-color:var(--ember-lt);transform:translateY(-2px)}
.btn-edge{background:var(--ground);color:var(--ember-lt);border-color:rgba(228,89,15,.55)}
.btn-edge:hover{border-color:var(--ember-lt);background:rgba(228,89,15,.07)}
.btn-ink{background:transparent;color:var(--ink);border-color:var(--line)}
.btn-ink:hover{border-color:var(--ink);transform:translateY(-2px)}
.arrow{transition:transform .3s cubic-bezier(.2,.8,.2,1)}
.btn:hover .arrow{transform:translateX(4px)}

/* ── THE CONTINUOUS SURFACE ──────────────────────────────────────
   By-Kin's lesson: a site should read as one surface, not a stack of sections.
   Our signature tick meter becomes the through-line — a rail down the left edge
   that fills as you move through the page, so the whole document is one reading
   on one instrument. Scroll-driven, zero JS, sits behind everything. */
.rail{position:fixed;left:14px;top:50%;transform:translateY(-50%);height:min(46vh,380px);width:9px;z-index:40;pointer-events:none;
  -webkit-mask:repeating-linear-gradient(180deg,#000 0 7px,transparent 7px 10px);
          mask:repeating-linear-gradient(180deg,#000 0 7px,transparent 7px 10px)}
.rail-track{position:absolute;inset:0;background:currentColor;opacity:.16}
.rail-fill{position:absolute;left:0;right:0;top:0;height:0;background:linear-gradient(180deg,var(--ember),var(--ember-lt));
  animation:railGrow linear both;animation-timeline:scroll(root);animation-range:0% 100%}
@keyframes railGrow{from{height:0}to{height:100%}}
/* the rail reads against whatever ground it is currently over */
.rail{color:var(--sage)}
@supports not (animation-timeline: scroll(root)){ .rail{display:none} }
/* the through-line rail is a desktop device: at 1024px it sits 3px off the edge of the
   text column and reads as clutter beside the copy rather than as an instrument */
@media(max-width:1024px){ .rail{display:none} }

/* ── nav ── */
header{position:fixed;inset:0 0 auto;z-index:60;transition:background .4s,box-shadow .4s,backdrop-filter .4s}
header.solid{background:rgba(14,20,26,.9);backdrop-filter:blur(16px) saturate(140%);box-shadow:0 1px 0 var(--line-dk)}
.nav{display:flex;align-items:center;justify-content:space-between;height:var(--hdr-h);gap:24px}
.nav .logo{height:38px}
/* the wordmark is the "home" target — it has to clear 44px on its own, not inherit
   the 38px height of the image inside it */
.nav>a{display:inline-flex;align-items:center;min-height:44px}
.nav>a:focus-visible{outline:2px solid var(--ember-lt);outline-offset:3px}
.nav-links{display:flex;align-items:center;gap:30px}
.nav-links a{font-family:var(--data);font-size:.79rem;letter-spacing:.05em;color:var(--sage-lt);position:relative;padding:6px 0;transition:color .25s}
.nav-links a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--ember-lt);transform:scaleX(0);transform-origin:left;transition:transform .35s cubic-bezier(.2,.8,.2,1)}
/* :not() keeps the drawer CTA out of this rule. At (0,2,1) it outranked
   `.nav-links .nav-drawer-cta` (0,2,0) and repainted the button's label paper on
   ember-lt on hover -- 2.4:1, the exact pair rejected at .btn-primary above. */
.nav-links a:not(.nav-drawer-cta):hover{color:var(--paper)}
.nav-links a:hover::after{transform:scaleX(1)}
/* The drawer had a :focus ring scoped inside max-width:1024px and the desktop bar had
   nothing, so six links fell back to the UA ring while .nav>a, .burger, .acc-btn and
   a.shotlink all used ember -- the indicator changed appearance mid-tab-sequence inside
   one header. */
.nav-links a:focus-visible{outline:2px solid var(--ember-lt);outline-offset:3px}
/* Current page. The underline already exists as the hover affordance, so the active
   state is that same mark held open rather than a new device. In the drawer ::after is
   display:none, so the colour change carries it there. */
.nav-links a[aria-current]{color:var(--paper)}
.nav-links a[aria-current]::after{transform:scaleX(1)}
/* Desktop only. Without this the flex items shrink and the LABELS wrap internally —
   "Websites & / search" over two lines — which is what pushed the bar to 52px the
   moment a seventh item (Pricing) was added. Scoped to the horizontal state; below
   1025px each link is a full-width drawer row where wrapping is correct. */
@media(min-width:1025px){ .nav-links a{white-space:nowrap} }
.nav-cta{display:flex;align-items:center;gap:12px}
.burger{display:none;background:none;border:1px solid var(--line-dk);width:46px;height:46px;cursor:pointer;
  align-items:center;justify-content:center;flex-direction:column;gap:5px;padding:0}
.burger span{display:block;height:1px;width:20px;margin:0;background:var(--paper);transition:transform .35s,opacity .25s}
.burger:focus-visible{outline:2px solid var(--ember-lt);outline-offset:2px}
/* the icon states the state: three rules become a cross while the menu is open, so the
   control is not relying on the drawer alone to say what it will do next */
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(6px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}
@media(prefers-reduced-motion:reduce){.burger span{transition:none}}

/* the CTA that lives INSIDE the drawer. It is the same destination as the header
   button, which is hidden below 980px — without this the one action the whole site
   drives toward is unreachable on a phone. */
.nav-drawer-cta{display:none}

/* ── the sticky mobile CTA ──────────────────────────────────────────
   Restraint rules: one action, no dismiss-nag, no second line of marketing, and it
   only exists after the hero has left the screen — before that the hero's own button
   is the CTA and a second one would just cover the copy. */
.mcta{position:fixed;left:0;right:0;bottom:0;z-index:55;display:none;
  padding:10px 16px calc(10px + env(safe-area-inset-bottom,0px));
  background:rgba(14,20,26,.95);border-top:1px solid var(--line-dk);
  -webkit-backdrop-filter:blur(14px) saturate(140%);backdrop-filter:blur(14px) saturate(140%);
  transform:translateY(115%);transition:transform .38s cubic-bezier(.2,.8,.2,1)}
.mcta.show{transform:none}
/* centered and capped rather than edge-to-edge: a 768px-wide orange bar is a banner,
   which is the opposite of restrained. On a phone the cap never binds. */
.mcta{justify-content:center}
.mcta .btn{width:100%;max-width:420px;justify-content:center;min-height:48px}
@media(prefers-reduced-motion:reduce){.mcta{transition:none}}

/* ── hero ── */
.hero{position:relative;background:var(--ground);color:var(--haze);overflow:hidden;padding:170px 0 190px}
/* horizon wash — cool ground, warm haze low. no radial "glow". */
.hero::before{content:"";position:absolute;inset:auto 0 0;height:62%;background:linear-gradient(180deg,transparent,rgba(139,147,135,.10) 60%,rgba(237,231,220,.07));pointer-events:none}
/* The sawgrass field, in CSS. 64 blades, ascending left to right like the logo,
   warm only at the tip (relationship #1), each swaying on its own clock so the
   field never pulses in lockstep. Previously three.js — same picture, 230 KB less. */
.field{position:absolute;inset:auto 0 0;height:170px;z-index:1;pointer-events:none;
  display:flex;align-items:flex-end;gap:3px;padding:0 2px;opacity:.5;
  -webkit-mask:linear-gradient(180deg,transparent,#000 70%);mask:linear-gradient(180deg,transparent,#000 70%)}
.field i{flex:1 1 0;min-width:0;height:var(--h);transform-origin:bottom;border-radius:1px 1px 0 0;
  background:linear-gradient(180deg,var(--ember-lt) 0,rgba(255,122,51,0) 5%,var(--sage) 26%,var(--line-dk) 100%);
  /* TWO motions, composited — not one motion made bigger.
     `sway` is the idle field: wind, each blade on its own clock, unchanged.
     `gain` is amplification: a single crest that travels left to right, once per pass,
     lifting each blade as it arrives. animation-composition:add MULTIPLIES the two scaleY
     values instead of letting the later animation replace the earlier one, so the wave
     rides ON the wind rather than flattening it. Without `add`, the last animation in the
     list simply wins and the per-blade sway is lost.
     Both animations touch transform ONLY. Nothing here animates opacity, height or any
     layout property: this field sits in the hero, above the fold, next to the LCP element,
     and an opacity animation here would make the same mistake that cost 2.8-4.1s of mobile
     LCP on the hero paragraph. */
  animation:sway var(--d) ease-in-out var(--o) infinite alternate,
            gain 3.6s cubic-bezier(.3,0,.25,1) calc(var(--g) + 2.9s) infinite;
  animation-composition:replace,add}
@keyframes sway{from{transform:scaleY(var(--lo))}to{transform:scaleY(1.04)}}
/* The crest occupies the first ~40% of the cycle and rests flat for the remainder, so the
   field is a signal that gets amplified in passes — not a permanently agitated equalizer.
   1.34 is deliberately generous: the field's own mask fades its top 30%, so a blade driven
   past the box is absorbed rather than colliding with the copy above it. The mask is acting
   as the limiter, which is what a real amplifier has — gain without clipping into the text.
   The 2.9s delay in the rule above keeps the first crest out of the load window entirely:
   mobile LCP lands near 2.4s on a 4x-throttled phone, and at 1.4s the crest was starting
   INSIDE that window. The field is already alive on sway before then, so nothing is lost. */
@keyframes gain{
  0%   {transform:scaleY(1)}
  18%  {transform:scaleY(1.34)}
  40%  {transform:scaleY(1)}
  100% {transform:scaleY(1)}
}
@media(prefers-reduced-motion:reduce){.field i{animation:none}}
.hero .wrap{position:relative;z-index:3;display:grid;grid-template-columns:1.12fr .88fr;gap:56px;align-items:start}
/* ── THE ONE KINETIC MOMENT ──────────────────────────────────────
   The headline gains weight as you scroll — Gabarito travels wght 420 → 900:
   opsz 96 -> 8, wght 500 -> 800. Scroll-driven, zero JS, and it demonstrates the
   optical-size axis that fixed this font's legibility problem in the first place.

   Kinetic type normally wrecks Core Web Vitals because reflowing weight rewraps
   the lines and changes the block's height. Two guards make that impossible here:
   the line breaks are AUTHORED (<br>, not computed), and the h1 is locked to an
   explicit min-height. Line count cannot change, so height cannot change.
   Measured CLS after this shipped: see the note in DESIGN-DNA.md. */
.hero h1{--hf:clamp(2.7rem,5.7vw,4.5rem);--hlh:1.04;font-size:var(--hf);line-height:var(--hlh);margin-top:24px;
  min-height:calc(4 * var(--hlh) * var(--hf));padding-bottom:.04em;
  font-weight:420;letter-spacing:.004em;
  animation:heroCut linear both;animation-timeline:scroll(root);animation-range:0% 16%}
.hero h1 .ln{display:block;white-space:nowrap}
@keyframes heroCut{
  from{font-weight:420;letter-spacing:.004em}
  to  {font-weight:900;letter-spacing:-.028em}
}
/* no scroll-timeline support, or motion turned down: hold the sturdy cut */
@supports not (animation-timeline: scroll(root)){
  .hero h1{font-weight:900;letter-spacing:-.024em;animation:none}
}
/* ember as an edge on the type, not a fill */
.hero h1 .hl{position:relative;display:inline-block}
/* "Amplify" amplifies. Two things happen, both tied to scroll and both borrowing
   the site's own vocabulary rather than a generic flourish:
     1. a LEVEL METER fills underneath it — the same tick mask used for every score
        on the site, so the word is literally gaining level.
     2. the word's own weight axis travels harder than the rest of the headline
        (wght 380 -> 800, opsz 96 -> 6), so it swells while the line around it
        only firms up. Glyph weight changes inside a nowrap block, so nothing can
        rewrap — the CLS guard still holds. */
.hero h1 .hl{font-weight:400;
  animation:amplifyWord linear both;animation-timeline:scroll(root);animation-range:0% 18%}
@keyframes amplifyWord{
  from{font-weight:400}
  to  {font-weight:900}
}
.hero h1 .hl::after{content:"";position:absolute;left:0;bottom:-.07em;height:.075em;width:0;
  background:linear-gradient(90deg,var(--ember),var(--ember-lt));
  -webkit-mask:repeating-linear-gradient(90deg,#000 0 7px,transparent 7px 10px);
          mask:repeating-linear-gradient(90deg,#000 0 7px,transparent 7px 10px);
  animation:amplifyLevel linear both;animation-timeline:scroll(root);animation-range:1% 20%}
@keyframes amplifyLevel{from{width:0}to{width:100%}}
@supports not (animation-timeline: scroll(root)){
  .hero h1 .hl{font-weight:900;animation:none}
  .hero h1 .hl::after{width:100%;animation:none}
}
.hero p.sub{margin:26px 0 34px;font-size:1.1rem;color:var(--sage-lt);max-width:38ch}
.hero .cta{display:flex;gap:12px;flex-wrap:wrap}

/* ── the reading card: a real measurement, dated. replaces the old
      invented "+12 hrs" dashboard, which contradicted the Work section. ── */
.reading{position:relative;background:var(--ground-2);border:1px solid var(--line-dk);padding:28px;margin-top:22px}
.reading::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--ember)}
.rd-top{display:flex;justify-content:space-between;align-items:baseline;gap:14px}
.rd-src{font-family:var(--data);font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;color:var(--sage)}
.rd-date{font-family:var(--data);font-size:.7rem;color:var(--sage)}
.reading h3{font-size:1.16rem;color:var(--haze);margin:22px 0 10px}
.rd-what{font-family:var(--data);font-size:.72rem;color:var(--sage);letter-spacing:.03em}
.rd-rows{margin-top:22px;display:grid;gap:17px}
.reading .lvl{background-color:#25313A}
.rd-note{margin-top:20px;padding-top:15px;border-top:1px solid var(--line-dk);font-size:.82rem;color:var(--sage)}
.rd-note a{color:var(--ember-lt);border-bottom:1px solid rgba(228,89,15,.4)}

/* ── THE SIGNATURE DEVICE: the tick meter ──
   Uniform ticks, varied fill. Warm only where lit. Used in the hero,
   the FORGE stages, and every work card. See DESIGN-DNA.md §5. */
.mrow .mhead{display:flex;justify-content:space-between;align-items:baseline;gap:12px;margin-bottom:9px}
.mlabel{font-family:var(--data);font-size:.7rem;font-weight:500;letter-spacing:.13em;text-transform:uppercase;color:var(--sage-dk)}
.bg-dark .mlabel,.reading .mlabel,#work .mlabel{color:var(--sage)}
/* Base is the LIGHT-ground value. --sage is 2.99:1 on bone paper; the dark grounds
   override below, same direction as .eyebrow and .mlabel. */
.mval{font-family:var(--data);font-weight:500;font-size:.9rem;color:var(--ink-soft);white-space:nowrap}
.reading .mval,.bg-dark .mval,#work .mval{color:var(--sage)}
/* was opacity:.5 -> 2.38:1. This glyph carries the from-to sense of the figure it
   sits inside; it is not decoration and it gets a real colour. */
.mval i{font-style:normal;margin:0 5px;color:var(--sage-dk)}
.reading .mval i,.bg-dark .mval i,#work .mval i{color:var(--sage)}
/* --ember-ink, not --ember: the token exists precisely because #E4590F measures
   3.45:1 on bone paper. Dark grounds take --ember-lt on the next line. */
.mval b{font-family:var(--display);font-weight:700;font-size:1.42rem;color:var(--ember-ink);letter-spacing:-.005em}
.reading .mval b,.bg-dark .mval b,#work .mval b{color:var(--ember-lt)}
.bar{position:relative}
.lvl{height:14px;background-color:var(--line);
  background-image:linear-gradient(90deg,var(--ember),var(--ember-lt));
  background-repeat:no-repeat;background-size:0 100%;
  -webkit-mask:var(--tick);mask:var(--tick);
  transition:background-size 1.5s cubic-bezier(.16,.84,.24,1) var(--md,.1s)}
.in .lvl{background-size:var(--now) 100%}
.tick{position:absolute;top:-5px;bottom:-5px;width:1px;background:var(--ink);opacity:.45}
.reading .tick,.bg-dark .tick,#work .tick{background:var(--haze);opacity:.5}
.legend{margin-top:13px;font-family:var(--data);font-size:.7rem;color:var(--sage-dk);display:flex;align-items:center;gap:7px}
.legend i{display:inline-block;width:1px;height:11px;background:currentColor;opacity:.7}

/* ── section heads ── */
.sec-head{max-width:700px;margin-bottom:58px}
/* balance at every width, not only on phones: the mono figure is wider than the
   Gabarito it sits beside, which orphaned the last word on desktop. */
.sec-head h2{font-size:clamp(2.05rem,3.7vw,2.95rem);margin:24px 0 0;text-wrap:balance}
/* the measurement itself, in the face this site reserves for measurement */
.sec-head h2 .fig{font-family:var(--data);font-weight:500;font-size:.92em;letter-spacing:-.02em}
.sec-head h2 .fig i{font-style:normal;margin:0 .12em;color:var(--sage)}
.sec-head p{margin-top:22px;color:var(--ink-soft);font-size:1.08rem}
.bg-soft{background:var(--paper-2)}
.bg-dark{background:var(--ground);color:var(--haze)}
.bg-dark .sec-head h2{color:var(--haze)}
.bg-dark .sec-head p{color:var(--sage)}

/* ── problem: a numbered ledger, not a card grid ── */
.ledger{border-top:1px solid var(--line)}
.lrow{display:grid;grid-template-columns:auto 1fr 1fr;gap:28px;align-items:baseline;padding:26px 0;border-bottom:1px solid var(--line);transition:background .3s}
.lrow:hover{background:var(--paper-2)}
.lnum{font-family:var(--data);font-size:.72rem;color:var(--ember-ink);letter-spacing:.1em}
.lrow h3{font-size:1.22rem}
.lrow p{color:var(--ink-soft);font-size:.97rem}

/* ── the problem trigger: Recognition → Consequence → Cost → Relief ── */
.trigger{margin-top:64px;background:var(--ground);color:var(--haze);padding:52px 48px;position:relative;overflow:hidden}
.trigger::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--ember)}
.trigger h3{font-size:clamp(1.5rem,2.9vw,2.15rem);max-width:30ch;color:var(--haze);margin-top:20px}
.tsteps{margin-top:36px;display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line-dk)}
.tstep{background:var(--ground-2);padding:24px 22px}
.tstep .tk{font-family:var(--data);font-size:.7rem;letter-spacing:.17em;text-transform:uppercase;color:var(--ember-lt)}
.tstep p{margin-top:11px;font-size:.93rem;color:var(--sage-lt)}
.tstep:last-child p{color:var(--haze)}
.tsrc{margin-top:26px;font-family:var(--body);font-size:.95rem;color:var(--sage-lt);max-width:62ch;line-height:1.65}

/* ── THE FORGE ACCORDION ───────────────────────────────────────────
   FORGE used to be a five-column hover rail. It replaced itself: the copy now lives on
   its own page, and on a phone a five-column rail is a five-row stack of clipped
   summaries that only opens on hover — which no phone has. So the five stages are a
   plain disclosure list instead.

   Three rules make it trustworthy rather than decorative:
     1. each stage's one-line summary is ALWAYS visible. Only the detail collapses, so
        the reader can see all five steps and their shape without opening anything.
     2. the panel is toggled with the [hidden] attribute, not clipped with max-height.
        A clipped panel is the single most common accordion bug — expanded content that
        is cut off at an arbitrary cap — and it cannot happen if there is no cap.
     3. on a wide screen every panel is open. The accordion is a small-screen
        affordance, not a way to hide the argument from anybody who has the room. */
/* Every selector below is qualified with .acc on purpose: the accordion sits inside
   .prose, whose `.prose h2` and `.prose p` rules are one specificity step above a bare
   class and would otherwise win. */
.acc{margin-top:34px;border-top:1px solid var(--line)}
.acc-item{border-bottom:1px solid var(--line)}
/* the heading IS the button — no nested interactive elements, and the h2 keeps the
   document outline intact for search engines and screen-reader heading navigation */
.acc .acc-h{margin:0;font-size:inherit;letter-spacing:normal;line-height:inherit;font-weight:inherit}
.acc .acc-btn{appearance:none;-webkit-appearance:none;background:none;border:0;color:inherit;font:inherit;
  width:100%;display:flex;align-items:center;gap:16px;text-align:left;cursor:pointer;
  padding:20px 0;min-height:56px;transition:color .25s}
.acc .acc-btn:focus-visible{outline:2px solid var(--ember-ink);outline-offset:2px}
.acc .acc-k{font-family:var(--data);font-weight:500;font-size:.8rem;letter-spacing:.14em;
  color:var(--ember-ink);flex:0 0 auto;width:2.7em}
.acc .acc-name{font-family:var(--display);font-weight:850;font-size:1.34rem;letter-spacing:-.015em;
  color:var(--ink);flex:1 1 auto;line-height:1.15}
/* the sign is decorative: the state is carried by aria-expanded, and the panel's own
   presence is what a sighted reader is actually reading */
.acc .acc-sign{flex:0 0 auto;position:relative;width:18px;height:18px}
.acc .acc-sign::before,.acc .acc-sign::after{content:"";position:absolute;background:var(--ember-ink);
  transition:transform .3s cubic-bezier(.2,.8,.2,1),opacity .3s}
.acc .acc-sign::before{left:0;right:0;top:8px;height:2px}
.acc .acc-sign::after{top:0;bottom:0;left:8px;width:2px}
.acc .acc-btn[aria-expanded="true"] .acc-sign::after{transform:scaleY(0);opacity:0}
.acc .acc-btn:hover .acc-name{color:var(--ember-ink)}
/* the summary NEVER collapses — the five steps and their shape are readable without
   opening anything, so the accordion hides detail rather than the argument */
.acc .acc-sum{margin:0 0 20px;padding-left:calc(2.7em + 16px);color:var(--ink-soft);
  font-size:1.02rem;max-width:60ch}
.acc .acc-panel{padding:0 0 26px;margin-left:calc(2.7em + 16px);max-width:62ch}
.acc .acc-panel[hidden]{display:none}
.acc .acc-panel p{margin-top:14px;color:var(--ink-soft);font-size:1.02rem}
.acc .acc-panel p:first-child{margin-top:0}
.acc .acc-panel p b{color:var(--ink)}
.acc .acc-panel .acc-get{margin-top:18px;padding-top:15px;border-top:1px solid var(--line);
  font-family:var(--data);font-size:.84rem;line-height:1.7;color:var(--ink-soft)}
.acc .acc-panel .acc-get b{color:var(--ember-ink);font-weight:500}
@media(prefers-reduced-motion:reduce){
  .acc .acc-btn,.acc .acc-sign::before,.acc .acc-sign::after{transition:none}
}

/* ── services ── */
.svc-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);border:1px solid var(--line)}
.svc-grid.svc-2{grid-template-columns:repeat(2,1fr)}
.svc .visit{margin-top:18px;display:inline-block}
@media(max-width:900px){.svc-grid.svc-2{grid-template-columns:1fr}}
.svc{background:var(--paper);padding:34px 30px;transition:background .35s}
.svc:hover{background:var(--paper-2)}
.svc .sn{font-family:var(--data);font-size:.7rem;letter-spacing:.16em;color:var(--ember-ink)}
.svc h2,.svc h3{font-size:1.3rem;margin-top:22px;margin-bottom:0}
.svc p{margin-top:16px;color:var(--ink-soft);font-size:.96rem}

/* ── OUTCOMES ─────────────────────────────────────────────────────
   The bridge from "here's what we build" to "here's what it did". Every claim in
   here is either a dated measurement published further down the page, or a plain
   description of the work with no result claimed. Nothing is projected, and nothing on
   the page advertises something we do not have. */
#outcomes{background:var(--paper)}
.oc-foot{margin-top:26px;font-family:var(--body);font-size:.94rem;color:var(--ink-soft);display:flex;align-items:center;gap:11px;max-width:64ch}
.oc-foot::before{content:"";width:20px;height:8px;background:var(--ember);-webkit-mask:var(--tick);mask:var(--tick);flex:0 0 auto}
@media(max-width:980px){ }

/* ── THE SPOTLIGHT WALK-THROUGH ───────────────────────────────────
   Iventions' lesson: a guided walk-through beats a grid. Each property is an
   illuminated installation that lights as you reach it and dims as you leave —
   so the section is walked, not scanned. The light is an ember wash on a dark
   ground (the only place ember is allowed area, because it is literally light),
   and every meter fills on its own scroll timeline instead of a JS observer. */
#work{background:var(--ground);color:var(--haze)}
#work .sec-head h2{color:var(--haze)}
#work .sec-head p{color:var(--sage)}
.walk{border-top:1px solid var(--line-dk)}
.inst{--gut:26px;position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) minmax(0,1.05fr);gap:44px;
  align-items:start;padding:52px 0 54px 30px;border-bottom:1px solid var(--line-dk);
  --lit:0;transition:none}
/* the spotlight: --lit runs 0 -> 1 -> 0 as the row crosses the viewport */
.inst{animation:litPass linear both;animation-timeline:view();animation-range:entry 15% exit 85%}
@keyframes litPass{0%{--lit:0}38%{--lit:1}70%{--lit:1}100%{--lit:0}}
@property --lit{syntax:'<number>';inherits:true;initial-value:0}
.inst::before{content:"";position:absolute;inset:-1px 0;pointer-events:none;
  background:radial-gradient(120% 150% at 18% 50%,rgba(228,89,15,calc(.10 * var(--lit))),transparent 62%);
  opacity:calc(.35 + .65 * var(--lit))}
.inst::after{content:"";position:absolute;left:calc(-1 * var(--gut));top:0;bottom:0;width:2px;background:var(--ember);
  transform:scaleY(var(--lit));transform-origin:top}
.inst>*{position:relative;z-index:1}
.inst-n{font-family:var(--data);font-size:.72rem;letter-spacing:.18em;color:var(--sage);padding-top:10px;white-space:nowrap}
.inst-n b{display:block;font-family:var(--display);font-weight:700;font-size:2.2rem;letter-spacing:-.01em;
  color:var(--sage);line-height:1;margin-bottom:12px;transition:color .5s}
.inst h3{font-size:clamp(1.5rem,2.4vw,2.05rem);color:var(--haze);margin-bottom:0}
.inst .what{margin-top:16px;color:var(--sage);font-size:.96rem;max-width:34ch}
.inst .visit{margin-top:18px;display:inline-block;color:var(--sage-lt);border-bottom-color:var(--line-dk)}
.inst .visit:hover{color:var(--ember-lt);border-bottom-color:var(--ember)}
.inst .deltas{margin-top:4px}
.inst .method{border-top-color:var(--line-dk);color:var(--sage)}
.inst .method b{color:var(--haze)}
.inst .legend{color:var(--sage)}
.inst .stage-tag{color:var(--sage)}
/* meters fill on their own scroll timeline — no observer, no JS */
.inst .lvl{background-color:#25313A;animation:fillLvl linear both;animation-timeline:view();animation-range:entry 40% cover 44%}
@keyframes fillLvl{from{background-size:0 100%}to{background-size:var(--now) 100%}}
@supports not (animation-timeline: view()){
  .inst{--lit:1}
  .inst .lvl{background-size:var(--now) 100%;animation:none}
}
@media(max-width:980px){
  .inst{grid-template-columns:auto minmax(0,1fr);gap:22px;padding:38px 0 40px 20px}
  .inst .col3{grid-column:1 / -1}
  .inst::before{inset:-1px 0}
}
@media(max-width:600px){
  .inst{grid-template-columns:1fr;gap:16px;padding-left:18px}
  .inst-n{padding-top:0;display:flex;align-items:baseline;gap:12px}
  .inst-n b{margin-bottom:0;font-size:1.7rem}
}
.proof{background:var(--paper);padding:34px}
.proof h3{font-size:1.42rem}
.what{color:var(--ink-soft);font-size:.94rem;margin-top:14px}
/* padding, not font-size: these were 22px tall — 2px under the WCAG 2.2 minimum
   tap target of 24x24. */
.visit{font-family:var(--data);font-size:.72rem;color:var(--ink-soft);white-space:nowrap;display:inline-block;border-bottom:1px solid var(--line);padding:5px 0 6px;transition:color .25s,border-color .25s}
.visit:hover{color:var(--ember-ink);border-color:var(--ember)}
.deltas{margin-top:28px;display:grid;gap:19px}
.method{margin-top:24px;padding-top:17px;border-top:1px solid var(--line);font-size:.85rem;color:var(--ink-soft)}
.method b{font-family:var(--data);font-weight:500;font-size:.82rem;color:var(--ink)}
.stage-tag{font-family:var(--data);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft)}
.disclose{margin-top:44px;border:1px solid var(--line-dk);border-left:3px solid var(--ember);padding:28px 32px;background:var(--ground-2)}
/* h2 carries a real section heading ("Also built -- a system, not a website") and is
   set as one. The mono micro-label treatment stays on h4, where the content is a
   label rather than a sentence. */
.disclose h2{font-family:var(--display);font-weight:750;font-size:1.18rem;letter-spacing:-.015em;color:var(--haze);margin:0 0 14px}
.disclose h4{font-family:var(--data);font-weight:500;font-size:.7rem;letter-spacing:.17em;text-transform:uppercase;color:var(--ember-lt);margin:0 0 13px}
/* max-width is load-bearing, not cosmetic. .disclose sits outside .prose, so it inherited
   no measure cap and ran the full container: measured 142 characters per line on /work/ at
   1280px and above, against a ~75ch readable optimum. Same defect the .claims block had —
   any text block placed outside .prose needs its own cap. */
.disclose p{color:var(--sage-lt);font-size:.95rem;max-width:74ch}
.disclose p+p{margin-top:13px}
.disclose b{color:var(--haze);font-weight:500}

/* ── why: an asymmetric argument, not a symmetric stat quad ── */
.why{display:grid;grid-template-columns:1.05fr .95fr;gap:64px;align-items:start}
.claims{border-top:1px solid var(--line)}
/* fixed first column, not auto: each .claim is its own grid container, so `auto`
   sizes per row and the headings never line up down the list. */
.claim{display:grid;grid-template-columns:5.6rem 1fr;gap:18px;padding:22px 0;border-bottom:1px solid var(--line);align-items:baseline}
.claim .cn{font-family:var(--data);font-size:1.02rem;font-weight:500;color:var(--ember-ink);white-space:nowrap}
.claim .ct{font-size:.94rem;color:var(--ink-soft)}
.claim .ct b{display:block;font-family:var(--display);font-weight:700;font-size:1.1rem;color:var(--ink);margin-bottom:3px;letter-spacing:-.004em}

/* ── start ── */
/* Ember edges are rationed: the argument gets one (.answer, .disclose), a state
   report gets sage (.sent, .nope), and a plain container gets a hairline. A mark
   worn by six unrelated components had stopped distinguishing any of them. */
.band{border:1px solid var(--line);padding:60px 54px;background:var(--paper-2)}
.band h2{font-size:clamp(2rem,3.6vw,2.9rem);max-width:26ch;margin-top:20px}
.band p{margin:20px 0 30px;max-width:56ch;color:var(--ink-soft);font-size:1.06rem}
.band .row{display:flex;gap:12px;flex-wrap:wrap}
.note{margin-top:22px;font-family:var(--body);font-size:.92rem;color:var(--ink-soft);max-width:66ch}
/* .band p is (0,1,1) and was beating .note, so the privacy line rendered at the band's
   lead-paragraph size. That was previously masked by an inline font-size on the markup;
   stated as a rule instead. */
.band .note{font-size:.92rem}

/* ── the intake form: the actual conversion path ──────────────────
   Replaces a mailto: link, which does nothing at all on a desktop browser with no
   mail client registered — silently, which is the worst possible failure for the
   one button the whole page drives toward. */
.intake{margin-top:34px;display:grid;grid-template-columns:1fr 1fr;gap:18px 20px;max-width:760px}
.fld{display:flex;flex-direction:column;gap:7px}
.fld.wide{grid-column:1 / -1}
/* Not uppercase. Most labels here are two words, but two are full questions --
   "What eats the most time every week?" and a 58-character website prompt -- and
   caps strip the word shapes that make a sentence readable. Size up and tracking
   down to suit lowercase mono. */
.fld label{font-family:var(--data);font-size:.74rem;font-weight:500;letter-spacing:.03em;color:var(--ink-soft)}
.fld label .req{color:var(--ember-ink)}
/* Native select in the intake form. Added 2026-09-19 with the "What would you like
   help with?" field - without these rules an unstyled OS control sits mid-form and
   breaks the field rhythm. Focus uses the inset ember bar, matching input/textarea:
   ember is edge light, never a fill. */
.fld select{font-family:var(--body);font-size:1rem;color:var(--ink);background:var(--paper);
  border:1px solid var(--line-field);border-radius:0;padding:13px 14px;width:100%;
  min-height:50px;appearance:none;-webkit-appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23566' stroke-width='1.6' stroke-linecap='round'/></svg>");
  background-repeat:no-repeat;background-position:right 14px center;background-size:12px 8px;
  transition:border-color .25s,box-shadow .25s}
.fld select:focus,.fld select:focus-visible{outline:none;border-color:var(--ember);box-shadow:inset 2px 0 0 var(--ember)}
@media(max-width:767px){.fld select{font-size:1.02rem}}

.fld input,.fld textarea{font-family:var(--body);font-size:1rem;color:var(--ink);background:var(--paper);
  border:1px solid var(--line-field);border-radius:0;padding:13px 14px;width:100%;
  transition:border-color .25s,box-shadow .25s}
.fld textarea{min-height:104px;resize:vertical}
/* :focus-visible as well, so a pointer click does not paint the ring, but the
   keyboard path always does. */
/* Chrome's default placeholder is #757575, which measures 4.34:1 on bone paper and
   fails AA by 0.16. --ink-soft is 5.99:1 and still reads lighter than an entered value. */
.fld input::placeholder,.fld textarea::placeholder{color:var(--ink-soft);opacity:1}
.fld input:focus,.fld textarea:focus,.fld input:focus-visible,.fld textarea:focus-visible{outline:none;border-color:var(--ember);box-shadow:inset 2px 0 0 var(--ember)}
.fld input[aria-invalid="true"],.fld textarea[aria-invalid="true"]{border-color:var(--alert)}
.fld .err{font-family:var(--data);font-size:.7rem;color:var(--alert-ink);min-height:1em}
.intake-foot{grid-column:1 / -1;display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-top:4px}
.intake-status{font-family:var(--data);font-size:.78rem;color:var(--ink-soft)}
.intake-status.bad{color:var(--alert-ink)}
.intake-status.good{color:var(--ember-ink)}

/* The unavailability notice used to be a grey line BELOW the submit, so six fields got
   filled before the visitor learned nothing would send. It is now a banner above the
   form, carrying the ember edge the rest of the site uses for a statement that matters. */
.form-notice{margin-top:26px;background:var(--ground-2);color:var(--haze);
  border:1px solid var(--line-dk);border-left:3px solid var(--ember);padding:18px 22px;
  font-size:.97rem;max-width:760px}
.form-notice:focus-visible{outline:2px solid var(--ember-lt);outline-offset:3px}
.form-notice b{color:var(--haze);font-weight:500}

/* Unavailable is not "faded primary". opacity:.45 on ember produced a washed peach that
   appears nowhere in the palette and read as already-pressed. This reads as a control
   that is not currently available: neutral fill, full-strength ink, 12.4:1. */
.btn-primary[aria-disabled="true"]{background:var(--line);color:var(--ink);
  border-color:var(--sage);opacity:1;transform:none}
.btn-primary[aria-disabled="true"]:hover{background:var(--line);border-color:var(--sage);transform:none}
.sent{grid-column:1 / -1;border:1px solid var(--line);border-left:3px solid var(--sage-dk);background:var(--paper);padding:24px 26px}
.sent h3{font-size:1.35rem}
.sent p{margin-top:10px;color:var(--ink-soft);font-size:.96rem}
@media(max-width:767px){ .intake{grid-template-columns:1fr} }

/* ── footer ── */
footer{background:var(--ground);color:var(--sage);padding:64px 0 32px;border-top:1px solid var(--line-dk)}
.foot-top{display:flex;justify-content:space-between;gap:38px;flex-wrap:wrap;align-items:flex-start}
.foot-top .logo{height:34px;margin-bottom:16px}
footer .tag{font-family:var(--display);font-weight:700;color:var(--haze);font-size:1.05rem}
.foot-links{display:flex;gap:52px;flex-wrap:wrap}
.foot-links .foot-h,.foot-links h4{font-family:var(--data);font-weight:500;color:var(--haze);font-size:.7rem;letter-spacing:.15em;text-transform:uppercase;margin:0 0 14px}
.foot-links a{display:block;font-size:.92rem;padding:5px 0;color:var(--sage);transition:color .25s}
.foot-links a:hover{color:var(--ember-lt)}
.foot-bot{margin-top:46px;padding-top:24px;border-top:1px solid var(--line-dk);display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;font-family:var(--data);font-size:.72rem;color:var(--sage)}

/* ── pronunciation guide (kept — it is genuinely ours) ── */
.pronounce{display:inline-flex;align-items:center;gap:13px;flex-wrap:wrap;margin:0;padding:9px 15px;
  background:transparent;border:1px solid var(--line-dk);cursor:pointer;font-family:var(--data);
  transition:border-color .3s,background .3s}
.pronounce:hover{border-color:rgba(228,89,15,.6);background:rgba(228,89,15,.05)}
.pronounce .say{font-family:var(--display);font-weight:700;font-size:1.08rem;color:var(--haze);display:inline-flex;align-items:baseline;letter-spacing:-.01em}
.pronounce .syl{display:inline-block;transition:color .2s,transform .2s;transform-origin:bottom}
.pronounce .sep{color:var(--sage);margin:0 3px}
.pronounce .syl.lit{color:var(--ember-lt)}
.pronounce .syl.stress.lit{transform:translateY(-4px) scale(1.14)}
.pronounce .phon{font-size:.72rem;color:var(--sage);white-space:nowrap}
.pronounce .phon b{color:var(--haze);font-weight:500}
.pronounce .spk{display:grid;place-items:center;width:23px;height:23px;border:1px solid var(--ember);flex:0 0 auto}
.pronounce .spk svg{width:12px;height:12px;fill:var(--ember-lt)}
.pronounce.playing .spk{border-color:var(--ember-lt)}
/* ══ THE PROOF WALL ═══════════════════════════════════════════════
   The hero's answer to "why should I believe you". Real builds, drifting slowly,
   feathered top and bottom so shots arrive and leave instead of snapping.
   Transform-only: the headline must never wait on this. Images are 380px wide,
   ~7KB each, 28KB for the set. Rest state is a legible stack, so reduced motion
   and a paused frame both show real work rather than a blank column. */
.proofwall{margin-top:34px;max-width:380px}
.pw-frame{position:relative;height:372px;overflow:hidden;scroll-snap-type:y proximity;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 11%,#000 84%,transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0,#000 11%,#000 84%,transparent 100%)}
.pw-track{display:flex;flex-direction:column;gap:16px;will-change:transform}
.pw-track figure{margin:0;flex:none}
.pw-track img{display:block;width:100%;height:auto;aspect-ratio:380/238;border:1px solid var(--line-dk);
  border-radius:2px;background:var(--ground-2)}
/* .72rem is the label scale: right for an uppercase tracked mark, wrong for a full
   sentence. This one carries the portfolio count and its live/pre-launch split, so it
   has to survive being read rather than glanced at. */
.pw-note{margin-top:15px;font-family:var(--data);font-size:.8rem;letter-spacing:.02em;
  color:var(--sage)}
.pw-note b{color:var(--haze);font-weight:500}
.pw-note a{color:var(--ember-lt);border-bottom:1px solid rgba(228,89,15,.4);
  display:inline-block;padding:3px 0}
.pw-note a:hover{border-bottom-color:var(--ember-lt)}

@media(prefers-reduced-motion:no-preference){
  /* -50% lands exactly on the duplicate set, so the loop has no seam */
  .pw-track{animation:pwDrift 46s linear infinite}
  /* :focus-within as well as :hover. A hover-only pause is not a WCAG 2.2.2 mechanism:
     it is unreachable by keyboard. The frame is focusable (tabindex=0 in markup) because
     a scrollable region must be, so focusing it now also stops the drift. */
  .proofwall:hover .pw-track,
  .proofwall:focus-within .pw-track{animation-play-state:paused}
}
@keyframes pwDrift{from{transform:translateY(0)} to{transform:translateY(-50%)}}
.pw-frame:focus-visible{outline:2px solid var(--ember-lt);outline-offset:3px}
.pw-track figure{scroll-snap-align:start}

/* Touch: no pointer means no hover, so the drift had no stop at all. Rather than bolt a
   pause button onto the hero, touch gets no motion and a real swipeable strip. Nothing
   moves, so WCAG 2.2.2 does not apply, and all four proofs are reachable by thumb. The
   four aria-hidden figures exist only to make the loop seamless; with no loop they are
   duplicate content and are removed. */
@media(hover:none){
  .pw-frame{overflow-y:auto;-webkit-overflow-scrolling:touch}
  .pw-track{animation:none!important}
  .pw-track figure[aria-hidden="true"]{display:none}
}

/* Reduced motion: the drift was the ONLY thing that revealed figures 2-8, so switching it
   off left a 372px window showing two thumbnails with no way to reach the rest. All four
   proofs, no window, no scroll. The hero's proof block must not withhold its evidence
   from the people who asked for less movement. */
@media(prefers-reduced-motion:reduce){
  .pw-frame{height:auto;overflow:visible}
  .pw-track{display:grid;grid-template-columns:1fr 1fr;gap:10px}
  .pw-track figure[aria-hidden="true"]{display:none}
}

@media(max-width:1024px){
  /* hero stacks here — keep it, but shorter, and never above the CTA */
  .proofwall{margin-top:28px;max-width:340px}
  .pw-frame{height:250px}
}

.pron-quip{margin-top:11px;font-family:var(--data);font-size:.74rem;color:var(--sage);min-height:1.3em;transition:opacity .3s}

/* ── motion: directional, staggered, per-element. not 29 uniform fades. ── */
[data-r]{opacity:0;transition:opacity .8s cubic-bezier(.16,.84,.24,1),transform .9s cubic-bezier(.16,.84,.24,1);transition-delay:var(--d,0ms)}
[data-r="up"]{transform:translateY(30px)}
[data-r="left"]{transform:translateX(-26px)}
[data-r="rise"]{transform:translateY(46px) scale(.985)}
[data-r="wipe"]{clip-path:inset(0 100% 0 0);opacity:1;transform:none;transition:clip-path 1.1s cubic-bezier(.16,.84,.24,1) var(--d,0ms)}
.in[data-r]{opacity:1;transform:none}
.in[data-r="wipe"]{clip-path:inset(0 0 0 0)}

/* ⚠️ ABOVE-THE-FOLD CONTENT MUST NEVER WAIT FOR JAVASCRIPT TO BECOME VISIBLE.
   An element at opacity:0 is not eligible to be the Largest Contentful Paint. The hero's
   sub-paragraph IS the LCP element on the homepage, and it carried data-r="up", so it sat
   at opacity:0 until the IntersectionObserver booted. Measured on the live host at 390px
   with 4x CPU throttling and Slow 4G: LCP 2776-4132ms across runs, while its font arrived
   at 1039ms and TTFB was 30ms. Nothing was slow to load — the text simply was not allowed
   to paint yet.
   The hero is on screen at t=0 by definition, so it never needed an observer. It now
   animates from CSS on load, and the entrance is TRANSFORM-ONLY: animating opacity would
   reintroduce the same problem, because a transform does not affect LCP eligibility and
   opacity does. Everything below the fold keeps the observer, where the delay is free. */
.hero [data-r]{opacity:1;animation:heroRise .8s cubic-bezier(.16,.84,.24,1) both;
  animation-delay:var(--d,0ms)}
.hero [data-r="up"],.hero [data-r="rise"],.hero [data-r="left"]{transform:none}
@keyframes heroRise{from{transform:translateY(20px)}to{transform:none}}
@media(prefers-reduced-motion:reduce){.hero [data-r]{animation:none}}
@media(prefers-reduced-motion:reduce){
  [data-r]{opacity:1!important;transform:none!important;clip-path:none!important;transition:none!important}
  .lvl{transition:none}
  /* the accordion still opens — it just stops animating. It never animated its height
     in the first place (the panel is toggled with [hidden]), so there is nothing here
     that can leave content half-revealed. */
  .acc .acc-btn,.acc .acc-sign::before,.acc .acc-sign::after{transition:none!important}
  .mcta,.nav-links,.nav-links.open,.skip{transition:none!important}
  /* kinetic headline holds the legible text cut instead of traveling */
  .hero h1{animation:none!important;font-weight:900!important;letter-spacing:-.024em!important}
  .hero h1 .hl{animation:none!important;font-weight:900!important}
  .hero h1 .hl::after{animation:none!important;width:100%!important}
  .field i{animation:none!important}
  /* Was height:100%!important -- a progress rail permanently reporting "end of
     document" from the first pixel. This site refuses to draw a bar that encodes
     nothing (see the #work row), so the instrument is withdrawn rather than shown
     giving a false reading. It is already display:none below 1024px. */
  .rail{display:none}
}

/* ── document pages (service, case study, legal) ─────────────────────
   Same tokens, same rhythm as the homepage. Nothing new invented. */
.phero{background:var(--ground);color:var(--haze);padding:150px 0 76px}
.phero h1{font-size:clamp(2.2rem,4.6vw,3.5rem);max-width:24ch;margin-top:20px}
.crumb{font-family:var(--data);font-size:.7rem;letter-spacing:.1em;color:var(--sage)}
.crumb a{color:var(--sage);border-bottom:1px solid var(--line-dk)}
.crumb a:hover{color:var(--ember-lt)}
/* was opacity:.5 -> 1.95:1. The links carry an underline, so the separator does not
   need to be dimmed to stay distinct from them. */
.crumb span{margin:0 7px}

/* the plain answer, immediately under the H1 */
.answer{margin-top:26px;max-width:62ch;border-left:3px solid var(--ember);padding:2px 0 2px 22px}
.answer p{font-size:1.16rem;color:var(--haze);line-height:1.6}
/* p{margin:0} is the global reset, so two paragraphs in the same .answer ran together
   at body leading and read as one block. The break is the argument's second beat. */
.answer p + p{margin-top:1.1em}
.phero .meta{margin-top:30px;padding-top:18px;border-top:1px solid var(--line-dk);
  font-family:var(--data);font-size:.72rem;color:var(--sage);display:flex;gap:22px;flex-wrap:wrap}
/* the mono gloss under an .answer — a definition, not a footnote, so it keeps a
   readable size on a phone instead of the .78rem it used to be set inline */
/* ── the subpage hero's right column ──────────────────────────────────
   Nine pages opened on the same slab: type in the left 55%, nothing in the right 45%,
   and the meta row wrapped underneath as a line of mono. Those facts are the one thing
   in the hero that differs page to page -- alldayfence.com / Live 2026-06-27, or
   Tested 2026-07-18 / Re-tested 2026-07-19 -- so they move into the empty column as a
   stacked panel headed by the tick mark. Pages with no meta keep the single column. */
@media(min-width:1025px){
  .phero .wrap:has(> .meta){display:grid;grid-template-columns:minmax(0,1fr) 264px;gap:0 60px;align-items:start}
  .phero .wrap:has(> .meta) > .meta{margin-top:6px;padding:0 0 0 22px;border-top:0;
    border-left:1px solid var(--line-dk);flex-direction:column;align-items:flex-start;gap:0}
  .phero .wrap:has(> .meta) > .meta::before{content:"";display:block;width:26px;height:9px;
    background:var(--ember);-webkit-mask:var(--tick);mask:var(--tick);margin-bottom:15px}
  .phero .wrap:has(> .meta) > .meta span{display:block;width:100%;padding:11px 0;
    border-bottom:1px solid var(--line-dk);line-height:1.5}
  .phero .wrap:has(> .meta) > .meta span:last-child{border-bottom:0}
}

.answer .gloss{margin-top:14px;font-family:var(--data);font-size:.82rem;line-height:1.7;color:var(--sage)}
@media(max-width:1024px){ .answer .gloss{font-size:.9rem;line-height:1.65} }
.answer .gloss b{color:var(--haze);font-weight:500}
.answer .gloss a{color:var(--ember-lt);border-bottom:1px solid rgba(228,89,15,.4)}

/* 68ch resolved against .prose's own font-size while the paragraphs render a step
   larger, so the real measure came out at 107 characters worst / 79 median against
   a 65-75 target. Narrowing the column keeps the hairline rows spanning it. */
.prose{max-width:56ch;min-width:0}
/* margin-top:0 on every h2 jammed each heading against the preceding list border.
   Only the first one should hug the top of the column. */
.prose h2{font-size:clamp(1.6rem,2.8vw,2.2rem);margin-top:52px}
/* the accordion is the first thing in the column on /forge-system/ */
.prose > .acc:first-child{margin-top:0}
.prose > h2:first-child{margin-top:0}
.prose h3{font-size:1.22rem;margin-top:36px}
.prose p{margin-top:16px;color:var(--ink-soft);font-size:1.02rem}
.prose p b,.prose li b{color:var(--ink)}
.prose ul{margin:16px 0 0;padding:0;list-style:none}
.prose li{padding:11px 0;border-bottom:1px solid var(--line);color:var(--ink-soft);font-size:1rem}
.prose li:first-child{border-top:1px solid var(--line)}
.prose .lede{font-size:1.12rem;color:var(--ink)}
/* :not(.btn) is load-bearing. `.prose a` is specificity (0,1,1) and `.btn-primary` is only
   (0,1,0), so an unscoped rule here repaints the CTA inside every prose column — ember text
   on an ember fill, measured at 1.72:1 on ten of fourteen pages. Buttons are not prose links
   and must keep their own color. */
.prose a:not(.btn){color:var(--ember-ink);border-bottom:1px solid var(--line)}
.prose a:not(.btn):hover{border-color:var(--ember)}

/* two-column document layout: prose left, proof rail right */
.dcols{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:56px;align-items:start}
/* These documents run 4,000-9,000px while the rail holds three short boxes, so the whole
   right column was empty from the first screen down. align-items:start above is what makes
   sticky work in the grid; --hdr-h keeps it clear of the fixed header. */
.dcols > aside{position:sticky;top:calc(var(--hdr-h) + 22px)}

/* Comparison table. /what-it-all-means/ is a reference document whose entire job is
   telling four acronyms apart, and it was doing that in ten identical hairline rows like
   every other page. A table is the form that does that job. Same hairlines, same mono
   labels -- a different structure, not a different language. */
.ctable-wrap{overflow-x:auto;margin-top:24px;min-width:0;-webkit-overflow-scrolling:touch}
.ctable{width:100%;border-collapse:collapse;min-width:520px}
.ctable th{font-family:var(--data);font-weight:500;font-size:.7rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ember-ink);text-align:left;padding:0 20px 12px 0;
  border-bottom:1px solid var(--line);white-space:nowrap}
.ctable td{padding:15px 20px 15px 0;border-bottom:1px solid var(--line);color:var(--ink-soft);
  vertical-align:top;font-size:.97rem}
.ctable tr:last-child td{border-bottom:0}
.ctable td:first-child{font-family:var(--data);font-weight:500;color:var(--ink);white-space:nowrap}
/* 2.5.8: these were 28x21 standalone jump links. */
.ctable td:first-child a{display:inline-flex;align-items:center;min-height:24px}
.ctable td:last-child,.ctable th:last-child{padding-right:0}

/* The cost band. On the two service pages "What it costs to leave alone" was an h3 and a
   paragraph, indistinguishable from the thirty hairline rows around it, at the exact point
   the argument turns. It is the same Consequence content .trigger exists for, so it takes
   .trigger -- the copy is untouched and differs on each page. */
.prose > .trigger{margin-top:52px}
/* .prose h3 and .trigger h3 have equal specificity, so which one won was decided by
   source order. Stated explicitly instead: one step above the document h3, not the
   trigger's display size -- the dark ground is the break, the heading stays in the
   page's hierarchy rather than shouting over it. */
.prose > .trigger > h3:first-child{margin-top:0;font-size:1.34rem}
.prose > .trigger p{color:var(--sage-lt);font-size:1.02rem;margin-top:18px;max-width:62ch}
.rail-box{background:var(--paper);border:1px solid var(--line);padding:24px 26px}
.rail-box + .rail-box{margin-top:20px}
.rail-box h3,.rail-box h4{font-family:var(--data);font-weight:500;font-size:.7rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ember-ink);margin:0 0 12px}
.rail-box p{color:var(--ink-soft);font-size:.94rem;margin-top:9px}
.rail-box .psrc{font-family:var(--data);font-size:.7rem;line-height:1.6;color:var(--ink-soft);margin-top:10px}
@media(max-width:1024px){.dcols{grid-template-columns:1fr;gap:34px}}

/* what we do not promise */
.nope{margin-top:30px;border:1px solid var(--line);border-left:3px solid var(--sage-dk);padding:24px 26px;background:var(--paper-2)}
.nope h3{margin:0 0 18px;font-size:1.1rem}
.nope ul{margin:0;padding:0;list-style:none}
.nope li{padding:9px 0;border-bottom:1px solid var(--line);color:var(--ink-soft);font-size:.96rem}
.nope li:last-child{border-bottom:0}

/* FAQ — visible copy, matching the schema exactly */
.faq{margin-top:34px;border-top:1px solid var(--line)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{cursor:pointer;padding:18px 0;font-family:var(--display);font-weight:800;
  font-size:1.06rem;list-style:none;display:flex;justify-content:space-between;gap:16px;align-items:baseline}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";font-family:var(--data);color:var(--ember-ink);font-size:1.1rem}
.faq details[open] summary::after{content:"–"}
.faq details p{padding:0 0 20px;color:var(--ink-soft);font-size:1rem;max-width:62ch}

/* .claim's first column is a fixed 5.6rem, tuned for short figures like "$3,950".
   A "from $15,000" label is nowrap and ran straight into the heading beside it, so
   the wide variant is opt-in rather than retuning every existing .claims block. */
.claims-wide .claim{grid-template-columns:8.6rem 1fr}
@media(max-width:700px){ .claims-wide .claim{grid-template-columns:1fr} }

/* ── PRICING LADDER ───────────────────────────────────────────────
   Deliberately not a three-card pricing grid — that layout is the single most
   copied component on the web and it would fail the swap test on sight. The
   argument this page makes is that each price explains the next one, so the
   plans are rungs on ONE measure and the site's own tick meter fills as you
   climb. Ember stays edge light: the meter's lit tip is the only ember in the
   component, never the fill. */
.plans{border-top:1px solid var(--line)}
.plan{display:grid;grid-template-columns:auto minmax(0,1fr) minmax(0,1.15fr);gap:44px;
  padding:36px 0 32px;border-bottom:1px solid var(--line);align-items:start;transition:background .3s}
.plan:hover{background:var(--paper-2)}
.pcode{font-family:var(--data);font-size:.72rem;letter-spacing:.18em;color:var(--ember-ink);
  padding-top:7px;white-space:nowrap}
.plan h3{font-size:1.28rem;margin:0}
.pprice{font-family:var(--display);font-weight:800;font-size:clamp(1.75rem,3vw,2.2rem);
  letter-spacing:-.02em;line-height:1;margin-top:15px;color:var(--ink)}
/* mono for the unit, because on this site the data face is what carries every
   number — the price reads as a measurement, not a banner */
.pper{font-family:var(--data);font-weight:400;font-size:.74rem;letter-spacing:.05em;
  color:var(--ink-soft);margin-left:9px;vertical-align:.32em}
.pwhat{margin-top:16px;color:var(--ink-soft);font-size:.96rem;max-width:36ch}
.pspec{margin:0;padding:0;list-style:none;border-top:1px solid var(--line)}
.pspec li{display:grid;grid-template-columns:5.4rem minmax(0,1fr);gap:16px;padding:9px 0;
  border-bottom:1px solid var(--line);align-items:baseline}
.pspec b{font-family:var(--data);font-weight:500;font-size:.92rem;color:var(--ink);
  font-variant-numeric:tabular-nums;letter-spacing:.01em}
.pspec span{font-size:.92rem;color:var(--ink-soft);line-height:1.55}
.pnot{margin-top:15px;font-family:var(--data);font-size:.74rem;line-height:1.75;color:var(--ink-soft)}

/* the meter. Two masked layers over one tick pattern: the full track at rest
   opacity, the lit run clipped to --fill, and a single ember rule at the head of
   the run. --fill is set per rung inline because it IS the content. */
.pmeter{position:relative;margin-top:17px;height:14px;width:100%;max-width:196px}
.pmeter::before,.pmeter::after{content:"";position:absolute;top:0;bottom:0;left:0;
  -webkit-mask-image:var(--tick);mask-image:var(--tick);
  -webkit-mask-repeat:repeat;mask-repeat:repeat}
.pmeter::before{right:0;background:var(--ink);opacity:.16}
.pmeter::after{width:calc(var(--fill,0) * 100%);background:var(--ink);opacity:.62}
/* 3px and over-tall, not 2px flush: at the high fills the tip sat among a dozen
   ink ticks and stopped reading as the head of the run. It still has to stay an
   edge — widening it further would make it a fill, which the DNA forbids. */
.pmeter i{position:absolute;top:-3px;bottom:-3px;width:3px;background:var(--ember);
  left:calc(var(--fill,0) * 100%);margin-left:-3px}

@media(max-width:900px){
  .plan{grid-template-columns:1fr;gap:22px;padding:32px 0 28px}
  .pcode{padding-top:0}
  .pwhat{max-width:none}
  .pmeter{max-width:160px}
  .pspec li{grid-template-columns:4.6rem minmax(0,1fr);gap:13px}
}

/* legal pages */
.legal .prose h2{margin-top:38px;font-size:1.32rem}
.legal .prose h2:first-child{margin-top:0}
.updated{font-family:var(--data);font-size:.72rem;color:var(--ink-soft);margin-bottom:26px}

/* 404 */
.nf{min-height:64vh;display:grid;place-items:center;text-align:center;background:var(--ground);color:var(--haze)}
.nf h1{font-size:clamp(3rem,9vw,6rem)}
.nf p{margin-top:18px;color:var(--sage)}
.nf .row{margin-top:30px;display:flex;gap:12px;justify-content:center;flex-wrap:wrap}

/* ── founder / accountability ─────────────────────────────────────
   NOTE: this block was silently lost once already — the insert anchor did not match
   and nothing verified it. If you edit the stylesheet with a script, assert that a
   selector from the new block is actually present afterwards. */
.founder{display:grid;grid-template-columns:380px minmax(0,1fr);gap:52px;align-items:start}
/* The portrait bleeds instead of being framed. The border and the ember left edge drew a hard
   rectangle around it; a feathered mask dissolves the photograph into the page instead, so the
   shape comes from the subject rather than from a line.
   The feather is deliberately asymmetric: light at the top so his head is not cut into, wider
   at the bottom where the desk and laptop are the natural place for the image to give out.
   Two gradients intersected mask all four edges at once — one gradient can only do one axis. */
.fportrait{margin:0;position:relative;aspect-ratio:3/5;display:grid;place-items:center;max-width:380px}
.fportrait img{width:100%;height:100%;object-fit:cover;display:block;
  -webkit-mask-image:linear-gradient(to right,transparent 0,#000 10%,#000 90%,transparent 100%),
    linear-gradient(to bottom,transparent 0,#000 4%,#000 92%,transparent 100%);
  -webkit-mask-composite:source-in;
  mask-image:linear-gradient(to right,transparent 0,#000 10%,#000 90%,transparent 100%),
    linear-gradient(to bottom,transparent 0,#000 4%,#000 92%,transparent 100%);
  mask-composite:intersect}
.fbody h2,.fbody h3{font-size:clamp(1.6rem,2.6vw,2.15rem);margin-bottom:0}
/* Not uppercase: this line carries a person's name and title, up to 51 characters
   on /about/. Caps is for the two-word labels, not for a sentence. */
.frole{margin-top:14px;font-family:var(--data);font-size:.78rem;letter-spacing:.02em;color:var(--ember-ink)}
.flead{margin-top:22px;font-size:1.12rem;color:var(--ink)}
.ffacts{margin:26px 0 0;padding:0;list-style:none;border-top:1px solid var(--line)}
.ffacts li{padding:13px 0;border-bottom:1px solid var(--line);color:var(--ink-soft);font-size:.98rem}
.ffacts b{color:var(--ink)}
.fwhy{margin-top:22px;color:var(--ink-soft);font-size:.98rem}
/* Credentials as ONE quiet line, not a bordered panel. The story above is what earns
   trust; these are the checkable facts standing behind it, so they sit last and small.
   Mono because on this site the data face is what carries every verifiable figure. */
.fcreds{margin-top:26px;padding-top:18px;border-top:1px solid var(--line);
  font-family:var(--data);font-size:.78rem;line-height:1.85;color:var(--ink-soft)}
/* the CTA sat flush against the closing paragraph — it needs real separation */
.fcontact{margin-top:38px;padding-top:28px;border-top:1px solid var(--line);
  display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.fcontact-note{font-family:var(--data);font-size:.76rem;color:var(--ink-soft)}
@media(max-width:767px){
  .founder{grid-template-columns:1fr;gap:26px}
  .fportrait{width:min(82vw,320px);max-width:none}
  .fcontact{margin-top:30px;padding-top:22px}
}

/* ── generated atmosphere ──────────────────────────────────────────
   Deterministic SVG from assets/art/ — seeded, so it is reproducible and cannot be
   matched to a stock library. Purely decorative: aria-hidden, never carries meaning,
   and sits behind the content on its own layer so text contrast is unaffected. */
.band-art{position:relative;isolation:isolate;overflow:hidden}
/* Anchored to the bottom edge at a capped height — it is a horizon, not a wallpaper.
   Stretching the 300px source over a 2700px section turns the reed field into streaks. */
.band-art > .art{position:absolute;left:0;right:0;bottom:0;width:100%;
  height:clamp(180px,26vh,300px);object-fit:cover;object-position:bottom;
  pointer-events:none;user-select:none;z-index:0;opacity:.9}
/* the contour set is a field rather than a horizon, so it fills its band */
.band-art > .art-full{top:0;height:100%;object-fit:fill;opacity:.85}
.band-art > .wrap{position:relative;z-index:1}
/* The one lit contour in each hero field was crossing the H1 and every line of the
   lead, a strikethrough at 390px, on the pages that sell legibility. Copy fills
   17–91% of the band's height, so there is no free zone to move the stroke into.
   A scrim sized off the text column carries the field under the type instead: the
   stroke keeps its full reach and comes to strength where the measure ends. */
.phero.band-art::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(90deg,rgba(14,20,26,.90) 0%,rgba(14,20,26,.88) 44%,
    rgba(14,20,26,.34) 66%,rgba(14,20,26,.34) 71%,rgba(14,20,26,.56) 77%,rgba(14,20,26,.5) 100%)}
/* Below the two-column hero the measure runs edge to edge, so a horizontal scrim has
   nothing to clear into. The field drops to a texture and reads behind the type. */
@media(max-width:1024px){
  .phero.band-art > .art-full{opacity:.64}
  .phero.band-art::after{background:linear-gradient(180deg,rgba(14,20,26,.24) 0%,
    rgba(14,20,26,.5) 16%,rgba(14,20,26,.5) 90%,rgba(14,20,26,.16) 100%)}
}

/* ── real captures, used as evidence rather than decoration ────────
   Desktop beside mobile, with a dated caption. Never presented as a before/after. */
.shotfig{margin:32px 0 8px;display:grid;grid-template-columns:minmax(0,1fr) 178px;
  gap:18px;align-items:end}
.shotfig img{display:block;width:100%;height:auto;border:1px solid var(--line);
  border-radius:3px;background:var(--paper-2)}
.shotfig figcaption{grid-column:1/-1;font-family:var(--data);font-size:.76rem;
  line-height:1.55;color:var(--ink-soft);margin-top:10px}
@media(max-width:700px){
  .shotfig{grid-template-columns:1fr;gap:14px}
  .shotfig img:last-of-type{max-width:200px}
}

/* ── property screenshots in the work rows ─────────────────────────
   Real captures of the live properties. The portfolio previously had no pictures of
   the work at all, which is the one image a portfolio actually owes the reader. */
/* The schematic is 1600 units wide; in the prose column it rendered at 732px — a 0.46
   scale that put its type on screen at under 6px. It now sits in its own band and is
   centred on the page. .wrap is centred, so 50% here IS the page centre; doing this from
   inside .prose resolved against a grid column and clipped the drawing off the left edge.
   Phones get the portrait redraw instead, which is drawn at 340 units and lands near 1:1. */
.schematic-band{padding:8px 0 0}
.schematic-band .schematic{width:min(96vw,1500px);max-width:none;
  margin:0 0 0 calc(50% - min(96vw,1500px) / 2)}
.schematic-band .schematic figcaption{max-width:72ch}
@media(max-width:1100px){
  /* below this the landscape drawing cannot be read at any width that fits, so the
     portrait redraw takes over — capped, or it would scale its 340 units up and balloon */
  .schematic-band .schematic{width:100%;margin-left:0}
  .schematic-band .schematic img{max-width:420px;margin-left:auto;margin-right:auto}
}

/* Portfolio grid — the shot leads, the words follow. Two up on desktop so each
   capture is large enough to actually be looked at; one up below 820px. */
.pf-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line)}
.pf{background:var(--paper);padding:24px 24px 28px;display:flex;flex-direction:column;
  transition:background .35s}
.pf:hover{background:var(--paper-2)}
.pf .wshot{margin:0 0 20px}
.pf-body{display:flex;flex-direction:column;flex:1}
.pf-kind{font-family:var(--data);font-size:.7rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ember-ink)}
/* h2, not h3: the work page ran h1 -> 13 x h3 with no second level. Weight is
   restated because the base ramp gives h3 850 and h2 800 -- the fix is semantic
   and must not move a pixel. */
.pf h2{font-size:1.24rem;margin:12px 0 0;font-weight:850}
.pf-where{font-family:var(--data);font-size:.76rem;letter-spacing:.03em;color:var(--sage-dk);
  margin-top:7px}
/* :not(.pf-frame p) keeps this off the placeholder card. .pf p:not(.pf-where) is
   (0,2,1) and was beating .pf-frame p (0,1,1), forcing --ink-soft (a LIGHT-ground token)
   onto the --ground panel at 2.92:1. The authored --sage measures 5.3:1. */
.pf .pf-body p:not(.pf-where){margin-top:13px;color:var(--ink-soft);font-size:.95rem}
.pf .visit{margin-top:16px;align-self:flex-start}
/* The open slot. Eleven real builds leave one empty cell in a two-column grid, so
   the twelfth card IS the empty cell — the same proportions and the same furniture as
   the others, but at zero. Deliberately NOT a mocked-up screenshot of an imaginary
   client: an invented site sitting among eleven real ones would undo the page.
   The device is the site's own tick meter with only the first tick lit — first light,
   which is the anchor this whole design is named after. */
.pf-frame{position:relative;aspect-ratio:880/550;background:var(--ground);
  border:1px solid var(--line-dk);display:grid;place-items:center;overflow:hidden;
  margin:0 0 20px}
/* registration marks: hairline corners, ember only at the tips — edge light, not fill */
.pf-frame::before,.pf-frame::after{content:"";position:absolute;width:20px;height:20px;
  border-color:rgba(228,89,15,.55);border-style:solid}
.pf-frame::before{top:13px;left:13px;border-width:1px 0 0 1px}
.pf-frame::after{bottom:13px;right:13px;border-width:0 1px 1px 0}
.pf-frame-in{text-align:center;padding:0 24px}
.pfm{position:relative;height:15px;width:min(190px,60%);margin:0 auto}
.pfm::before{content:"";position:absolute;inset:0;background:var(--haze);opacity:.15;
  -webkit-mask-image:var(--tick);mask-image:var(--tick);
  -webkit-mask-repeat:repeat;mask-repeat:repeat}
.pfm i{position:absolute;top:-3px;bottom:-3px;left:0;width:3px;background:var(--ember)}
.pf-frame p{margin-top:15px;font-family:var(--data);font-size:.7rem;letter-spacing:.17em;
  text-transform:uppercase;color:var(--sage)}
.pf-next .btn{margin-top:18px;align-self:flex-start}

@media(max-width:820px){ .pf-grid{grid-template-columns:1fr} }

.wshot{margin:20px 0 0;max-width:100%}
.wshot img{display:block;width:100%;height:auto;border:1px solid var(--line-dk);
  border-radius:3px;background:var(--ground-2)}
/* --ink-soft is correct on paper (5.99:1) but only 2.74:1 on the dark grounds, so the
   dark contexts take --sage (5.48-5.84:1). One token cannot serve both. */
#work .wshot figcaption,.bg-dark .wshot figcaption,.disclose .wshot figcaption,
#work .schematic figcaption,.disclose .schematic figcaption{color:var(--sage)}
.wshot figcaption{margin-top:8px;font-family:var(--data);font-size:.72rem;
  letter-spacing:.02em;color:var(--ink-soft)}
/* a trailing link in a caption is a standalone target, not a link inside a sentence,
   so it is held to the tap-target minimum rather than exempted */
.wshot figcaption a,.schematic figcaption a,.shotfig figcaption a{display:inline-block;
  padding:7px 0;color:var(--ember-ink);border-bottom:1px solid var(--line)}
.wshot figcaption a{color:var(--ember-lt);border-bottom-color:rgba(228,89,15,.4)}
.inst .wshot img{border-color:var(--line-dk)}
@media(max-width:980px){.wshot{max-width:520px}}

/* ── the automation schematic ──────────────────────────────────────
   A drawn diagram, never a mocked-up UI. The caption says so, because a fabricated
   screenshot of a login-gated console would be invented evidence. */
.schematic{margin:26px 0 34px}
/* <picture> is inline by default, which leaves a text-baseline gap under the drawing */
.schematic picture,.wshot picture,.shotfig picture{display:block}
.schematic img{display:block;width:100%;height:auto;border:1px solid var(--line);
  border-radius:3px;background:var(--ground)}
.schematic figcaption{margin-top:10px;font-family:var(--data);font-size:.76rem;
  line-height:1.55;color:var(--ink-soft)}

/* ── clickable portfolio screenshots ──────────────────────────────
   The picture is what people try to click, so it is the link. */
a.shotlink{display:block;position:relative;transition:transform .35s cubic-bezier(.2,.8,.2,1)}
a.shotlink img{transition:border-color .25s,filter .35s}
a.shotlink:hover{transform:translateY(-3px)}
a.shotlink:hover img{border-color:var(--ember);filter:saturate(1.06)}
a.shotlink:focus-visible{outline:2px solid var(--ember-lt);outline-offset:3px}
.visit-live{margin-left:18px}
@media(max-width:700px){.visit-live{margin-left:0;display:block;margin-top:10px}}

/* ══════════════════════════════════════════════════════════════════════════════
   THE RESPONSIVE LAYER
   Three intended layouts, not one layout squeezed three ways:

     ≤767px      phone   — one column, drawer nav, sticky CTA, accordion collapsed
     768–1024px  tablet  — TWO columns wherever two columns aid comprehension
     ≥1025px     desktop — the authored layout

   The nav collapses at 980px, not 767px: six mono items plus the CTA genuinely do
   not fit above that, so tablet keeps the drawer while keeping two-column content.
   Source order matters below — the tablet block is LAST so it re-establishes the
   columns that the shared ≤980px block flattens.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── shared by phone and tablet: the header, the drawer, the stacked hero ── */
@media(max-width:1024px){
  :root{--hdr-h:70px}
  /* only reserved once the bar exists, so a no-JS page has no empty band */
  html.mcta-ready{--mcta-h:68px}

  section{padding:80px 0}
  .hero{padding:calc(var(--hdr-h) + 30px) 0 84px}
  .field{height:96px}
  .hero .wrap{grid-template-columns:1fr;gap:36px}
  .svc-grid,.proof-2,.work-grid{grid-template-columns:1fr}
  .tsteps{grid-template-columns:repeat(2,1fr)}
  .phero{padding:calc(var(--hdr-h) + 38px) 0 58px}

  /* ── the drawer ────────────────────────────────────────────────────
     Full height and independently scrollable: once the CTA is inside it, a
     top-anchored auto-height panel ran off the bottom of a 320x568 screen with no
     way to reach the last item. And `visibility`, not transform alone, is what
     keeps the CLOSED drawer out of the tab order — a merely translated panel is
     still focusable, so keyboard users were tabbing through an invisible menu. */
  .nav-links{position:fixed;top:var(--hdr-h);left:0;right:0;bottom:0;
    flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;
    background:rgba(14,20,26,.985);
    -webkit-backdrop-filter:blur(18px) saturate(140%);backdrop-filter:blur(18px) saturate(140%);
    padding:6px 26px calc(30px + env(safe-area-inset-bottom,0px));
    overflow-y:auto;overscroll-behavior:contain;
    border-bottom:1px solid var(--line-dk);
    transform:translateY(-101%);visibility:hidden;
    transition:transform .42s cubic-bezier(.2,.8,.2,1),visibility 0s linear .42s}
  .nav-links.open{transform:none;visibility:visible;
    transition:transform .42s cubic-bezier(.2,.8,.2,1),visibility 0s linear 0s}
  .nav-links a{display:flex;align-items:center;min-height:56px;padding:15px 2px;
    font-family:var(--data);font-size:1rem;letter-spacing:.03em;color:var(--haze);
    border-bottom:1px solid var(--line-dk)}
  .nav-links a::after{display:none}
  /* :focus, not only :focus-visible. Opening the drawer moves focus to the first item
     programmatically, and Chrome does not treat programmatic focus after a pointer
     activation as "visible" — so the ring was missing on exactly the stop where the
     reader has just been moved somewhere new. */
  .nav-links a:focus{outline:2px solid var(--ember-lt);outline-offset:-2px}

  /* the bar itself only exists on the touch layouts */
  .mcta{display:flex}
  /* the one action the site drives toward, restored to the phone */
  .nav-links .nav-drawer-cta{display:inline-flex;justify-content:center;margin-top:24px;
    min-height:52px;border:1px solid var(--ember);font-size:.9rem;letter-spacing:.04em;
    color:var(--ground);background:var(--ember);
    /* capped and left-aligned: stretched across a 1024px drawer it stops reading as a
       button and starts reading as a banner */
    align-self:flex-start;width:100%;max-width:340px}
  .nav-links .nav-drawer-cta:hover{background:var(--ember-lt);border-color:var(--ember-lt)}
  .nav-cta > .btn{display:none}
  .burger{display:flex}
  @media(prefers-reduced-motion:reduce){
    .nav-links{transition:none}
    .nav-links.open{transition:none}
  }
}

/* ── touch targets: everything under 1025px is reached with a thumb ─────────
   Held to 44x44, not the WCAG 2.2 minimum of 24x24. Inline links inside a sentence
   are deliberately left alone — SC 2.5.8 exempts them and padding them breaks the
   line rhythm of the prose. Everything here is a standalone control: nav, footer,
   record links, caption links, disclosure headers, buttons. */
@media(max-width:1024px){
  .btn{min-height:48px}
  .visit{min-height:44px;display:inline-flex;align-items:center;padding:10px 0}
  .foot-links a{min-height:44px;display:flex;align-items:center;padding:9px 0}
  /* Stacked 44px targets with a 0px gap between them are one continuous strip to a
     thumb. Separate them enough to aim at. */
  .foot-links a + a{margin-top:6px}
  .rail-box .psrc a,.phero .meta a,
  .wshot figcaption a,.schematic figcaption a,
  .shotfig figcaption a{display:inline-flex;align-items:center;min-height:44px}
  .faq summary{min-height:52px;padding:16px 0}
  .pronounce{min-height:48px}
  .acc .acc-btn{min-height:56px}
}

/* ── the same controls, above 1025px ────────────────────────────────────────
   ⚠️ The block above is correct and was incomplete: it is scoped to max-width:1024px on
   the reasoning that "everything under 1025px is reached with a thumb." But WCAG 2.2
   SC 2.5.8 is NOT viewport-conditional. Above 1025px these same standalone links fell
   back to their natural line box — measured 14-15px tall on the live site at 1280, 1440,
   1920 and 2560: "Business automation ->" 141x14, "SEO/GEO and website revision ->"
   201x14, "How we measure ->" 110x15. A touchscreen laptop, a Surface, an iPad Pro in
   landscape and anyone using a trackpad with limited motor control all land here.
   Held to the 24px minimum rather than the 44px thumb figure, because a mouse is more
   precise than a thumb and 44px would visibly loosen a deliberately tight rail.
   MUST stay inside min-width:1025px: an unconditional rule would sit LATER in the file at
   equal specificity and quietly downgrade the 44px thumb targets above to 24px. */
@media(min-width:1025px){
  .rail-box .psrc a,.oc .psrc a,.phero .meta a,
  .wshot figcaption a,.schematic figcaption a,.shotfig figcaption a{display:inline-flex;align-items:center;min-height:24px}
  /* .foot-links a are STACKED nav links, not inline links inside a sentence. inline-flex
     collapsed each list onto a single row with a 5px gap, so "Business automation SEO/GEO
     & website revision The FORGE System" rendered as one run-on string on every page at
     1025px and up. Correct below 1025px, broken above it. They take the same 24px target
     as a block-level flex item. */
  .foot-links a{display:flex;align-items:center;min-height:24px}
}

/* Breadcrumb links were in NO tap-target rule at any width — measured 36x18 on phones and
   31x15 from 768px up, so they failed everywhere. Unconditional is safe here precisely
   because nothing else sets their height. */
.crumb a{display:inline-flex;align-items:center;min-height:24px}

/* ── phone: ≤767px ───────────────────────────────────────────────────────── */
@media(max-width:767px){
  /* Stable gutters prevent desktop layout shift, but on a phone they permanently
     surrender 15px of the narrowest and most valuable reading width. Mobile
     scrollbars overlay the page, so let the platform manage the gutter here. */
  html{scrollbar-gutter:auto}
  /* padding: 112px of section air is a third of a phone screen spent on nothing */
  section{padding:58px 0}
  .wrap{padding:0 19px}
  footer{padding:46px 0 26px}
  .foot-bot{margin-top:30px;padding-top:18px}

  /* ── the hero has to be readable in the first screen ──
     Header 96->70, top padding 170->100, four authored h1 lines at 2.05–2.9rem,
     and the CTA goes full width so it is the obvious next move rather than a
     small target floating in the middle of the column. */
  .hero{padding:calc(var(--hdr-h) + 22px) 0 66px}
  .hero h1{--hf:clamp(2.05rem,8.1vw,2.9rem);margin-top:13px;letter-spacing:-.008em}
  .hero p.sub{margin:15px 0 20px;font-size:1.02rem;line-height:1.52;max-width:none}
  .hero .cta{gap:10px}
  .hero .cta .btn{width:100%;justify-content:center}
  .field{height:74px}
  .reading{padding:22px 20px;margin-top:18px}
  .reading h3{margin:18px 0 9px;font-size:1.12rem}
  .pronounce{min-height:48px;padding:9px 13px;gap:10px}
  .pron-quip{margin-top:10px}

  /* ── section heads and body copy sized for a phone, not scaled down from 1180px ── */
  .sec-head{margin-bottom:32px}
  .sec-head h2{font-size:clamp(1.7rem,7.1vw,2.2rem)}
  .sec-head p{margin-top:15px;font-size:1.02rem}
  .phero h1{font-size:clamp(1.95rem,7.5vw,2.6rem)}
  .answer{margin-top:19px;padding-left:16px}
  .answer p{font-size:1.06rem}
  .prose h2{margin-top:40px}
  .prose h3{margin-top:28px}
  .dcols{gap:26px}
  .band h2{font-size:clamp(1.75rem,7vw,2.3rem)}
  .sec-head h2,.phero h1,.band h2,.why h2,.trigger h3{text-wrap:balance}

  /* ── raise every label that a phone made unreadable ──
     These are all rem-based against a 16px root while body copy is 18px, so the
     mono labels were landing at 11.0–12.5px. Nothing here is below 12.8px now. */
  .eyebrow{font-size:.8rem;letter-spacing:.15em;gap:9px;align-items:flex-start}
  .eyebrow::before{margin-top:.45em}
  .crumb,.phero .meta,.foot-bot{font-size:.8rem}
  .mlabel{font-size:.78rem;letter-spacing:.1em}
  .rd-src,.rd-date{font-size:.76rem}
  .frole,.stage-tag,.oc .plabel,.rail-box h4,.disclose h4,.foot-links .foot-h,.foot-links h4{font-size:.8rem}
  .legend,.psrc,.note,.oc-foot,.tsrc,.method,.method b,.rd-note,.intake-status,
  .fld .err,figcaption,.shotfig figcaption,.wshot figcaption,
  .schematic figcaption,.pron-quip{font-size:.86rem}
  /* .rail-box .psrc is a two-class selector, so the single-class rule above lost to it
     and the proof rail stayed at 11.2px */
  .rail-box .psrc,.oc .psrc{font-size:.86rem}
  .acc .acc-panel .acc-get{font-size:.86rem}
  .fld label{font-size:.84rem;letter-spacing:.03em}
  .visit{font-size:.82rem}
  .foot-links a{font-size:1rem}
  /* body copy: nothing under 16.3px */
  .svc p,.lrow p,.claim .ct,.disclose p,.wcard>p,.inst .what,.oc .how,.nope li,
  .svc-list li,.ffacts li,.fwhy,.flead,.rail-box p{font-size:1.02rem}
  .faq summary{font-size:1.06rem}

  .btn{padding:14px 22px}
  .faq details p{padding-bottom:18px}

  /* ── cards: less padding, less repetition of the same vertical rhythm ── */
  .svc,.proof,.wcard,.oc{padding:24px 20px}
  .disclose{padding:22px 18px}
  .disclose p+p{margin-top:11px}
  .band{padding:34px 20px}
  .trigger{padding:32px 20px}
  .tsteps{grid-template-columns:1fr}
  .nope,.rail-box{padding:20px 18px}
  .ledger .lrow{grid-template-columns:auto 1fr;gap:14px;padding:20px 0}
  .lrow p{grid-column:2;margin-top:8px}
  .lrow h3{font-size:1.15rem}
  .claim{grid-template-columns:1fr;gap:5px;padding:18px 0}
  .claim .cn{font-size:.86rem;letter-spacing:.1em}
  .founder{grid-template-columns:1fr;gap:22px}
  .fportrait{width:min(82vw,300px);max-width:none}
  .ffacts{margin-top:22px}

  /* forms: one column, and the submit is not a small target in a wide row */
  .intake{grid-template-columns:1fr;gap:15px}
  .intake-foot .btn{width:100%;justify-content:center}
  .fld textarea{min-height:120px}

  /* footer: two columns of links beats one 900px-tall stack */
  .foot-links{display:grid;grid-template-columns:repeat(auto-fit,minmax(136px,1fr));gap:20px 18px}

  /* the accordion loses its indent — 2.7em of gutter is real copy width at 320px */
  .acc .acc-name{font-size:1.22rem}
  .acc .acc-sum{padding-left:0;font-size:1.02rem}
  .acc .acc-panel{margin-left:0;padding-bottom:24px}
  .acc .acc-panel p{font-size:1.02rem}
  .acc .acc-btn{padding:17px 0;gap:12px;min-height:58px}
}

/* ── the 320–360px floor ─────────────────────────────────────────────────
   The hero's four authored lines plus a 60-word subhead plus the CTA is the tightest
   thing on the site. At 320px the sub wraps to ten lines, so the headline, the sub and
   the button are pulled up another ~30px to keep the whole argument in one screen. */
@media(max-width:360px){
  .hero{padding:calc(var(--hdr-h) + 18px) 0 56px}
  .hero h1{--hf:1.95rem;margin-top:10px}
  .hero p.sub{margin:13px 0 18px;font-size:1rem;line-height:1.5}
  .wrap{padding:0 16px}
  .band,.trigger{padding:28px 16px}
  .svc,.proof,.wcard,.oc{padding:22px 16px}
}

/* ── landscape phones: the hero cannot spend 250px on padding at 430px tall ── */
@media(max-width:1024px) and (max-height:520px) and (orientation:landscape){
  .hero{padding:calc(var(--hdr-h) + 16px) 0 40px}
  .hero .wrap{gap:24px}
  .field{height:52px}
  .phero{padding:calc(var(--hdr-h) + 22px) 0 34px}
  section{padding:46px 0}
  .nav-links a{min-height:48px;padding:11px 2px}
  .nav-links .nav-drawer-cta{margin-top:16px}
  .mcta{padding:7px 14px calc(7px + env(safe-area-inset-bottom,0px))}
  html.mcta-ready{--mcta-h:62px}
}

/* ── tablet: 768–1024px. Deliberately NOT one column. ─────────────────────
   Placed last so it re-establishes the columns the shared ≤980px block flattens. */
@media(min-width:768px) and (max-width:1024px){
  section{padding:76px 0}
  /* the proof rail moves below the prose, but as a two-up row: three rail boxes in
     one column add ~380px of scroll and say nothing extra */
  .dcols > aside{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:start}
  .dcols > aside .rail-box + .rail-box{margin-top:0}
  .svc-grid,.svc-grid.svc-2,.oc-grid,.work-grid,.proof-2{grid-template-columns:1fr 1fr}
  /* an odd last card would otherwise leave an empty cell showing the grid's own
     1px gap color as a solid block */
  .svc-grid > .svc:last-child:nth-child(odd),
  .work-grid > .wcard:last-child:nth-child(odd),
  .oc-grid > .oc:last-child:nth-child(odd){grid-column:1 / -1}
  .tsteps{grid-template-columns:1fr 1fr}
  /* the portrait stays beside the credentials — collapsing it wastes 500px of width
     and separates the face from the name it is accountable for */
  .founder{grid-template-columns:280px minmax(0,1fr);gap:38px}
  .fportrait{width:280px;max-width:none}
  .intake{grid-template-columns:1fr 1fr}
  .shotfig{grid-template-columns:minmax(0,1fr) 148px}
  .foot-links{display:flex;gap:40px}
  /* the walk-through keeps its number rail and puts the figures beside the copy */
  .inst{grid-template-columns:auto minmax(0,1fr) minmax(0,.92fr);gap:26px}
  .inst .col3{grid-column:auto}
  /* one column at this width means the subhead must fill it — a 38ch measure left the
     right half of an iPad empty */
  .hero p.sub{max-width:56ch}
  .hero .cta .btn{width:auto}
}

/* ── the wide end of tablet: the hero earns its two columns back ───────────
   Below 900px the reading card is too narrow to be worth a column of its own; at
   900–1024 there is room for the argument on the left and the instrument on the
   right, which is the composition the page was designed around. */
@media(min-width:900px) and (max-width:1024px){
  .hero .wrap{grid-template-columns:1.1fr .9fr;gap:34px;align-items:start}
  .hero p.sub{max-width:40ch}
  .hero{padding:calc(var(--hdr-h) + 44px) 0 96px}
}
