/* ============================================================
   LAB913 PORTFOLIO: DESIGN SYSTEM
   Implements LAB913 Portfolio Design System Contract v1.
   Semantic tokens only. Components never hold raw values.
   Documented in DESIGN-SYSTEM.md.
   ============================================================ */

/* ---------- 1. FOUNDATION: semantic color, light theme -------- */
:root{
  --page:#EEF0F3;
  --surface:#FFFFFF;
  --surface-raised:#F6F8FA;
  --surface-sunken:#E6E9EE;

  --text:#0F1216;
  --text-muted:#3B414A;
  /* Every text token in both themes is measured, not eyeballed. An earlier
     value shipped at 3.38:1 and failed AA across roughly twenty five usages. */
  --text-subtle:#5B616B;
  --text-on-accent:#FFFFFF;

  --border:#D8DCE2;
  --border-strong:#C3C9D2;
  --divider:#E3E6EB;

  --focus:#1F5FBF;
  --overlay:rgba(8,9,11,.66);

  /* Serif display type at this size reads as flat, printed black rather than
     ink if it uses the same near-black as body text. A touch of the page
     colour mixed in softens it to a warm charcoal instead, the way headline
     type actually looks on paper. Dark mode does not have this problem: the
     heading is already near-white there, so it stays tied to --text. */
  --text-display: color-mix(in oklab, var(--text) 84%, var(--page));

  --status-live:#2F6F4F;
  --status-warn:#8A6A1F;
  --status-error:#A3301F;
  --disabled:#9AA0A9;

  /* System accent. Cyan in dark, a deeper blue in light so it still passes on
     a near-white page. Project accents override this per route. */
  --accent:#1A6E86;
  --accent-strong:#12586C;
  --accent-soft:rgba(26,110,134,.10);
  /* Accent as TEXT is mixed at the point of use, never stored as a token.
     A color-mix() declared here would resolve against the :root accent and
     inherit as a fixed colour, so the per-project accent set on the card would
     be ignored. Measured and confirmed: every card came out identical. */

  /* ---------- 2. TYPOGRAPHY ROLES ---------- */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  /* Display face. Loaded from Google Fonts with a serif fallback, so a blocked
     or failed font request degrades to Georgia rather than to the body face. */
  --font-display: "Instrument Serif", Georgia, "Times New Roman", serif;

  --type-display: 400 clamp(46px, 8vw, 96px)/0.98 var(--font-display);
  --type-section: 400 clamp(32px, 4.6vw, 54px)/1.05 var(--font-display);
  --type-project: 700 clamp(21px, 2.3vw, 30px)/1.12 var(--font);
  --type-lede:    400 clamp(17px, 1.6vw, 21px)/1.5 var(--font);
  --type-body:    400 17px/1.62 var(--font);
  --type-meta:    600 13px/1.4 var(--font);
  --type-caption: 400 13px/1.5 var(--font);
  --type-label:   500 11px/1 var(--font-mono);
  --type-button:  700 15px/1 var(--font);

  --track-display:-.035em;
  --track-section:-.03em;
  --track-label:.14em;

  /* ---------- 3. SPACING SCALE ---------- */
  --s1:4px;  --s2:8px;  --s3:12px; --s4:16px; --s5:24px;
  --s6:32px; --s7:48px; --s8:64px; --s9:96px; --s10:128px;
  --section-y:clamp(72px,10vw,144px);

  /* ---------- 4. WIDTHS + GRID ---------- */
  --w-page:1240px;
  --w-text:70ch;
  --w-narrow:640px;
  --gutter:clamp(20px,5vw,56px);

  /* ---------- 5. RADIUS, BORDER, ELEVATION, Z ---------- */
  --r-sm:8px; --r-md:14px; --r-lg:22px; --r-pill:999px;
  --border-w:1px;
  --elev-1:0 2px 10px rgba(22,21,15,.06);
  --elev-2:0 18px 48px -22px rgba(22,21,15,.34);
  --z-nav:100; --z-overlay:200; --z-modal:210;

  /* ---------- 6. MOTION ---------- */
  --dur-micro:140ms;
  --dur-enter:280ms;
  --dur-move:420ms;
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  /* Panel travel between projects. Tuned against the browser's own scroll-snap
     animation, which these run alongside: a cubic ease-out tracks the scroller
     closely, where an expo-out arrives long before it and then waits. */
  --dur-panel:520ms;
  --ease-panel:cubic-bezier(.33,1,.68,1);
}

/* ---------- LIGHT/DARK MAPPING ---------- */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --page:#08090B;
    --surface:#0D0F12;
    --surface-raised:#12151A;
    --surface-sunken:#0A0C0F;
    --text:#F4F5F7;
    --text-muted:#C6CAD1;
    --text-subtle:#8B909A;
    --text-display:#F4F5F7;
    --border:#1C1F24;
    --border-strong:#2A2D33;
    --divider:#191C21;
    --focus:#7FB0FF;
    --overlay:rgba(0,0,0,.78);
    --status-live:#5FA97F;
    --status-warn:#C9A24F;
    --status-error:#D9614E;
    --disabled:#4A4F57;
    --accent:#6FD4EC;
    --accent-strong:#9AE3F4;
    --accent-soft:rgba(111,212,236,.12);
    --elev-1:0 2px 10px rgba(0,0,0,.4);
    --elev-2:0 18px 48px -22px rgba(0,0,0,.8);
  }
}
:root[data-theme="dark"]{
  --page:#08090B;
  --surface:#0D0F12;
  --surface-raised:#12151A;
  --surface-sunken:#0A0C0F;
  --text:#F4F5F7;
  --text-muted:#C6CAD1;
  --text-subtle:#8B909A;
  --text-display:#F4F5F7;
  --border:#1C1F24;
  --border-strong:#2A2D33;
  --divider:#191C21;
  --focus:#7FB0FF;
  --overlay:rgba(0,0,0,.78);
  --status-live:#5FA97F;
  --status-warn:#C9A24F;
  --status-error:#D9614E;
  --disabled:#4A4F57;
  --accent:#6FD4EC;
  --accent-strong:#9AE3F4;
  --accent-soft:rgba(111,212,236,.12);
  --elev-1:0 2px 10px rgba(0,0,0,.4);
  --elev-2:0 18px 48px -22px rgba(0,0,0,.8);
}

/* ============================================================
   BASE
   ============================================================ */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;background:var(--page);color:var(--text);
  font:var(--type-body);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
img,svg,video{display:block;max-width:100%}
a{color:inherit}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
h1,h2,h3,h4{margin:0;text-wrap:balance}
p{margin:0}

:focus-visible{outline:2.5px solid var(--focus);outline-offset:3px;border-radius:var(--r-sm)}

.wrap{max-width:var(--w-page);margin-inline:auto;padding-inline:var(--gutter)}
.sr{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:absolute;left:-9999px;top:0;z-index:var(--z-modal);
      background:var(--text);color:var(--page);padding:var(--s3) var(--s5)}
.skip:focus{left:0}

/* ============================================================
   TYPE ROLES
   ============================================================ */
/* The display face is a serif at regular weight, so it needs far less optical
   correction than the heavy sans it replaced. */
