/* ═══════════════════════════════════════════════════════════════════
   Thyra Tech — "Threshold"
   Plain CSS. No build step. Tokens first, then the hero, then the page.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Fonts (self-hosted, only the weights actually used) ───────────── */
@font-face{font-family:'Bricolage';src:url('/assets/fonts/bricolage-600.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:'Bricolage';src:url('/assets/fonts/bricolage-700.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:'Archivo';src:url('/assets/fonts/archivo-regular.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Archivo';src:url('/assets/fonts/archivo-600.woff2') format('woff2');font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:'JBMono';src:url('/assets/fonts/jetbrains-mono-500.woff2') format('woff2');font-weight:400 500;font-style:normal;font-display:swap}

/* ── Tokens ────────────────────────────────────────────────────────── */
:root{
  --canvas:#0a0c11;
  --canvas-2:#0d1017;
  --panel:#12161d;
  --panel-2:#171c25;
  --line:#232935;
  --line-strong:#38404f;
  --line-interactive:#5b6675;  /* borders you can click or type in: 3:1 minimum */
  --accent:#ff5a1f;
  --accent-hover:#ff7440;
  --accent-muted:rgba(255,90,31,.20);
  --text-primary:#f2efe9;
  --text-secondary:#99a0ad;
  --text-dim:#858e9c;

  --display:'Bricolage','Bricolage Grotesque',Georgia,serif;
  --body:'Archivo',system-ui,-apple-system,'Segoe UI',sans-serif;
  --mono:'JBMono','SFMono-Regular',Consolas,monospace;

  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);

  --tshadow:0 1px 2px rgba(5,5,10,.95),0 3px 12px rgba(5,5,10,.78),0 10px 44px rgba(5,5,10,.8);

  --gut:clamp(20px,5vw,72px);
  --maxw:1240px;
}

/* ── Reset ─────────────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box}
html,body{overflow-x:hidden;overflow-x:clip}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--canvas);color:var(--text-primary);
  font:400 clamp(16px,1.05vw,18px)/1.62 var(--body);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
img,svg,video{display:block;max-width:100%}
img{height:auto}
h1,h2,h3{margin:0;font-family:var(--display);font-weight:700;line-height:1.02;letter-spacing:-.02em}
p{margin:0}
ul,ol,dl,dd{margin:0;padding:0}
li{list-style:none}
a{color:inherit}
button{font:inherit;color:inherit}

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

.skip{
  position:fixed;top:10px;left:10px;z-index:200;
  padding:10px 16px;background:var(--accent);color:#0b0d12;font-weight:600;
  border-radius:4px;transform:translateY(-160%);transition:transform .25s var(--ease);
}
.skip:focus{transform:translateY(0)}

/* ── The fixed background environment (one place, not stacked sections) */
.env{position:fixed;inset:0;z-index:0;pointer-events:none}
.env-glow{
  position:absolute;inset:-20%;
  background:radial-gradient(ellipse 55% 45% at 50% 30%,rgba(255,90,31,.055),transparent 62%);
  animation:envDrift 140s var(--ease) -40s infinite alternate;
}
.env-dust{
  position:absolute;inset:0;opacity:.5;
  background-image:
    radial-gradient(1.4px 1.4px at 12% 22%,rgba(255,180,140,.30),transparent),
    radial-gradient(1.2px 1.2px at 68% 14%,rgba(255,200,170,.22),transparent),
    radial-gradient(1.6px 1.6px at 84% 62%,rgba(255,170,130,.24),transparent),
    radial-gradient(1.2px 1.2px at 32% 78%,rgba(240,235,225,.16),transparent),
    radial-gradient(1.3px 1.3px at 52% 46%,rgba(255,190,150,.18),transparent);
  animation:envDust 96s linear -30s infinite;
}
.env-grain{
  position:absolute;inset:0;opacity:.32;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.45'/%3E%3C/svg%3E");
}
@keyframes envDrift{from{transform:translate3d(-3%,-2%,0) scale(1)}to{transform:translate3d(3%,2%,0) scale(1.08)}}
@keyframes envDust{from{transform:translate3d(0,0,0)}to{transform:translate3d(0,-14%,0)}}

/* ── Nav ───────────────────────────────────────────────────────────── */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:100;
  display:flex;align-items:center;justify-content:space-between;gap:24px;
  padding:16px var(--gut) 28px;
  /* The scrim has to work over two very different backdrops: the hero video,
     where it should stay a soft veil, and the flat content sections, where
     large display headings scroll underneath it. At .82 fading straight to
     zero over a 2px blur, those headings stayed crisp and collided with the
     wordmark. Holding the tint most of the way down and blurring harder
     obscures them while still reading as a fade over the video. */
  background:linear-gradient(to bottom,rgba(11,13,18,.96) 0%,rgba(11,13,18,.88) 46%,rgba(11,13,18,.55) 76%,rgba(11,13,18,0) 100%);
  backdrop-filter:blur(7px);
  -webkit-backdrop-filter:blur(7px);
  pointer-events:none;
}
.nav>*{pointer-events:auto}
.nav-mark{display:flex;align-items:center;gap:12px;text-decoration:none;font-family:Georgia,serif;font-weight:700;letter-spacing:-.02em;font-size:1.25rem}
/* flex-shrink:0 is not cosmetic. As a shrinkable flex item this collapsed to
   width:0 at 320 and 360px once the menu button joined the bar — the bar
   "fitting" at those widths was the logomark being silently deleted to make
   room, wordmark left floating with no glyph. The mark holds its size; the
   narrow-width rules further down give up other things instead. */