.t-display{font:var(--type-display);letter-spacing:-.015em;color:var(--text-display)}
.t-section{font:var(--type-section);letter-spacing:-.012em;color:var(--text-display)}
.t-project{font:var(--type-project);letter-spacing:-.02em}
.t-lede{font:var(--type-lede);color:var(--text-muted);max-width:var(--w-text);text-wrap:pretty}
.t-caption{font:var(--type-caption);color:var(--text-subtle)}
.t-label{font:var(--type-label);letter-spacing:var(--track-label);text-transform:uppercase;
         color:var(--text-muted)}

/* ============================================================
   COMPONENT: global navigation + mobile nav + theme toggle
   ============================================================ */
.nav{position:sticky;top:0;z-index:var(--z-nav);
     background:color-mix(in srgb,var(--page) 84%,transparent);
     backdrop-filter:saturate(180%) blur(20px);
     border-bottom:var(--border-w) solid transparent;
     transition:border-color var(--dur-micro) var(--ease)}
.nav[data-stuck="true"]{border-bottom-color:var(--divider)}
.nav-in{max-width:var(--w-page);margin-inline:auto;padding:var(--s4) var(--gutter);
        display:flex;align-items:center;justify-content:space-between;gap:var(--s5)}
.brand{display:inline-flex;align-items:center;gap:var(--s3);text-decoration:none;
       font:700 15px/1 var(--font);letter-spacing:-.01em}
.brand-mark{width:22px;height:22px;border-radius:var(--r-sm);background:var(--text);
            color:var(--page);display:grid;place-items:center;font:800 10px/1 var(--font);
            letter-spacing:0;flex:none}
.nav-links{display:flex;align-items:center;gap:var(--s6)}
.nav-links a{font:var(--type-meta);color:var(--text-muted);text-decoration:none;
             transition:color var(--dur-micro) var(--ease)}
.nav-links a:hover{color:var(--text)}
@media (max-width:760px){.nav-links a{display:none}}

.theme-toggle{display:inline-flex;gap:2px;padding:3px;border-radius:var(--r-pill);
              border:var(--border-w) solid var(--border);background:var(--surface);flex:none}
.theme-toggle button{width:32px;height:28px;border-radius:var(--r-pill);color:var(--text-subtle);
                     display:grid;place-items:center;
                     transition:background var(--dur-micro) var(--ease),color var(--dur-micro) var(--ease)}
.theme-toggle button:hover{color:var(--text)}
.theme-toggle button[aria-pressed="true"]{background:var(--text);color:var(--page)}
.theme-toggle svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.9;
                  stroke-linecap:round;stroke-linejoin:round}

/* ============================================================
   COMPONENT: project stage label
   Text + glyph + accessible description. Never color alone.
   ============================================================ */
.stage{display:inline-flex;align-items:center;gap:var(--s2);
       padding:5px 11px 5px 9px;border-radius:var(--r-pill);
       border:var(--border-w) solid var(--border);background:var(--surface);
       font:var(--type-label);letter-spacing:.1em;text-transform:uppercase;
       color:var(--text-muted);white-space:nowrap}
.stage-glyph{font-size:9px;line-height:1}
.stage[data-stage="complete"]{color:var(--text);border-color:var(--border-strong)}
.stage[data-stage="production"]{color:var(--text);border-color:var(--border-strong);
                                background:var(--accent-soft)}
.stage[data-stage="development"]{color:var(--text-subtle)}

/* On a colour field the chip cannot keep using the theme's surface. --surface
   is not part of the field ink, so in light mode the pill stayed white while
   its text went white with everything around it, and the label disappeared
   entirely. In dark mode it survived, but as a near-black lozenge dropped onto
   a saturated field.

   Stage is carried by its glyph and its words, never by colour, so giving all
   three variants one translucent treatment here loses no meaning. It is the
   background that has to adapt, not the label.

   Both values were measured against all six fields and set to the worst case,
   not to the one that looked fine: the border clears 3:1 for a component
   boundary on every field, and the label clears 10:1 on its own background. */
#work .stage,
.case-hero.has-field .stage{
  background:rgba(0,0,0,.30);
  border-color:rgba(255,255,255,.55);
  color:#FFFFFF}

/* ============================================================
   COMPONENT: section heading
   ============================================================ */
.section{padding-block:var(--section-y)}
.section-head{display:flex;flex-direction:column;gap:var(--s4);margin-bottom:var(--s8)}
.rule{height:1px;background:var(--divider);border:0;margin:0}

/* Six compact proofs under the hero. This is an index, not a second gallery:
   enough signal to choose a path, with the detailed work waiting below. */
.systems-standard{min-height:auto;display:block}
.systems-standard-head{margin-bottom:var(--s6)}
.systems-strip{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));border-block:var(--border-w) solid var(--border)}
.systems-strip a{display:flex;flex-direction:column;gap:var(--s3);min-height:180px;padding:var(--s5) var(--s4);text-decoration:none;border-right:var(--border-w) solid var(--border);transition:background var(--dur-enter) var(--ease),color var(--dur-enter) var(--ease)}
.systems-strip a:first-child{padding-left:0}
.systems-strip a:last-child{border-right:0;padding-right:0}
.systems-strip a:hover{background:var(--surface)}
.systems-num{font:var(--type-label);letter-spacing:var(--track-label);color:var(--text-subtle)}
.systems-name{font:var(--type-meta);color:var(--text)}
.systems-proof{font:var(--type-caption);color:var(--text-muted);text-wrap:pretty}
@media (max-width:980px){.systems-strip{grid-template-columns:repeat(3,minmax(0,1fr))}.systems-strip a:nth-child(3){border-right:0;padding-right:0}.systems-strip a:nth-child(4){padding-left:0;border-top:var(--border-w) solid var(--border)}.systems-strip a:nth-child(n+4){border-top:var(--border-w) solid var(--border)}}
@media (max-width:640px){.systems-strip{grid-template-columns:1fr}.systems-strip a,.systems-strip a:first-child,.systems-strip a:last-child{min-height:0;padding:var(--s4) 0;border-right:0;border-top:var(--border-w) solid var(--border)}.systems-strip a:first-child{border-top:0}.systems-proof{max-width:46ch}}

/* ============================================================
   COMPONENT: selected-work gallery + project card
   ============================================================ */
.gallery{display:grid;gap:var(--s6);grid-template-columns:repeat(2,minmax(0,1fr))}
@media (max-width:820px){.gallery{grid-template-columns:1fr}}
.gallery > .card:nth-child(1),
.gallery > .card:nth-child(2){grid-column:span 1}

.card{display:flex;flex-direction:column;gap:var(--s4);text-decoration:none;
      border-radius:var(--r-lg);padding:var(--s5);margin:calc(var(--s5) * -1);
      transition:background var(--dur-enter) var(--ease)}
.card:hover{background:var(--surface)}

.card-figure{position:relative;aspect-ratio:16/10;overflow:hidden;border-radius:var(--r-md);
             border:var(--border-w) solid var(--border);background:var(--surface-sunken);
             display:grid;place-items:center}
.card-figure::after{content:"";position:absolute;inset:0;border-radius:inherit;
                    box-shadow:inset 0 0 0 1px rgba(0,0,0,.04);pointer-events:none}
.card:hover .card-figure{border-color:var(--border-strong)}

/* Project counter. Mono, quiet, and the only numeral on the card, so it reads
   as an index rather than as data. */
.card-num{font:var(--type-label);letter-spacing:var(--track-label);
          color:var(--text-subtle);font-variant-numeric:tabular-nums}
.card:hover .card-num{color:var(--text-muted)}

/* A coded concept mark stands in for a project with no approved screens.
   Neutral by design: it must read as a diagram, never as an interface. */
/* Pinned to the figure rather than sized inside it. Grid centring let the mark
   grow past the frame, which pushed the disclosure caption under the clip and
   hid it. The caption is the honest part, so it must never be the thing that
   overflows. */
.mark{position:absolute;inset:0;display:grid;place-items:center;
      padding:var(--s5) var(--s5) var(--s7);color:var(--text-muted)}
.mark svg{width:100%;height:100%;min-height:0;overflow:visible}
.mark-cap{position:absolute;left:var(--s5);bottom:var(--s4);
          font:var(--type-label);letter-spacing:var(--track-label);
          text-transform:uppercase;color:var(--text-muted)}
/* Never hidden, at any width. An earlier version dropped it below 520px, which
   removed the disclosure from exactly the devices most people browse on. It
   gets smaller and tighter instead. */
@media (max-width:520px){
  .mark-cap{left:var(--s4);bottom:var(--s3);font-size:9px;letter-spacing:.1em}
  .mark{padding:var(--s4) var(--s4) var(--s6)}
}

.card-meta{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap}
.card-client{font:var(--type-meta);color:var(--text-muted)}
.card-title{font:var(--type-project);letter-spacing:-.02em}
/* Project accents are identity colours chosen for character, not legibility.
   Measured raw against the page they ranged from 2.84 to 6.14, so several
   failed even the 3:1 large-text floor. Mixing toward the text colour keeps the
   identity while guaranteeing headroom, and it flips correctly per theme. */
.card:hover .card-title{color:color-mix(in oklab, var(--accent) 62%, var(--text))}
.card-teaser{font:var(--type-body);color:var(--text-muted);max-width:46ch}
.card-cue{font:var(--type-meta);color:var(--text-subtle);display:inline-flex;gap:var(--s2);
          align-items:center;transition:gap var(--dur-micro) var(--ease)}
.card:hover .card-cue{gap:var(--s3);color:var(--text)}

/* ============================================================
   MOTION SYSTEM
   ------------------------------------------------------------
   One easing curve for everything that settles, so the whole page
   moves with a single hand. Transform and opacity only, which the
   compositor can run without touching layout.

   Durations are deliberately short. The reference this was drawn
   from spends about three seconds before content is readable. A
   hiring manager opening a link between meetings is not that
   visitor, so nothing here delays the headline.
   ============================================================ */
:root{
  --ease-settle:cubic-bezier(.16,1,.3,1);
  --dur-wash:640ms;   /* background moving between projects */
  --dur-settle:520ms; /* things arriving */
}

/* Cross-document transitions. The project image persists across the
   navigation into its case study instead of the page blinking. Chrome
   honours it; everywhere else this line is inert and the link behaves
   like an ordinary link. */
@view-transition{ navigation:auto }

/* The page takes a wash of the active project's colour. Subtle enough
   that type contrast is unaffected, which is measured, not assumed. */
html{background:var(--stage,var(--page))}
body{background:var(--stage,var(--page));
     transition:background var(--dur-wash) var(--ease-settle)}

/* The work section is a colour field. It bleeds the full width regardless of
   the page gutter, and carries its own ink, so it reads as a place rather than
   as a tinted region of the page. */
/* `color` has to be re-declared here, not just the --text token underneath it.
   Inherited colour is an already-computed value: body resolves var(--text) once
   at the root, and every descendant inherits that result. Overriding --text on
   this section therefore reached only the elements that re-read the token, and
   the project headline, which simply inherits, kept the light theme's near-black
   and sat at 1.93:1 on a saturated field. Re-reading it here puts the whole
   section back on one ink. */
#work{position:relative;isolation:isolate;color:var(--text)}
/* Full bleed without a box-shadow trick. A shadow spread paints outside the
   clip and left two colours fighting at the gutters. */
#work::before{content:"";position:absolute;z-index:-1;
              inset:0 calc(50% - 50vw);
              background-color:var(--stage,transparent);
              transition:background-color var(--dur-wash) var(--ease-settle)}

/* The scroll cue. Present because it costs nothing, quiet because anyone
   reading this site can work out that pages scroll. */
.cue{display:flex;align-items:center;gap:var(--s3);margin-top:var(--s6);
     font:var(--type-label);letter-spacing:var(--track-label);
     text-transform:uppercase;color:var(--text-subtle)}
.cue i{font-style:normal;animation:cue-drift 2.6s var(--ease-settle) infinite}
@keyframes cue-drift{0%,100%{translate:0 0}50%{translate:0 3px}}
@media (prefers-reduced-motion:reduce){ .cue i{animation:none} }

/* ---------- Locked panels ----------
   One project fills the frame at a time. `scroll-snap-stop:always` is what
   makes it advance one at a time rather than letting a hard flick skip three.

   The motion is positional, not directional: panels before the active one sit
   to the left, panels after it sit to the right. Scrolling down therefore
   carries the current project off to the left while the next arrives from the
   right, and scrolling up is the same thing played backwards without any
   direction tracking to jitter when you change your mind mid-scroll. */
html{scroll-snap-type:y mandatory}

/* Every full-frame section is a snap target. This is not decorative: with
   mandatory snapping, a section that is NOT a target gets skipped past on load
   and on every scroll, because the browser must land on something. The hero
   was missing its alignment, so refreshing the page jumped straight to the
   first project and the hero was unreachable. */
.stage-lock,.panel{scroll-snap-align:center;scroll-snap-stop:always;
                   min-height:min(86svh,860px);
                   display:grid;align-content:center}
.panel{align-content:normal}

/* The section head introducing the work is its own beat rather than something
   glimpsed on the way past. */
#work > .section-head{scroll-snap-align:center;scroll-snap-stop:always;
                      min-height:min(60svh,600px);
                      display:grid;align-content:center}

/* The whole panel moves as one object. An earlier version animated the copy and
   the image separately with a 70ms offset, which read as the text shooting out
   from behind the picture rather than as a block arriving. Two elements moving
   in loose formation always looks like a mistake; one element moving looks
   deliberate.

   Translate and opacity run on ONE duration and ONE curve. Splitting them
   (420ms slide against a 240ms fade) meant the panel finished disappearing
   while it was still travelling, so the eye saw it vanish mid-move rather than
   leave. Matched, it reads as a single object crossing the frame.

   The curve matters as much as the duration. An expo-out shoots almost the
   whole distance in the first eighth of the time and then crawls, which fights
   the browser's own snap animation running alongside it. A cubic ease-out is
   closer to how the scroller itself moves, so the two read as one motion
   instead of two. Travel is a little shorter for the same reason: less ground
   to cover in the same time is calmer. */
.panel{transition:translate var(--dur-panel) var(--ease-panel),
                  opacity   var(--dur-panel) var(--ease-panel);
       will-change:translate,opacity}