.nav-mark svg{width:19px;height:25px;flex-shrink:0;color:var(--accent)}
.nav-rule{width:1px;height:24px;background:var(--line-strong);margin-left:7px}
.nav-greek{font-family:var(--mono);font-weight:500;font-size:.72rem;letter-spacing:.17em;color:var(--accent)}
.nav nav{display:flex;align-items:center;gap:clamp(14px,2vw,28px)}
.nav nav a{text-decoration:none;font-size:.9rem;color:var(--text-secondary);transition:color .22s var(--ease)}
.nav nav a:hover{color:var(--text-primary)}
.nav-cta{
  padding:9px 17px;border:1px solid var(--line-interactive);border-radius:999px;
  color:var(--text-primary)!important;
  transition:border-color .22s var(--ease),background-color .22s var(--ease),color .22s var(--ease)
}
.nav-cta:hover{border-color:var(--accent);background:var(--accent);color:#0b0d12!important}
/* ── The mobile menu ───────────────────────────────────────────────── */
/* A <details> disclosure. The three links used to be `display:none` below
   760px, which left a phone with the wordmark, one button, and no way to
   reach Lead system, What you keep or Questions.

   THE DEFAULT STATE HERE IS THE DISCLOSURE, at every width. That is the
   no-JS rendering, and it is a working menu rather than a broken bar —
   which matters because privacy.html, sms-terms.html and 404.html load no
   JavaScript at all. The wide inline bar is the enhancement, applied by the
   `[open]` rules below once the inline script after each </header> has set
   the attribute.

   Why the attribute and not CSS: a CLOSED <details> is hidden from the
   accessibility tree even when CSS forces its contents visible — measured
   2026-09-05, the three links rendered on screen and did not appear in the
   tree at all, and their flex layout collapsed on top of itself as well.
   Styling it open would have looked right and been unusable. */
.nav-menu{position:static}
.nav nav{position:relative}

.nav-menu-btn{
  display:inline-flex;align-items:center;gap:9px;
  min-height:44px;padding:0 4px;
  font-size:.9rem;color:var(--text-secondary);cursor:pointer;
  list-style:none;   /* kill the disclosure triangle */
}
.nav-menu-btn::-webkit-details-marker{display:none}
.nav-menu-btn:hover,.nav-menu[open] .nav-menu-btn{color:var(--text-primary)}

/* The panel hangs off the BAR's right edge, not the summary's — anchored
   under the CTA and the gutter, where a dropdown is expected. Anchored to
   the summary it floated mid-bar with the wordmark showing past it. */
.nav-menu-links{
  display:none;
  position:absolute;top:calc(100% + 14px);right:0;min-width:210px;
  flex-direction:column;
  padding:6px;
  background:var(--panel);border:1px solid var(--line-strong);border-radius:4px;
  box-shadow:0 18px 44px rgba(3,4,8,.66);
}
.nav-menu[open] .nav-menu-links{display:flex}

/* Three bars from one element: the box is the middle bar, the two
   pseudo-elements are the outer pair. */
.nav-burger{
  position:relative;display:block;width:18px;height:2px;
  background:currentColor;border-radius:2px;
}
.nav-burger::before,.nav-burger::after{
  content:"";position:absolute;left:0;width:18px;height:2px;
  background:currentColor;border-radius:2px;
  transition:transform .22s var(--ease);
}
.nav-burger::before{top:-6px}
.nav-burger::after{top:6px}
/* Open: the outer bars cross, the middle one gets out of the way. */
.nav-menu[open] .nav-burger{background:transparent}
.nav-menu[open] .nav-burger::before{transform:translateY(6px) rotate(45deg)}
.nav-menu[open] .nav-burger::after{transform:translateY(-6px) rotate(-45deg)}

/* Full-width rows: the whole row is the tap target, not just the words. */
.nav nav .nav-menu-links a{
  display:flex;align-items:center;min-height:46px;
  padding:0 14px;border-radius:3px;
  font-size:.98rem;color:var(--text-primary);
}
.nav nav .nav-menu-links a+a{border-top:1px solid var(--line)}
.nav nav .nav-menu-links a:active{background:var(--panel-2)}

/* Wide, once the script has opened it: the dropdown flattens into a row in
   the bar. The links stay wrapped in the disclosure and the wrapper becomes
   the flex row — NOT `display:contents` on the <details>, which was tried
   first and lost the gap between the links entirely (they came out 4px
   apart). <details> carries a UA shadow tree with slots, so contents on the
   host does not reliably hand its children to the outer flex container.
   Laying the wrapper out as its own row sidesteps the whole question. */
@media (min-width:761px){
  .nav-menu[open]{display:flex;align-items:center}
  .nav-menu[open] .nav-menu-btn{display:none}
  .nav-menu[open] .nav-menu-links{
    display:flex;flex-direction:row;align-items:center;
    gap:clamp(14px,2vw,28px);
    position:static;min-width:0;padding:0;
    background:none;border:0;border-radius:0;box-shadow:none;
  }
  .nav nav .nav-menu[open] .nav-menu-links a{
    display:inline-block;min-height:0;padding:0;border-radius:0;
    font-size:.9rem;color:var(--text-secondary);
  }
  .nav nav .nav-menu[open] .nav-menu-links a+a{border-top:0}
  .nav nav .nav-menu[open] .nav-menu-links a:hover{color:var(--text-primary)}
}

/* Adding the menu button put the bar back over its width at 320px — the
   CTA's right edge landed 40px past the viewport. Tightening the bar's own
   gap recovers most of it. */
@media (max-width:420px){.nav{gap:12px}}

/* Below 380px the word goes and the icon stays; `aria-label="Menu"` on the
   summary carries the name, so nothing is lost to a screen reader. Given a
   choice between the word and the logomark at these widths, the logomark
   wins — a burger glyph needs no label, the brand mark has no substitute. */
@media (max-width:380px){
  .nav-menu-word{display:none}
  .nav-menu-btn{gap:0;padding:0 6px}
}

/* The very smallest. Even without the word, restoring the logomark's 19px
   put 320px five over; the two flex gaps give it back. */
@media (max-width:359px){
  .nav{gap:8px}
  .nav-mark{gap:8px}
}

/* Phones. At 375px the bar wanted about 339px of content inside 335px of
   usable width, so the wordmark broke to "Thyra / Tech" and the CTA to
   "Get / started" — four px of overflow reading as a broken header.
   The divider and the Greek are decorative (both aria-hidden) and are the
   cheapest ~60px in the bar, so they go first and everything else keeps its
   size. nowrap is the guard: these two are the site's name and its only
   mobile action, and neither may ever wrap again. */
@media (max-width:560px){
  .nav-rule,.nav-greek{display:none}
}
.nav-mark>span,.nav-cta{white-space:nowrap}

/* ── Buttons ───────────────────────────────────────────────────────── */
.btn{
  display:inline-block;padding:14px 26px;border:0;border-radius:2px;
  background:var(--accent);color:#0b0d12;text-decoration:none;
  font-family:var(--body);font-weight:600;font-size:.98rem;letter-spacing:.01em;
  text-shadow:none;cursor:pointer;
  transition:background-color .22s var(--ease),transform .22s var(--ease),box-shadow .3s var(--ease);
}
.btn:hover{background:var(--accent-hover);transform:translateY(-2px);box-shadow:0 10px 34px rgba(255,90,31,.24)}
.btn-lg{padding:16px 34px;font-size:1.04rem}
@media (pointer:coarse){.btn{min-height:44px;display:inline-flex;align-items:center;justify-content:center}}

/* ═══════════════  HERO  ═══════════════════════════════════════════ */
.hero{position:relative;z-index:1;height:600vh}
.stage{
  position:sticky;top:0;height:100vh;height:100svh;overflow:hidden;
  background:var(--canvas);
}
.poster{
  position:absolute;inset:0;background-size:cover;background-position:center;
  transition:opacity .6s var(--ease);
}
#hero-video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity .55s var(--ease);
  will-change:transform;transform:translateZ(0);
}
.stage.video-ready #hero-video{opacity:1}
.stage.video-failed #hero-video{display:none}