.panel[data-pos="before"]{translate:-9vw 0;opacity:0}
.panel[data-pos="after"] {translate: 9vw 0;opacity:0}
.panel[data-pos="active"]{translate:0 0;opacity:1}

/* Snapping traps anyone who scrolls in small increments, and the sliding is
   the whole point of the effect, so both go together. */
@media (prefers-reduced-motion:reduce){
  html{scroll-snap-type:none}
  .panel{translate:0 0!important;opacity:1!important;transition:none}
}
/* On a phone the panels stack and the horizontal movement has nowhere to go. */
@media (max-width:900px){
  html{scroll-snap-type:none}
  .panel{translate:0 0!important;opacity:1!important}
}

/* ---------- Project rail ---------- */
.rail{position:fixed;right:clamp(12px,1.8vw,26px);top:50%;translate:0 -50%;
      z-index:var(--z-nav);display:flex;flex-direction:column;gap:11px}
.rail a{width:9px;height:9px;border-radius:50%;background:var(--text);
        opacity:.28;display:block;
        transition:opacity var(--dur-micro) var(--ease-settle),
                   scale var(--dur-micro) var(--ease-settle)}
.rail a:hover{opacity:.6}
.rail a[aria-current="true"]{opacity:1;scale:1.35}
@media (max-width:900px){ .rail{display:none} }

/* ---------- Hero, with one rotating real project ---------- */
.hero{display:grid;align-items:center;gap:clamp(32px,5vw,84px);
      grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr)}
.hero-copy{min-width:0}

.hero-feature{position:relative}
.hero-feature-link{display:flex;flex-direction:column;gap:var(--s3);
                   text-decoration:none;color:inherit}
.hero-feature-media{position:relative;display:grid;place-items:center;
                    aspect-ratio:4/3;overflow:hidden;border-radius:var(--r-lg);
                    border:var(--border-w) solid var(--border);
                    background:var(--surface-sunken);
                    transition:border-color var(--dur-enter) var(--ease)}
.hero-feature-link:hover .hero-feature-media{border-color:var(--border-strong)}
.hero-feature-media .slot-img{border-radius:inherit}
.hero-feature-open{align-self:center;display:inline-flex;align-items:center;gap:var(--s2);white-space:nowrap;
                   font:var(--type-meta);color:var(--text-muted);
                   transition:color var(--dur-micro) var(--ease),translate var(--dur-micro) var(--ease)}
.hero-feature-open i{font-style:normal;transition:translate var(--dur-micro) var(--ease)}
.hero-feature-link:hover .hero-feature-open{color:var(--text);translate:0 -2px}
.hero-feature-link:hover .hero-feature-open i{translate:3px 0}

.hero-feature-meta{display:flex;align-items:center;gap:var(--s3);
                   font:var(--type-label);letter-spacing:var(--track-label);
                   text-transform:uppercase}