.scrim{
  position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse 120% 90% at 50% 45%,rgba(10,10,18,0) 35%,rgba(10,10,18,.62) 100%);
}

.ring{
  position:absolute;right:calc(var(--gut) - 4px);bottom:calc(var(--gut) - 4px);
  width:38px;height:38px;color:var(--accent);
  transform:rotate(-90deg);opacity:.85;
  transition:opacity .4s var(--ease);
}
.ring circle{transition:stroke-dashoffset .12s linear}
.stage.video-ready .ring{opacity:0}

.cue{position:absolute;left:50%;bottom:26px;transform:translateX(-50%);opacity:.55}
.cue span{
  display:block;width:1px;height:44px;
  background:linear-gradient(to bottom,transparent,var(--text-secondary));
  animation:cueDrop 2.8s var(--ease) -.6s infinite;transform-origin:top;
}
@keyframes cueDrop{0%,100%{transform:scaleY(.4);opacity:.3}50%{transform:scaleY(1);opacity:.85}}
.stage.scrolled .cue{opacity:0;transition:opacity .4s var(--ease)}

/* ── Caption bands (two-sided variant: the centre lane stays bright) ── */
.bands{position:absolute;inset:0;pointer-events:none}
.band{
  position:absolute;top:0;bottom:0;display:flex;flex-direction:column;justify-content:center;
  opacity:0;text-shadow:var(--tshadow);
}
.band .btn{text-shadow:none}
.band--left{left:0;width:34%;padding-left:var(--gut);padding-right:12px;align-items:flex-start;text-align:left}
.band--right{right:0;width:34%;padding-right:var(--gut);padding-left:12px;align-items:flex-end;text-align:right}
.band--settle{left:0;right:0;align-items:center;text-align:center;justify-content:flex-end;padding:0 var(--gut) 13vh}

/* The scrim that keeps a caption legible over the video.

   z-index MUST NOT be negative here. `.stage` is position:sticky, which
   always creates a stacking context, so a negative z-index does not tuck the
   pseudo-element behind its own text — it drops it behind every positioned
   child of the stage, the video included, and the scrim disappears entirely.
   It sat at -1 until 2026-09-05 and had never once painted; the captions were
   reading straight off the video, which is why the settle line over the open
   doorway measured 1.6:1. Layer it explicitly instead: scrim at 0, the text
   above it at 1. */
.band::before{
  content:"";position:absolute;inset:-6% -10%;pointer-events:none;z-index:0;
  opacity:calc(.25 + .75*var(--k,1));
}
.band>*{position:relative;z-index:1}
.band--left::before{background:radial-gradient(ellipse 86% 58% at 30% 50%,rgba(5,5,10,.70) 0%,rgba(5,5,10,.46) 46%,rgba(5,5,10,0) 76%)}
.band--right::before{background:radial-gradient(ellipse 86% 58% at 70% 50%,rgba(5,5,10,.70) 0%,rgba(5,5,10,.46) 46%,rgba(5,5,10,0) 76%)}
/* The settle band is the one caption that lands on the brightest part of
   the shot: by the last frame the doorway is open and the floor in front of
   it is near-white. Measured behind the sub line, the backdrop averaged
   rgb(210,193,138) and the radial below only reached the middle of it —
   1.6:1 against the text. The linear pass runs the full width, so the
   contrast no longer depends on where in the frame the line happens to sit. */
.band--settle::before{
  inset:auto -6% -8% -6%;height:74%;
  background:
    linear-gradient(to top,rgba(5,5,10,.72) 0%,rgba(5,5,10,.54) 34%,rgba(5,5,10,.22) 66%,rgba(5,5,10,0) 100%),
    radial-gradient(ellipse 76% 100% at 50% 100%,rgba(5,5,10,.55) 0%,rgba(5,5,10,.34) 44%,rgba(5,5,10,0) 78%);
}

.band .line{
  font-family:var(--display);font-weight:700;line-height:1.02;letter-spacing:-.025em;
  font-size:clamp(2.1rem,4.3vw,4.2rem);max-width:13ch;
}
.band--settle .line{font-size:clamp(2.4rem,5.2vw,5rem);max-width:14ch}
.band .sub{
  margin-top:20px;color:var(--text-primary);opacity:.9;
  font-size:clamp(.98rem,1.15vw,1.12rem);max-width:52ch;
}
.band .cta-row{margin-top:30px}

/* Screen-reader copy of split text */
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ── Text choreography: transform + opacity only, driven by --k ────── */
.w{display:inline-block;white-space:pre}
.c{display:inline-block;will-change:transform,opacity}

/* (a) blur-to-sharp — echoes the shot resolving out of haze */
[data-entrance="blur"] .line{position:relative}
[data-entrance="blur"] .soft{position:absolute;inset:0;filter:blur(11px);opacity:calc(1 - var(--k,0))}
[data-entrance="blur"] .sharp{opacity:var(--k,0)}

/* (b) approach-from-depth — echoes the camera pushing forward */
[data-entrance="depth"] .line{
  transform:scale(calc(.82 + .18*var(--k,0)));
  opacity:var(--k,0);
}
.band--left[data-entrance="depth"] .line{transform-origin:0% 50%}
.band--right[data-entrance="depth"] .line{transform-origin:100% 50%}
.band--settle[data-entrance="depth"] .line{transform-origin:50% 50%}

/* (c) halves parting — echoes the frame opening as the camera crosses */
[data-entrance="part"] .half{display:inline-block}
[data-entrance="part"] .half{white-space:nowrap}
[data-entrance="part"] .half-l{transform:translateX(calc((1 - var(--k,0)) * 0.42em))}
[data-entrance="part"] .half-r{transform:translateX(calc((1 - var(--k,0)) * -0.42em))}
[data-entrance="part"] .line{opacity:var(--k,0)}

/* (e) word-by-word rise into a staged settle — the arrival */
[data-entrance="settle"] .w{
  --kc:clamp(0,(var(--k,0) - var(--th,0))*2.6,1);
  opacity:var(--kc);transform:translateY(calc((1 - var(--kc))*22px));
}
[data-entrance="settle"] .sub{
  --ks:clamp(0,(var(--k,0) - .60)*3.4,1);
  opacity:var(--ks);transform:translateY(calc((1 - var(--ks))*14px));
}
[data-entrance="settle"] .cta-row{
  --kb:clamp(0,(var(--k,0) - .74)*4.4,1);
  opacity:var(--kb);transform:translateY(calc((1 - var(--kb))*14px));
}

/* ── Static hero (reduced motion, landscape phones) ────────────────── */
/* The fallback layer, not the mobile layer — phones scrub like everything
   else since 2026-09-05. It carries its own, smaller image rather than
   sharing the desktop poster. Because this element is display:none until a
   TIER 1 gate matches, no browser fetches the image unless it is shown. */
.static-hero{
  display:none;position:absolute;inset:0;z-index:3;
  flex-direction:column;justify-content:flex-end;
  padding:0 var(--gut) clamp(56px,12vh,110px);
  text-shadow:var(--tshadow);
  background:var(--canvas) url('/assets/hero-static.jpg') center/cover no-repeat;
}
/* Reduced motion on a phone held upright: the landscape still crops to a
   third of its frame here, so use the portrait cut the scrub already ships. */
@media (orientation:portrait) and (max-width:720px){
  .static-hero{background-image:url('/assets/hero-mobile-poster.jpg')}
}
.static-hero::before{
  content:"";position:absolute;inset:auto 0 0 0;height:72%;pointer-events:none;
  background:linear-gradient(to top,rgba(5,5,10,.88) 0%,rgba(5,5,10,.58) 40%,rgba(5,5,10,0) 100%);
}
.static-hero>*{position:relative}
.static-hero .line{font-family:var(--display);font-weight:700;font-size:clamp(2.2rem,9vw,3.4rem);line-height:1.02;letter-spacing:-.025em;max-width:13ch}
.static-hero .sub{margin-top:16px;color:var(--text-primary);opacity:.92;font-size:1.02rem;max-width:34ch}
.static-hero .cta-row{margin-top:26px}
.static-hero .btn{text-shadow:none}

/* ── The gates. TIER 1 below must match STATIC_GATES in site.js
      character-for-character, or one side loads what the other side hides.
      TIER 2 has no JS counterpart on purpose: it picks the caption LAYOUT
      by width, while site.js picks the video SOURCE by aspect ratio. Two
      different questions — do not try to keep them in lockstep. ─────── */

/* TIER 2 — narrow screens keep the scrub, in a portrait caption layout.
   The bands cannot stay in 34% side lanes at 390px: three words per line,
   and the two lanes collide. They become one full-width column, alternating
   left/right for the same rhythm the wide layout gets from the lanes. */
@media (max-width:720px),
       (orientation:portrait) and (max-width:1024px),
       (orientation:portrait) and (pointer:coarse){
  /* Shorter than the desktop 600vh: the same four captions over a phone's
     scroll distance, so the story lands in about four flicks rather than
     eight. Below 420vh the bands cross-fade faster than they can be read. */
  .hero{height:420vh}

  .band--left,.band--right,.band--settle{
    left:0;right:0;width:auto;
    padding-left:var(--gut);padding-right:var(--gut);
  }
  .band--left{align-items:flex-start;text-align:left}
  .band--right{align-items:flex-end;text-align:right}
  /* The first three sit above centre so the caption never fights the
     brightest part of the frame, which is the doorway in the middle. */
  .band--left,.band--right{justify-content:flex-start;padding-top:34vh}
  .band--settle{justify-content:flex-end;padding-bottom:15vh}

  /* Full-bleed scrims: at this width a 34%-wide radial leaves the far edge
     of the line sitting on bare video. */
  .band--left::before,.band--right::before{
    inset:-4% -12%;
    background:radial-gradient(ellipse 92% 62% at 50% 42%,rgba(5,5,10,.76) 0%,rgba(5,5,10,.52) 48%,rgba(5,5,10,0) 80%);
  }
  /* The portrait crop concentrates the bright floor into the bottom third,
     exactly where the settle band's heading and sub sit, so this needs to go
     further than the wide layout does. */
  .band--settle::before{
    inset:auto 0 -6% 0;height:66%;
    background:linear-gradient(to top,rgba(5,5,10,.86) 0%,rgba(5,5,10,.72) 38%,rgba(5,5,10,.36) 70%,rgba(5,5,10,0) 100%);
  }

  .band .line{font-size:clamp(2.1rem,8.4vw,3.2rem);max-width:11ch}
  .band--settle .line{font-size:clamp(2.2rem,9vw,3.4rem);max-width:12ch}
  .band .sub{margin-top:14px;font-size:1rem;max-width:32ch}

  /* The ring sits bottom-right over the loading frames; at phone gutters it
     needs to clear the scroll cue in the centre. */
  .ring{width:30px;height:30px}
}

/* TIER 1 — no scrub at all. Reduced motion is a promise we keep; a phone
   held sideways has no vertical room for the bands. Phones in portrait used
   to be here and are not any more (2026-09-05): the scroll story IS the
   hero, and cutting it left mobile on a still frame carrying stale copy.

   This block sits AFTER tier 2 deliberately. Both set `.hero` height at the
   same specificity, so source order decides, and a phone in portrait matches
   both when reduced motion is on. Tier 1 has to be the one that wins or that
   visitor gets 420vh of dead scroll behind a still image. */