.hero-feature-num{color:var(--text);font-variant-numeric:tabular-nums}
.hero-feature-num i{font-style:normal;color:var(--text-subtle);padding:0 2px}
.hero-feature-client{color:var(--text-muted);flex:1;min-width:0;
                     overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hero-feature-cue{color:var(--text-subtle);
                  transition:transform var(--dur-micro) var(--ease),color var(--dur-micro) var(--ease)}
.hero-feature-link:hover .hero-feature-cue{color:var(--text);transform:translateX(3px)}

/* Below the two-column threshold the hero reads better as pure type. The
   rotating project is a bonus, not the argument, and on a phone it would push
   the headline and the call to action below the fold. */
@media (max-width:900px){
  .hero{grid-template-columns:1fr}
  .hero-feature{display:none}
}

/* ---------- Domains, and the practice list ---------- */
.domains{margin-top:var(--s7);padding-top:var(--s5);
         border-top:var(--border-w) solid var(--divider)}
.domain-list{list-style:none;margin:var(--s4) 0 0;padding:0;
             display:flex;flex-wrap:wrap;gap:var(--s3)}
.domain-list li{font:var(--type-meta);color:var(--text-muted);
                border:var(--border-w) solid var(--border);border-radius:var(--r-pill);
                padding:8px 14px}

.practice{list-style:none;margin:0;padding:0;display:grid;gap:var(--s6);
          grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.practice li{display:flex;flex-direction:column;gap:var(--s3);
             padding-top:var(--s4);border-top:2px solid var(--border-strong)}
.practice-num{font:var(--type-label);letter-spacing:var(--track-label);
              color:var(--text-subtle);font-variant-numeric:tabular-nums}
.practice h3{margin:0;font:600 19px/1.3 var(--font);letter-spacing:-.01em}
.practice p{margin:0;font:var(--type-caption);line-height:1.6;color:var(--text-muted)}

/* ============================================================
   COMPONENT: project panel
   The homepage gives each project a full band rather than a card in a
   grid, so the work is read one at a time. Media and text alternate
   sides so the eye has to reset, which slows the scroll down enough
   for the headline to land.
   ============================================================ */
.panels{display:flex;flex-direction:column;gap:0}

/* Each project owns a screen. Not quite the full viewport: leaving a
   sliver visible is what tells someone there is more below, and it
   avoids the trapped feeling of an exact 100vh block. */
/* Equal columns, deliberately. An earlier 1.08 / 0.92 split gave the copy more
   room, but the media alternates sides and the columns did not, so the image
   was 569px wide on one panel and 485px on the next. Alternating sides is meant
   to read as a mirror; a different size each time reads as a mistake. Equal
   columns make the alternation the only thing that changes. */
.panel{display:grid;align-items:center;
       min-height:min(86svh,860px);
       padding-block:clamp(40px,6vw,88px);
       grid-template-columns:repeat(2,minmax(0,1fr));
       gap:clamp(28px,4.4vw,76px)}
.panel:nth-child(even) .panel-media-wrap{grid-column:2;grid-row:1}
.panel:nth-child(even) .panel-body {grid-column:1;grid-row:1}

/* The image is the hover trigger, not the headline. Everything below reads
   from one gesture: the picture lifts off the field with a soft shadow, and
   its own caption brightens and nudges forward. The headline no longer
   changes colour on hover at all, because shifting it toward the project's
   accent hue was the actual flaw a hover was supposed to signal, not fix: on
   a saturated colour field, a hue-matched headline reads as blending into
   the background rather than as an interactive state. */
/* The media and its cue are one link. It is a real anchor rather than an
   overlay, so its bounds are exactly what it looks like: the picture and the
   words under it, and nothing of the prose alongside. */
/* align-self:center, not stretch and not start. Stretch would grow the anchor
   to the full row height and quietly hand back the oversized click target this
   markup exists to remove; start left the picture riding high against copy that
   is centred. Centred keeps the anchor exactly as tall as the picture and its
   cue, and puts both columns on the same axis. */
.panel-media-wrap{display:flex;flex-direction:column;gap:var(--s4);min-width:0;
                  text-decoration:none;color:inherit;
                  align-self:center;border-radius:var(--r-lg)}
.panel-media-wrap:focus-visible{outline:2.5px solid var(--focus);outline-offset:10px}
.panel-media{position:relative;aspect-ratio:16/10;overflow:hidden;
             border-radius:var(--r-lg);border:var(--border-w) solid var(--border);
             background:var(--surface-sunken);display:grid;place-items:center;
             transition:border-color var(--dur-enter) var(--ease),
                        transform var(--dur-enter) var(--ease-out),
                        box-shadow var(--dur-enter) var(--ease-out)}
.panel-media-wrap:hover .panel-media,
.panel-media-wrap:focus-visible .panel-media{border-color:var(--border-strong);
                          transform:translateY(-8px);box-shadow:var(--elev-2)}
.panel-media .slot-img{border-radius:inherit}

/* The cue carries the headline's size and weight. At meta size it was a
   footnote under a full-frame image and did not read as the way in. At project
   size it is the second thing on the panel after the headline itself, which is
   what it is. */
.panel-cue{display:inline-flex;align-items:center;gap:var(--s3);margin:0;
           font:var(--type-project);letter-spacing:-.015em;
           color:var(--text-muted);
           transition:color var(--dur-micro) var(--ease)}
.panel-cue-arrow{display:inline-block;line-height:1;
                 transition:translate 220ms var(--ease-panel)}
.panel-media-wrap:hover .panel-cue,
.panel-media-wrap:focus-visible .panel-cue{color:var(--text)}
.panel-media-wrap:hover .panel-cue-arrow,
.panel-media-wrap:focus-visible .panel-cue-arrow{translate:8px 0}

.panel-body{display:flex;flex-direction:column;gap:var(--s4);min-width:0}

.panel-meta{display:flex;align-items:center;gap:var(--s3);margin:0;flex-wrap:wrap}
.panel-num{font:var(--type-label);letter-spacing:var(--track-label);
           font-variant-numeric:tabular-nums;color:var(--text)}
.panel-num i{font-style:normal;color:var(--text-subtle);padding:0 2px}
.panel-rule{flex:1;height:1px;background:var(--divider);min-width:18px;max-width:70px}
.panel-own{font:var(--type-label);letter-spacing:var(--track-label);
           text-transform:uppercase;color:var(--text-subtle);
           border:var(--border-w) solid var(--border);border-radius:var(--r-pill);
           padding:5px 10px}
.panel-own.is-client{color:var(--text-muted);border-color:var(--border-strong)}

.panel-client{margin:0;font:var(--type-meta);letter-spacing:var(--track-label);
              text-transform:uppercase;color:var(--text-muted)}

.panel-headline{margin:0;font:var(--type-project);letter-spacing:-.015em;
                max-width:22ch;text-wrap:balance}
.panel{position:relative}
/* The headline is a link, and only the headline. An earlier version gave it an
   `::after` at inset:0, which turned the entire panel band, most of a viewport
   tall, into one click target. Reading a panel means moving the cursor over it,
   and people click while they read: to rest the pointer, to place a caret, to
   dismiss something. Every one of those clicks navigated away mid-sentence.
   The interactive parts are now the two things that look interactive. */
.panel-headline a{color:inherit;text-decoration:none;
                  text-decoration:underline;text-decoration-color:transparent;
                  text-underline-offset:5px;text-decoration-thickness:2px;
                  transition:text-decoration-color var(--dur-micro) var(--ease)}
.panel-headline a:hover{text-decoration-color:currentColor}
.panel-headline a:focus-visible{outline:2.5px solid var(--focus);outline-offset:6px;
                                border-radius:var(--r-sm)}

.panel-teaser{margin:0;font:var(--type-lede);color:var(--text-muted);max-width:46ch}
.panel-direction{margin:0;font:var(--type-caption);line-height:1.6;
                 color:var(--text-subtle);max-width:52ch;
                 padding-left:var(--s4);border-left:2px solid var(--border)}

.panel-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
             gap:var(--s4);margin:var(--s2) 0 0;padding-top:var(--s4);
             border-top:var(--border-w) solid var(--divider)}
.panel-facts dt{font:var(--type-label);letter-spacing:var(--track-label);
                text-transform:uppercase;color:var(--text-subtle);margin-bottom:5px}
.panel-facts dd{margin:0;font:var(--type-caption);color:var(--text-muted)}

.panel-foot{display:flex;align-items:center;gap:var(--s4);margin:var(--s2) 0 0;
            flex-wrap:wrap}

@media (max-width:900px){
  .panel{grid-template-columns:1fr;gap:var(--s5)}
  .panel:nth-child(even) .panel-media-wrap{grid-column:1;grid-row:1}
  .panel:nth-child(even) .panel-body {grid-column:1;grid-row:2}
  .panel-headline{max-width:none}
}

/* ============================================================
   COMPONENT: image slot / placeholder
   Placeholder names the intended evidence for THIS project.
   ============================================================ */
.slot-img{display:block;width:100%;height:100%;object-fit:cover;
          object-position:top center;background:var(--surface-sunken)}
.slot{position:relative;width:100%;height:100%;display:grid;place-items:center;
      padding:var(--s5);text-align:center;gap:var(--s3)}
.slot-label{font:var(--type-label);letter-spacing:var(--track-label);text-transform:uppercase;
            color:var(--text-subtle)}
.slot-name{font:600 15px/1.4 var(--font);color:var(--text-muted);max-width:32ch}
.slot-note{font:var(--type-caption);color:var(--text-subtle);max-width:34ch}
.slot-grid{position:absolute;inset:0;opacity:.5;
  background-image:linear-gradient(var(--divider) 1px,transparent 1px),
                   linear-gradient(90deg,var(--divider) 1px,transparent 1px);
  background-size:28px 28px;pointer-events:none}

/* ============================================================
   COMPONENT: case-study modules
   ============================================================ */
/* The case hero continues the homepage panel: same colour field, same lead
   image, same ink. Without this, opening a case study dropped from a full
   colour panel to a page of text, which read as a hard stop. */
.case-hero{padding-top:var(--s8);padding-bottom:var(--s7)}
.case-hero.has-field{background:var(--stage);
  /* --text-display is listed explicitly for the same reason initStage sets it:
     it is a color-mix() at :root, so it resolves against the page colour once
     and will not follow an overridden --text. */
  --text:#FFFFFF; --text-display:#FFFFFF;
  --text-muted:rgba(255,255,255,.93); --text-subtle:rgba(255,255,255,.82);
  --border:rgba(255,255,255,.26); --border-strong:rgba(255,255,255,.46);
  --divider:rgba(255,255,255,.22); --surface-sunken:rgba(0,0,0,.20);
  color:var(--text)}

/* Back to work. An explicit way out of a case study that does not depend on
   the browser's own back button, which drops someone who arrived by a direct
   or shared link nowhere useful. */
.case-back{display:inline-flex;align-items:center;gap:var(--s2);
  font:var(--type-meta);font-weight:700;color:inherit;text-decoration:none;
  opacity:.86;margin-bottom:var(--s6)}
.case-back:hover,.case-back:focus-visible{opacity:1}
.case-back-arrow{display:inline-block;transition:translate 180ms var(--ease-settle)}
.case-back:hover .case-back-arrow,
.case-back:focus-visible .case-back-arrow{translate:-4px 0}

.case-hero-in{display:grid;align-items:center;gap:clamp(28px,4vw,64px);
              grid-template-columns:repeat(2,minmax(0,1fr))}
.case-hero-copy{display:flex;flex-direction:column;gap:var(--s5);min-width:0}
.case-hero .case-title{margin:0}
.case-hero .t-lede{margin:0;max-width:52ch}

.case-lead{position:relative;overflow:hidden;border-radius:var(--r-lg);
           border:var(--border-w) solid var(--border);background:var(--surface-sunken);
           display:grid;place-items:center;aspect-ratio:4/3}
.case-lead .slot-img{width:100%;height:100%;object-fit:cover;object-position:top center}

@media (max-width:900px){
  .case-hero-in{grid-template-columns:1fr}
  .case-lead{order:-1}
}
.case-kicker{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;margin-bottom:var(--s5)}
.case-title{font:var(--type-display);letter-spacing:var(--track-display);max-width:16ch}
.case-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
            gap:var(--s5);margin-top:var(--s7);padding-top:var(--s5);
            border-top:var(--border-w) solid var(--divider)}
.case-fact dt{font:var(--type-label);letter-spacing:var(--track-label);text-transform:uppercase;
              color:var(--text-subtle);margin-bottom:var(--s2)}
.case-fact dd{margin:0;font:var(--type-meta);font-weight:600;color:var(--text)}

/* ============================================================
   CASE DEPTH
   A snapshot is short and quiet. A flagship gets room. The layout
   should never make an unfinished concept look like shipped work.
   ============================================================ */

/* SNAPSHOT: narrow measure, no wide modules, smaller display type.
   It reads as a considered note, which is what it is. */
[data-depth="snapshot"]{--w-page:840px}
[data-depth="snapshot"] .case-title{font:800 clamp(30px,4.6vw,50px)/1.04 var(--font)}
[data-depth="snapshot"] .case-hero{padding-top:var(--s7);padding-bottom:var(--s6)}
[data-depth="snapshot"] .module{padding-block:var(--s7)}
[data-depth="snapshot"] .module-grid{grid-template-columns:1fr;gap:var(--s3)}
[data-depth="snapshot"] .module-label{position:static}
[data-depth="snapshot"] .quote{font-size:clamp(19px,2.2vw,26px);max-width:26ch}
[data-depth="snapshot"] .approach{border-radius:var(--r-sm)}
[data-depth="snapshot"] .case-facts{grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}

/* FLAGSHIP: the title earns full display weight and the hero gets air. */
[data-depth="flagship"] .case-hero{padding-bottom:var(--s8)}
[data-depth="flagship"] .case-title{max-width:18ch}

/* Honest gap list. Marked with an open ring, never a checkmark: these are
   things that do not exist, and the glyph should say so. */
.list.gaps li::before{background:transparent;border:1.5px solid var(--text-subtle);
                      box-sizing:border-box}
.list.gaps li{color:var(--text-subtle)}

.module{padding-block:var(--s8);border-top:var(--border-w) solid var(--divider)}
.module-grid{display:grid;grid-template-columns:minmax(0,220px) minmax(0,1fr);gap:var(--s7)}
@media (max-width:860px){.module-grid{grid-template-columns:1fr;gap:var(--s4)}}
.module-label{font:var(--type-label);letter-spacing:var(--track-label);text-transform:uppercase;
              color:var(--text-subtle);position:sticky;top:96px;align-self:start}
@media (max-width:860px){.module-label{position:static}}
.module-body{display:flex;flex-direction:column;gap:var(--s5);max-width:var(--w-text)}

/* Wide module: for evidence, screens, and diagrams. The 70ch cap is right
   for prose and wrong for anything with structure. Label sits above and the
   body gets the full page width. */
.module.wide .module-grid{grid-template-columns:1fr;gap:var(--s5)}
.module.wide .module-label{position:static;padding-bottom:var(--s2);
                           border-bottom:var(--border-w) solid var(--divider)}
.module.wide .module-body{max-width:none;gap:var(--s8)}
/* A section opens with its argument, then shows the proof. */
.section-lede{font:var(--type-lede);color:var(--text);max-width:52ch;
              text-wrap:pretty}
.module.wide .module-body > .t-caption,
.module.wide .module-body > .notice{max-width:var(--w-text)}
/* Cap a single lead screen so it stays a considered image, not a banner. */
.module.wide .evidence-lead{max-width:1040px}
.module-body p{font:var(--type-body);color:var(--text-muted)}
.module-body p.lead{font:var(--type-lede);color:var(--text)}

.list{display:grid;gap:var(--s4);padding:0;margin:0;list-style:none}
.list li{position:relative;padding-left:var(--s5);font:var(--type-body);color:var(--text-muted)}
.list li::before{content:"";position:absolute;left:0;top:11px;width:7px;height:7px;
                 border-radius:var(--r-pill);background:var(--accent);opacity:.9}

.approach{display:grid;gap:1px;background:var(--divider);
          border:var(--border-w) solid var(--divider);border-radius:var(--r-md);overflow:hidden}
.approach div{background:var(--page);padding:var(--s5)}
.approach h3{font:600 16px/1.3 var(--font);letter-spacing:-.01em;margin-bottom:var(--s2)}
.approach p{font:var(--type-caption);font-size:14.5px;color:var(--text-muted);line-height:1.55}

.quote{font:700 clamp(21px,2.6vw,32px)/1.22 var(--font);letter-spacing:-.025em;
       max-width:22ch;color:var(--text)}
.quote span{color:var(--accent)}