@media (prefers-reduced-motion:reduce),
       (orientation:landscape) and (pointer:coarse) and (max-height:560px){
  .hero{height:100vh;height:100svh}
  .bands,.ring,.cue{display:none}
  .static-hero{display:flex}
}

/* Scrub-capable but short: keep the scrub, drop the overlays with no room */
@media (min-width:721px) and (max-height:560px) and (pointer:fine){.cue{display:none}}

/* ═══════════════  THE PAGE BELOW  ═════════════════════════════════ */
/* The hero is full bleed and is excluded here on purpose: the generic
   section rule would cap it to --maxw and give it section padding, which
   insets the sticky stage and letterboxes the video. */
main>section:not(.hero){position:relative;z-index:1;padding:clamp(88px,13vh,160px) var(--gut);margin:0 auto;max-width:var(--maxw)}
.kicker{
  font-family:var(--mono);font-weight:500;font-size:.74rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--accent);margin-bottom:18px;
}
main h2{font-size:clamp(1.9rem,4.1vw,3.1rem);max-width:20ch}

/* Reveal choreography */
/* FAIL-VISIBLE. Every section on this page is a .reveal, so hiding them
   unconditionally means one failed request to site.js renders a blank site.
   The hidden state is therefore gated on html.js, which the inline head
   script adds and a watchdog removes if site.js never checks in.

   The guard MUST stay wrapped in :where(). A bare `html.js .reveal>*` scores
   (0,2,1) and outranks `.reveal.in>*` at (0,2,0), so the entrance rule stops
   winning and every section stays at opacity 0 forever — the exact blank page
   this guard exists to prevent. :where() contributes no specificity, so the
   cascade below is identical to an unguarded `.reveal>*`. */
:where(html.js) .reveal>*{opacity:0;transform:translateY(18px);transition:opacity .7s var(--ease-out),transform .7s var(--ease-out)}
.reveal.in>*{opacity:1;transform:none}
.reveal.in>*:nth-child(2){transition-delay:.08s}
.reveal.in>*:nth-child(3){transition-delay:.16s}
.reveal.in>*:nth-child(4){transition-delay:.24s}
.reveal.done>*,.reveal.done>*:nth-child(2),.reveal.done>*:nth-child(3),.reveal.done>*:nth-child(4){transition-delay:0s}

/* The jamb divider: draws itself on scroll */
.jamb{position:relative;z-index:1;max-width:var(--maxw);margin:0 auto;padding:0 var(--gut);color:var(--line-strong)}
.jamb svg{width:100%;height:64px;overflow:visible}
.jamb-path{stroke-dasharray:1600;stroke-dashoffset:1600;transition:stroke-dashoffset 1.5s var(--ease-out)}
.jamb.in .jamb-path{stroke-dashoffset:0}

/* ── 1. Pain: one wide measure, left aligned, one big number ───────── */
.s-pain .lede{font-size:clamp(1.15rem,1.7vw,1.42rem);color:var(--text-secondary);max-width:46ch;line-height:1.5}
.pain-grid{display:grid;grid-template-columns:1fr;gap:clamp(34px,5vw,64px);margin-top:34px;align-items:start}
@media (min-width:900px){.pain-grid{grid-template-columns:1.15fr .85fr}}
.stat{border-left:2px solid var(--accent);padding-left:22px}
.stat-num{font-family:var(--display);font-weight:700;font-size:clamp(3.6rem,8vw,6.2rem);line-height:.9;letter-spacing:-.04em;color:var(--accent)}
.stat-num span{font-size:.42em;vertical-align:super;margin-left:.04em}
.stat-note{margin-top:12px;color:var(--text-secondary);font-size:.98rem;max-width:34ch}
.pain-close{margin-top:clamp(38px,6vh,64px);font-family:var(--display);font-weight:600;font-size:clamp(1.3rem,2.5vw,2rem);max-width:26ch;line-height:1.16}
.pain-cta{margin-top:28px}
.stat-source{margin-top:18px;color:var(--text-dim);font-size:.78rem;line-height:1.45;max-width:62ch}
.stat-source a{color:var(--text-secondary);text-underline-offset:3px}

/* ── 2. Idea: three apertures in a grid ────────────────────────────── */
.doors{display:grid;grid-template-columns:1fr;gap:clamp(30px,4vw,44px);margin-top:44px}
@media (min-width:820px){.doors{grid-template-columns:repeat(3,1fr)}}
.door{position:relative;background:var(--panel);border:1px solid var(--line);padding:0 0 26px;overflow:hidden}
.door img{width:100%;aspect-ratio:16/10;object-fit:cover;filter:saturate(.92) contrast(1.04)}
.door-frame{position:absolute;top:14px;left:14px;width:34px;height:44px;color:var(--accent);opacity:.7;z-index:2}
.door-frame svg{width:100%;height:100%}
.door-n{margin:22px 24px 8px;font-family:var(--mono);font-weight:500;font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;color:var(--accent)}
.door p:last-child{margin:0 24px;color:var(--text-secondary);font-size:.99rem}
.door::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse 60% 40% at 50% 0%,rgba(255,90,31,.10),transparent 70%);
  opacity:0;transition:opacity .5s var(--ease);
}
.door:hover::after{opacity:1}

/* The outcome path borrows the console language from the primary Thyra site,
   but stays horizontal here so the three outcomes read in one decisive pass. */
.lead-path{margin-top:44px;border:1px solid var(--line-strong);background:rgba(10,12,17,.52);overflow:hidden}
.lead-path-top{display:flex;justify-content:space-between;gap:20px;padding:18px 22px;border-bottom:1px solid var(--line);font-family:var(--mono);font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;color:var(--accent)}
.lead-path-top span:last-child{color:#7da5ff}
.lead-path-steps{--lead-gap:clamp(22px,3vw,46px);position:relative;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--lead-gap);padding:38px clamp(24px,4vw,46px) 34px;list-style:none}
.lead-step{position:relative;padding-top:32px}
.lead-dot{position:absolute;top:0;left:0;width:14px;height:14px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 5px rgba(255,90,31,.14);z-index:1}