.stack{display:flex;flex-direction:column;gap:var(--s8)}
.evidence-grid{display:grid;gap:var(--s5);grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.frame{border:var(--border-w) solid var(--border);border-radius:var(--r-md);
       background:var(--surface-sunken);overflow:hidden;position:relative}
.frame-cap{padding:var(--s3) var(--s4);border-top:var(--border-w) solid var(--border);
           background:var(--surface);font:var(--type-caption);color:var(--text-muted)}

/* ---------- IMAGE TREATMENT ----------
   Source screens are TOP-JUSTIFIED in their window: original width and
   aspect ratio preserved, cropped only from the bottom. Never center-crop,
   never stretch. (Freya Handoff, non-negotiable placement rule.) */
.shot{position:relative;width:100%;overflow:hidden;background:var(--surface-sunken);
      display:block;border:0;padding:0;cursor:zoom-in;width:100%}
.shot img{width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
.shot::after{content:"";position:absolute;inset:0;pointer-events:none;
             box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}
.shot:hover::after{box-shadow:inset 0 0 0 1px var(--accent)}

/* Desktop screen sits in a restrained browser frame. */
.device-desktop{border:var(--border-w) solid var(--border);border-radius:var(--r-md);
                overflow:hidden;background:var(--surface)}
.device-bar{display:flex;align-items:center;gap:6px;padding:9px var(--s4);
            border-bottom:var(--border-w) solid var(--border);background:var(--surface-raised)}
.device-bar i{width:8px;height:8px;border-radius:var(--r-pill);background:var(--border-strong);
              display:block}

/* Mobile artboard sits in a phone viewport, width preserved, top-justified. */
.device-phone{width:186px;flex:none;border:6px solid var(--text);border-radius:20px;
              overflow:hidden;background:var(--surface);box-shadow:var(--elev-1)}
.device-phone .shot{aspect-ratio:375/700}

.pair{display:flex;gap:var(--s5);align-items:flex-start}
.pair > .device-desktop{flex:1;min-width:0}
@media (max-width:640px){
  .pair{flex-direction:column;align-items:stretch}
  .device-phone{width:150px;align-self:center}
}

/* ---------- DECK GALLERY ---------- */
/* Screen gallery. Sized so an interface stays readable rather than becoming a
   swatch, and each cell is a lightbox trigger for the full-size view. */
.screens{display:grid;gap:var(--s4);
         grid-template-columns:repeat(auto-fill,minmax(340px,1fr))}
@media (min-width:1240px){ .screens{grid-template-columns:repeat(3,minmax(0,1fr))} }
.screens .shot{border:var(--border-w) solid var(--border);border-radius:var(--r-md);
               background:var(--surface-sunken);overflow:hidden}
.screens .shot img{object-fit:cover;object-position:top center}
.screens .shot:hover{border-color:var(--border-strong)}

.deck{display:grid;gap:var(--s3);
      grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
@media (min-width:1040px){ .deck{grid-template-columns:repeat(auto-fill,minmax(170px,1fr))} }
.deck .shot{aspect-ratio:16/9;border:var(--border-w) solid var(--border);
            border-radius:var(--r-sm)}

/* ---------- Fact list ---------- */
.facts{display:grid;gap:var(--s4);margin:var(--s5) 0 0;padding-top:var(--s5);
       border-top:var(--border-w) solid var(--divider)}
.facts > div{display:grid;grid-template-columns:minmax(90px,110px) 1fr;gap:var(--s4);
             align-items:baseline}
.facts dt{font:var(--type-label);letter-spacing:var(--track-label);text-transform:uppercase;
          color:var(--text-subtle)}
.facts dd{margin:0;font:var(--type-body);font-size:15.5px;color:var(--text)}
@media (max-width:520px){.facts > div{grid-template-columns:1fr;gap:2px}}

/* ============================================================
   ORIGINAL CODED COMPONENTS
   Portfolio explanations, visibly distinct from client screens:
   no device frame, flat on the page surface, labelled as diagrams.
   ============================================================ */
.dgm{display:flex;flex-direction:column;gap:var(--s6);
     border:var(--border-w) solid var(--border);border-radius:var(--r-md);
     padding:clamp(var(--s5),3vw,var(--s7));background:var(--surface)}
/* A lone diagram in its own section does not need a second frame around it. */
.stack > .dgm:only-child{border:0;background:transparent;padding:0}
.dgm-head{display:grid;gap:var(--s3) var(--s7);align-items:start}
@media (min-width:900px){
  .dgm-head{grid-template-columns:minmax(0,320px) minmax(0,1fr)}
}
.dgm-head h3{font:800 clamp(20px,2vw,26px)/1.15 var(--font);letter-spacing:-.025em}
.dgm-head p{font:var(--type-body);font-size:16px;color:var(--text-muted);max-width:60ch}
.chip{display:inline-block;padding:3px 9px;border-radius:var(--r-pill);
      border:var(--border-w) solid var(--border);font:var(--type-caption);font-size:12px;
      color:var(--text-muted);background:var(--page)}

/* --- IA hierarchy --- */
.ia{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s2)}
.ia-row{display:flex;gap:var(--s4);align-items:flex-start;
        padding:var(--s4);border-radius:var(--r-sm);background:var(--page);
        border:var(--border-w) solid var(--divider);
        margin-left:calc(var(--depth) * clamp(0px, 2.2vw, 26px));
        position:relative}
.ia-row::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
                border-radius:var(--r-pill);background:var(--accent);
                opacity:calc(1 - (var(--depth) * .13))}
.ia-level{font:800 12px/1 var(--font);color:var(--text-subtle);
          padding-top:3px;flex:none;width:22px}
.ia-body{display:flex;flex-direction:column;gap:5px;min-width:0}
.ia-name{font:700 15.5px/1.25 var(--font);letter-spacing:-.01em}
.ia-eg{font:var(--type-caption);color:var(--text-muted)}
.ia-kinds{display:flex;gap:6px;flex-wrap:wrap;margin-top:3px}

/* --- Migration steps --- */
.steps{list-style:none;margin:0;padding:0;display:grid;gap:var(--s3);
       grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.step-card{background:var(--page);border:var(--border-w) solid var(--divider);
           border-radius:var(--r-sm);padding:var(--s4);
           display:flex;flex-direction:column;gap:var(--s3)}
.step-top{display:flex;align-items:center;gap:var(--s3)}
.step-n{width:24px;height:24px;border-radius:var(--r-pill);flex:none;
        background:var(--accent);color:var(--page);
        display:grid;place-items:center;font:800 12px/1 var(--font)}
.step-name{font:700 15px/1 var(--font);letter-spacing:-.01em}
.step-what{font:var(--type-caption);font-size:13.5px;line-height:1.5;color:var(--text-muted);flex:1}
.step-foot{display:grid;grid-template-columns:auto 1fr;gap:4px var(--s3);
           padding-top:var(--s3);border-top:var(--border-w) solid var(--divider);
           font:var(--type-caption);font-size:12.5px;color:var(--text-muted)}

/* --- Workflow: step, what it does, what survives --- */
.flow{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s2)}
.flow-row{display:grid;grid-template-columns:auto minmax(0,1fr) minmax(0,1.1fr);
          gap:var(--s4);align-items:start;padding:var(--s4);
          background:var(--page);border:var(--border-w) solid var(--divider);
          border-radius:var(--r-sm)}
.flow-n{font:800 11px/1 var(--font);color:var(--accent);padding-top:4px;flex:none}
.flow-body{display:flex;flex-direction:column;gap:3px;min-width:0}
.flow-name{font:700 15.5px/1.2 var(--font);letter-spacing:-.01em}
.flow-does{font:var(--type-caption);color:var(--text-muted)}
.flow-keeps{display:flex;flex-direction:column;gap:3px;min-width:0;
            padding-left:var(--s4);border-left:var(--border-w) solid var(--divider)}
.flow-keeps span:last-child{font:var(--type-caption);font-size:13.5px;color:var(--text)}
@media (max-width:720px){
  .flow-row{grid-template-columns:auto minmax(0,1fr)}
  .flow-keeps{grid-column:2;padding-left:0;padding-top:var(--s3);border-left:0;
              border-top:var(--border-w) solid var(--divider)}
}

/* --- Matrix: preset classification --- */
.matrix{width:100%;border-collapse:collapse;font:var(--type-caption);font-size:13.5px}
.matrix th,.matrix td{text-align:left;padding:var(--s3) var(--s3) var(--s3) 0;
                      border-bottom:var(--border-w) solid var(--divider);vertical-align:top}
.matrix thead th{font:var(--type-label);letter-spacing:var(--track-label);
                 text-transform:uppercase;color:var(--text-subtle);
                 border-bottom-color:var(--border-strong)}
.matrix tbody th{font:600 14px/1.4 var(--font);color:var(--text);padding-right:var(--s4)}
.matrix-what{color:var(--text-muted)}
.kind{display:inline-block;padding:3px 9px;border-radius:var(--r-pill);
      font:var(--type-label);letter-spacing:.08em;text-transform:uppercase;
      border:var(--border-w) solid var(--border)}
.kind[data-kind="correct"]{color:var(--text-muted)}
.kind[data-kind="alter"]{color:var(--text);border-color:var(--text);font-weight:800}
.matrix .yes{font-weight:700;color:var(--text)}
.matrix .no{color:var(--text-subtle)}
@media (max-width:640px){
  .matrix thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  .matrix tr{display:grid;gap:4px;padding:var(--s4) 0;
             border-bottom:var(--border-w) solid var(--divider)}
  .matrix th,.matrix td{border:0;padding:0}
}

/* --- Journey --- */
.journey{list-style:none;margin:0;padding:0}
.jr{display:flex;gap:var(--s4)}
.jr-rail{display:flex;flex-direction:column;align-items:center;flex:none;width:14px}
.jr-dot{width:11px;height:11px;border-radius:var(--r-pill);background:var(--accent);
        margin-top:6px;flex:none}
.jr-line{width:2px;flex:1;background:var(--divider);margin:4px 0}
.jr-body{padding-bottom:var(--s5);display:flex;flex-direction:column;gap:5px;min-width:0}
.jr-phase{font:var(--type-label);letter-spacing:var(--track-label);text-transform:uppercase;
          color:var(--text-subtle)}
.jr-q{font:700 17px/1.25 var(--font);letter-spacing:-.015em}
.jr-need{font:var(--type-caption);font-size:14px;color:var(--text-muted);max-width:56ch}
.jr-ui{font:var(--type-caption);font-size:13px;color:var(--text-subtle);max-width:56ch}
.jr-ui .t-label{display:inline;margin-right:6px;color:var(--accent)}

/* ---------- LIGHTBOX ---------- */
.lightbox{position:fixed;inset:0;z-index:var(--z-modal);background:var(--overlay);
          display:none;align-items:center;justify-content:center;padding:var(--s5);
          backdrop-filter:blur(6px)}
.lightbox[open]{display:flex}
.lightbox-img{max-width:min(1200px,92vw);max-height:86vh;overflow:auto;
              border-radius:var(--r-md);background:var(--surface);
              box-shadow:var(--elev-2);overscroll-behavior:contain}
.lightbox-img img{width:100%;display:block}
.lightbox-cap{position:absolute;left:0;right:0;bottom:var(--s4);text-align:center;
              font:var(--type-caption);color:#fff;padding-inline:var(--s5);
              text-shadow:0 1px 3px rgba(0,0,0,.7)}
.lightbox button{position:absolute;width:48px;height:48px;border-radius:var(--r-pill);
                 background:var(--surface);color:var(--text);display:grid;place-items:center;
                 font-size:20px;box-shadow:var(--elev-1)}
.lb-close{top:var(--s5);right:var(--s5)}
.lb-prev{left:var(--s5);top:50%;transform:translateY(-50%)}
.lb-next{right:var(--s5);top:50%;transform:translateY(-50%)}
@media (max-width:640px){
  .lb-prev{left:var(--s3)} .lb-next{right:var(--s3)}
  .lb-close{top:var(--s3);right:var(--s3)}
}

.notice{border:var(--border-w) solid var(--border-strong);border-radius:var(--r-md);
        padding:var(--s5);background:var(--surface);display:flex;gap:var(--s4);
        align-items:flex-start;max-width:var(--w-text)}
.notice-glyph{font-size:14px;line-height:1.5;color:var(--text-subtle);flex:none}
.notice p{font:var(--type-caption);font-size:14.5px;color:var(--text-muted);line-height:1.6}

/* ============================================================
   COMPONENT: actions
   ============================================================ */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--s2);
     padding:14px 24px;border-radius:var(--r-pill);font:var(--type-button);
     text-decoration:none;border:1.5px solid transparent;
     transition:transform var(--dur-micro) var(--ease),background var(--dur-micro) var(--ease),
                border-color var(--dur-micro) var(--ease)}