/* The rail is drawn per step, from this dot's centre to the next one's, rather
   than as one line across the container. A container-wide line has to guess
   where the dots are, and it guessed wrong in both directions: the previous
   `left:9%;right:9%` put the rail 45px to the RIGHT of dot 01 and ran it 246px
   PAST dot 03 into empty space, because the dots sit at grid-column starts and
   9% has nothing to do with the column geometry. The stacked rule had the same
   overshoot vertically.

   Per-step the arithmetic is exact and survives any gap or column count. Two
   numbers do all the work: 7px is half the 14px dot, so it is the dot's centre
   on either axis, measured from the step's own border edge — and the segment
   has to end one gap plus one dot-centre later, which is the next dot.
   `:not(:last-child)` is what stops the rail overshooting: the last dot
   terminates the path instead of trailing a line off it. */
.lead-step:not(:last-child)::after{
  content:"";position:absolute;top:7px;left:7px;
  right:calc(-1 * (var(--lead-gap) + 7px));
  height:1px;background:var(--line-strong);
}
.lead-step--blue .lead-dot{background:#4f8cff;box-shadow:0 0 0 5px rgba(79,140,255,.14)}
.lead-index{font-family:var(--mono);font-size:.76rem;letter-spacing:.14em;color:var(--accent)}
.lead-step--blue .lead-index{color:#7da5ff}
.lead-step h3{margin:13px 0 10px;font-size:clamp(1.35rem,2.1vw,1.72rem);max-width:14ch}
.lead-step p{margin:0;color:var(--text-secondary);font-size:.95rem;line-height:1.5;max-width:27ch}
.lead-path-chips{display:flex;gap:14px;padding:0 clamp(24px,4vw,46px) 20px}
.lead-path-chips span{padding:9px 13px;border:1px solid var(--accent);font-family:var(--mono);font-size:.7rem;letter-spacing:.13em;text-transform:uppercase;color:var(--accent)}
.lead-path-chips span:last-child{border-color:#4f8cff;color:#7da5ff}
.lead-path-readout{display:flex;align-items:center;gap:12px;margin:0;padding:15px clamp(24px,4vw,46px);border-top:1px solid var(--line);font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:#7da5ff}
.lead-path-readout span{width:10px;height:10px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 4px rgba(255,90,31,.14)}
@media (max-width:700px){
  /* These two mono labels were dropped to .65rem (10.4px) to keep the flex
     row from colliding. Measured at 390px they need 236px of 354px, so the
     shrink was buying nothing and cost the smallest screen its smallest
     text. Held at the site's label size instead. */
  .lead-path-top{padding:16px 18px;font-size:.72rem;letter-spacing:.12em}
  .lead-path-steps{grid-template-columns:1fr;gap:0;padding:28px 22px 14px}
  /* Same segment, turned through 90°. The steps stack with gap:0, so the next
     dot's centre is 7px past this step's bottom edge — hence bottom:-7px. */
  .lead-step:not(:last-child)::after{
    top:7px;bottom:-7px;left:7px;right:auto;
    width:1px;height:auto;
  }
  .lead-step{padding:0 0 34px 32px}
  .lead-dot{top:0;left:0}
  .lead-path-chips{padding:0 22px 18px}
  .lead-path-readout{padding:15px 22px;font-size:.72rem;letter-spacing:.08em}
}

/* ── 3. The signature: press and hold ──────────────────────────────── */
.s-hold{text-align:center;max-width:820px}
.s-hold h2{margin:0 auto;max-width:14ch}
.hold-micro{margin-top:16px;color:var(--text-secondary)}
.aperture{
  position:relative;width:min(300px,66vw);aspect-ratio:200/260;margin:44px auto 34px;
  overflow:hidden;background:#05070a;
}
/* the lintel above the door */
.aperture::before{
  content:"";position:absolute;left:0;right:0;top:0;height:6%;z-index:5;
  background:linear-gradient(180deg,#1c222c,#141922);
  box-shadow:inset 0 -1px 0 var(--line-strong);
}
/* the light waiting behind the door: a lit space with depth, not a flat slab.
   Dark at the top where the room recedes, brightest at the floor. */
.aperture-light{
  position:absolute;inset:6% 0 0 0;
  background:
    radial-gradient(ellipse 88% 46% at 50% 104%,rgba(255,196,150,.92),transparent 72%),
    linear-gradient(to bottom,#2e1207 0%,#8f2c0c 34%,#d4491a 62%,var(--accent) 84%,#ff9455 100%);
  opacity:calc(.18 + .82*var(--open,0));
  transition:opacity .12s linear;
}
/* edge falloff, so it reads as a volume rather than a card */
.aperture-light::after{
  content:"";position:absolute;inset:0;
  box-shadow:inset 0 0 46px 18px rgba(8,4,2,.72);
}
/* the two leaves, machined metal, each carrying one trace */
.leaf{
  position:absolute;top:6%;bottom:0;width:50.5%;z-index:2;
  background:linear-gradient(180deg,#1a2029,#10141b);
  box-shadow:inset 0 0 0 1px rgba(56,64,79,.85);
  transition:transform .12s linear;
}
.leaf::before{content:"";position:absolute;top:38%;height:1px;width:54%;background:var(--accent);opacity:.45}
.leaf-l{left:0;transform:translateX(calc(-100% * var(--open,0)))}
.leaf-l::before{right:9%}
.leaf-r{right:0;transform:translateX(calc(100% * var(--open,0)))}
.leaf-r::before{left:9%}
/* the seam of light between them, brightest when shut */
.aperture-seam{
  position:absolute;left:50%;top:6%;bottom:0;width:2px;margin-left:-1px;z-index:3;
  background:var(--accent);box-shadow:0 0 16px 4px rgba(255,90,31,.65);
  opacity:calc(1 - var(--open,0));transition:opacity .12s linear;
}
.aperture-frame{position:absolute;inset:0;z-index:4;color:var(--line-strong);pointer-events:none;transition:color .6s var(--ease)}
.aperture-frame svg{width:100%;height:100%}
.s-hold.open .aperture-frame{color:var(--accent)}

.hold-btn{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  min-height:48px;padding:14px 32px;overflow:hidden;cursor:pointer;
  background:transparent;border:1px solid var(--line-interactive);border-radius:999px;
  font-weight:600;letter-spacing:.01em;-webkit-user-select:none;user-select:none;touch-action:none;
  transition:border-color .3s var(--ease),color .3s var(--ease);
}
.hold-btn:hover{border-color:var(--accent)}
/* The fill is a wash, not a solid: mix-blend-mode:difference turned the label
   cyan over full accent. A translucent fill keeps one label colour legible at
   every point between empty and full. */
.hold-fill{
  position:absolute;left:0;top:0;bottom:0;width:100%;
  background:var(--accent);opacity:.32;transform-origin:0 50%;
  transform:scaleX(var(--open,0));transition:transform .12s linear;
}
.hold-fill::after{
  content:"";position:absolute;right:0;top:0;bottom:0;width:2px;
  background:#ffcaa8;opacity:calc(1 - var(--open,0));
}
.hold-label{position:relative;color:var(--text-primary)}
.s-hold.open .hold-btn{border-color:var(--accent)}

.outcomes{margin-top:38px;display:flex;flex-direction:column;gap:14px;align-items:center}
.outcomes li{
  font-family:var(--display);font-weight:600;font-size:clamp(1.15rem,2.2vw,1.75rem);
  opacity:0;transform:translateY(14px);
  transition:opacity .55s var(--ease-out),transform .55s var(--ease-out);
}
.s-hold.open .outcomes li{opacity:1;transform:none}
.s-hold.open .outcomes li:nth-child(2){transition-delay:.18s}
.s-hold.open .outcomes li:nth-child(3){transition-delay:.36s}
/* Specificity matters here: .reveal.in>* also targets this element and would
   otherwise win, revealing the link before the door has been opened. */
.s-hold .hold-after{margin-top:30px;opacity:0;transform:translateY(10px);transition:opacity .6s var(--ease-out) .58s,transform .6s var(--ease-out) .58s}
.s-hold.open .hold-after{opacity:1;transform:none}
.s-hold.open .hold-after{opacity:1;transform:none}
.link-arrow{color:var(--accent);text-decoration:none;border-bottom:1px solid var(--accent-muted);padding-bottom:2px;transition:border-color .25s var(--ease)}
.link-arrow:hover{border-color:var(--accent)}

/* ── 4. Offers: two asymmetric offset panels ───────────────────────── */
.offers{display:grid;grid-template-columns:1fr;gap:clamp(24px,3vw,36px);margin-top:44px}
@media (min-width:900px){
  .offers{grid-template-columns:1fr 1fr;align-items:start}
  .offer-b{margin-top:76px}
}
.offer{position:relative;background:var(--panel);border:1px solid var(--line);padding:clamp(28px,3.4vw,42px)}
.offer-b{background:transparent;border-color:var(--line-strong)}
.offer-n{font-family:var(--mono);font-size:.78rem;letter-spacing:.16em;color:var(--text-dim)}
.offer h3{margin-top:14px;font-size:clamp(1.35rem,2.4vw,1.85rem);max-width:16ch}
.offer-sub{margin-top:8px;font-family:var(--mono);font-size:.76rem;letter-spacing:.14em;text-transform:uppercase;color:var(--accent)}
.offer>p:not(.offer-n):not(.offer-sub){margin-top:18px;color:var(--text-secondary);max-width:44ch}
.offer ul{margin-top:22px;display:flex;flex-direction:column;gap:12px}
.offer li{position:relative;padding-left:24px;color:var(--text-secondary);font-size:.96rem;max-width:42ch}
.offer li::before{content:"";position:absolute;left:0;top:.62em;width:11px;height:1px;background:var(--accent)}

/* ── 5. Terms: stacked full-width rows, reads like a contract ──────── */
.terms{margin-top:40px;border-top:1px solid var(--line)}
.term{
  display:grid;grid-template-columns:1fr;gap:8px;
  padding:clamp(24px,3.4vh,36px) 0;border-bottom:1px solid var(--line);
}
@media (min-width:860px){.term{grid-template-columns:.9fr 1.1fr;gap:40px;align-items:baseline}}
.term dt{font-family:var(--display);font-weight:600;font-size:clamp(1.15rem,2.1vw,1.6rem);letter-spacing:-.015em}
.term-n{font-family:var(--mono);font-size:.72rem;letter-spacing:.14em;color:var(--accent);vertical-align:middle;margin-right:12px}
.term dd{color:var(--text-secondary);max-width:52ch}

/* ── 6. Who: portrait beside text, off the page's usual grid ───────── */
.s-who{display:grid;grid-template-columns:1fr;gap:clamp(30px,4vw,58px);align-items:center}
@media (min-width:860px){.s-who{grid-template-columns:.62fr 1fr}}
.who-portrait{position:relative;max-width:340px}
.who-photo-crop{position:relative;aspect-ratio:4/5;overflow:hidden;background:var(--panel)}
.who-photo-crop img{width:100%;height:100%;object-fit:cover;object-position:45% 18%;transform:scale(1.52);transform-origin:45% 24%;filter:saturate(.72) contrast(1.06) brightness(.94)}
/* The threshold motif around the portrait: two jambs and a lintel, open at the
   foot. Drawn in CSS so the jambs land outside the photo edge rather than
   behind it, which is what an SVG scaled to the same box does. */
.who-frame{
  position:absolute;left:-18px;right:-18px;top:-18px;bottom:0;z-index:2;
  border:2px solid var(--accent);border-bottom:0;opacity:.5;pointer-events:none;
}
.who-text h2{max-width:16ch}
.who-text>p:not(.kicker):not(.who-work){margin-top:20px;color:var(--text-secondary);max-width:48ch}
.who-work{margin-top:26px;font-family:var(--mono);font-size:.8rem;letter-spacing:.06em;color:var(--text-dim)}
.who-work span{display:inline-block;margin-right:14px;color:var(--text-primary);border-bottom:1px solid var(--accent-muted);padding-bottom:2px}

/* ── 7. FAQ: two columns of question and answer ────────────────────── */
.faq{margin-top:40px;display:grid;grid-template-columns:1fr;gap:clamp(22px,2.6vw,34px)}
@media (min-width:900px){.faq{grid-template-columns:1fr 1fr;gap:clamp(30px,3.4vw,48px)}}
.qa{border-top:1px solid var(--line);padding-top:20px}
.qa dt{font-family:var(--display);font-weight:600;font-size:1.14rem;letter-spacing:-.01em}
.qa dd{margin-top:10px;color:var(--text-secondary);font-size:.97rem;max-width:48ch}

/* ── 8. The call and the form ──────────────────────────────────────── */
.s-talk{max-width:900px}
.talk-lede{margin-top:18px;color:var(--text-secondary);font-size:1.08rem;max-width:52ch}

/* The HighLevel iframe. The panel is ours; everything inside the frame is
   styled in HighLevel's own form builder — see README.md for the token values
   to mirror there, because CSS here cannot cross the iframe boundary. */
.ghl{
  --ghl-pad:clamp(18px,2.4vw,28px);
  margin-top:42px;
  background:var(--panel);border:1px solid var(--line);
  padding:var(--ghl-pad);
  min-height:200px;
}
.ghl-frame iframe{display:block;width:100%;border:0}

/* Reserve the frame's height before it arrives so the sections below do not
   jump when it loads. */
.ghl[data-state="loading"] .ghl-frame{min-height:640px}
.ghl[data-state="unconfigured"],.ghl[data-state="failed"]{border-color:var(--line-strong)}

/* The status paragraph follows the frame in the DOM, which is right for the
   failure states — the box has collapsed by then and the message reads at the
   top. While loading it is wrong: the frame is holding 640px open, so the
   message would sit at the bottom of an empty rectangle instead of where the
   eye already is. Lift it for that one state only, so the ready layout is
   untouched. */
.ghl[data-state="loading"]{position:relative}
.ghl[data-state="loading"] .ghl-status{
  position:absolute;top:var(--ghl-pad);left:var(--ghl-pad);right:var(--ghl-pad);margin:0;
}

.ghl-status{
  font-family:var(--mono);font-size:.86rem;line-height:1.6;
  color:var(--text-secondary);margin:0;
}
.ghl[data-state="failed"] .ghl-status,
.ghl[data-state="unconfigured"] .ghl-status{color:var(--accent)}

.ghl-direct{margin-top:26px;display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 16px}
.ghl-direct-lead{
  font-family:var(--mono);font-size:.74rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--text-secondary);margin:0;
}
.ghl-direct-links{margin:0;color:var(--text-dim);font-size:.97rem}
.ghl-direct-links a{color:var(--text-primary);text-decoration:none;border-bottom:1px solid var(--line-interactive);padding-bottom:1px}
.ghl-direct-links a:hover{color:var(--accent);border-bottom-color:var(--accent)}
.ghl-direct-links a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.ghl-direct-links span{margin:0 4px;color:var(--text-dim)}

.ghl-legal{margin:18px 0 0;max-width:68ch;color:var(--text-secondary);font-size:.86rem;line-height:1.6}
.ghl-legal a{color:var(--text-primary);border-bottom:1px solid var(--line-interactive);text-decoration:none;padding-bottom:1px}
.ghl-legal a:hover{color:var(--accent);border-bottom-color:var(--accent)}
.ghl-legal a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.ghl-legal-links{display:block;margin-top:8px}
.ghl-legal-links span{margin:0 8px;color:var(--text-dim)}

.ghl-noscript{margin-top:20px;color:var(--text-secondary);font-size:.97rem}
.ghl-noscript a{color:var(--accent)}

/* ── Footer ────────────────────────────────────────────────────────── */
.foot{
  position:relative;z-index:1;max-width:var(--maxw);margin:0 auto;
  padding:clamp(46px,7vh,80px) var(--gut) clamp(40px,6vh,64px);
  border-top:1px solid var(--line);
  display:grid;grid-template-columns:auto 1fr;gap:clamp(24px,4vw,60px);align-items:start;
}
.foot-mark svg{width:26px;height:26px;color:var(--accent);opacity:.85}
.foot-cols{display:grid;grid-template-columns:1fr;gap:18px;color:var(--text-secondary);font-size:.92rem}
@media (min-width:760px){.foot-cols{grid-template-columns:repeat(4,1fr)}}
.foot-legal{display:flex;flex-direction:column;gap:6px}
.foot-legal a{color:var(--text-secondary);text-decoration:none;transition:color .22s var(--ease)}
.foot-legal a:hover{color:var(--accent)}
.foot-legal a:focus-visible,.foot-cols a:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
.foot-cols strong{color:var(--text-primary);font-weight:600}
.foot-cols a{color:var(--text-secondary);text-decoration:none;transition:color .22s var(--ease)}
.foot-cols a:hover{color:var(--accent)}
.foot-note{color:var(--text-dim);font-size:.84rem}

/* Touch targets. Inline links measure 19-23px tall, which is comfortable
   with a cursor and not with a thumb. Widen the tap area only where the
   pointer is coarse so the desktop layout keeps its rhythm — padding on the
   block axis rather than a fixed height, so the wrapped email/phone pair in
   the footer still breaks where it did. */
@media (pointer:coarse){
  .foot-cols a,.foot-legal a,.ghl-direct-links a,.stat-source a{
    display:inline-flex;align-items:center;min-height:44px;
  }
  .nav-cta{min-height:44px;display:inline-flex;align-items:center}
  /* Back-to-top, and the only other thing in the bar. 32px with a thumb. */
  .nav-mark{min-height:44px}
}

/* ── Off-screen and hidden-tab animation pausing ───────────────────── */
body.paused *,body.paused *::before,body.paused *::after{animation-play-state:paused!important}

/* ── Reduced motion, honored completely ────────────────────────────── */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;transition-delay:0s!important;
  }
  .reveal>*{opacity:1;transform:none}
  .jamb-path{stroke-dashoffset:0}
  .outcomes li,.hold-after{opacity:1;transform:none}
  .env-glow,.env-dust{animation:none}
  /* positional transforms are re-declared, never blanket-removed */
  .cue{display:none}
}

/* Pinned final states, applied by JS when reduced motion flips on live */
.pinned .reveal>*{opacity:1!important;transform:none!important}
.pinned .jamb-path{stroke-dashoffset:0!important;transition:none}
.pinned .outcomes li,.pinned .hold-after{opacity:1!important;transform:none!important}