.btn.btn-primary{background:var(--text);color:var(--page)}
.btn.btn-primary:hover{transform:translateY(-1px)}
.btn.btn-secondary{border-color:var(--border-strong);color:var(--text)}
.btn.btn-secondary:hover{background:var(--surface);border-color:var(--text)}
.btn:active{transform:scale(.98)}

/* ============================================================
   COMPONENT: footer + contact
   ============================================================ */
.footer{border-top:var(--border-w) solid var(--divider);padding-block:var(--s8)}
.footer-in{display:flex;justify-content:space-between;gap:var(--s6);flex-wrap:wrap}
.footer p{font:var(--type-caption);color:var(--text-subtle);max-width:52ch}

/* ============================================================
   STATES: loading, empty, error
   ============================================================ */
.state{padding:var(--s9) 0;text-align:center;display:grid;gap:var(--s3);justify-items:center}
.state h2{font:var(--type-section);letter-spacing:var(--track-section)}
.state p{font:var(--type-body);color:var(--text-muted);max-width:44ch}
.is-loading{opacity:.5}

/* ============================================================
   MOTION: scroll reveal. Removed entirely under reduced motion.
   ============================================================ */
/* Reveal is a progressive enhancement. The hidden state only exists once the
   script has confirmed it can undo it, so no failure mode leaves the page
   blank: no JavaScript, no IntersectionObserver, or a throttled background
   tab all fall back to fully visible content. */
:root.js-reveal .reveal{opacity:0;transform:translateY(14px);
        transition:opacity var(--dur-move) var(--ease-out),transform var(--dur-move) var(--ease-out)}
:root.js-reveal .reveal[data-shown="true"]{opacity:1;transform:none}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:1ms!important;animation-iteration-count:1!important;
    transition-duration:1ms!important;
  }
  :root.js-reveal .reveal{opacity:1;transform:none}
}

/* ============================================================
   TRY IT: links to the working product
   The strongest evidence a product designer can offer is the
   thing itself, so these are prominent rather than a footnote.
   ============================================================ */
.try{display:grid;gap:var(--s3);margin-top:var(--s5);
     grid-template-columns:repeat(auto-fit,minmax(260px,1fr));max-width:var(--w-text)}
.try-link{display:flex;flex-direction:column;gap:4px;text-decoration:none;
          padding:var(--s4) var(--s5);border-radius:var(--r-md);
          border:1.5px solid var(--text);background:var(--text);color:var(--page);
          transition:transform var(--dur-micro) var(--ease)}
.try-link:nth-child(2){background:transparent;color:var(--text);
                       border-color:var(--border-strong)}
.try-link:hover{transform:translateY(-2px)}
.try-label{font:var(--type-button);font-size:16px}
.try-note{font:var(--type-caption);font-size:13px;opacity:.72}

/* ---------- BETS: expected outcomes, stated as testable claims ---------- */
.bets{display:grid;gap:1px;background:var(--divider);
      border:var(--border-w) solid var(--divider);border-radius:var(--r-md);
      overflow:hidden;grid-template-columns:1fr}
@media (min-width:760px){ .bets{grid-template-columns:repeat(2,minmax(0,1fr))} }
.bet{background:var(--page);padding:var(--s5);display:flex;flex-direction:column;
     gap:var(--s3)}
.bet-claim{font:600 17px/1.4 var(--font);letter-spacing:-.01em;color:var(--text)}
.bet-test{font:var(--type-caption);font-size:14px;line-height:1.55;color:var(--text-muted)}
.bet-test .t-label{display:block;margin-bottom:3px;color:var(--text-subtle)}
