/* =========================================================================
   System tokens — exact spec
   Pink carries authorship and current state.
   Yellow carries punctuation and transition.
========================================================================= */

:root{
  --ground:        #1A1716;
  /* --inverse-ground: the OTHER theme's --ground value. Used by the
     principles/manifesto section to perform a "tonal pivot" — the section
     uses the opposite mode's surface as its bg, and the spotlight effect
     punches through to reveal the current mode's --ground underneath.
     In dark mode (default), inverse = light mode's cream surface. */
  --inverse-ground: #F3F0E8;
  /* --inverse-accent: mirrors the inverse-ground pattern. Used for the
     spotlight color in the manifesto section so the accent weight is
     calibrated to the inverted surface. In dark mode the section is
     cream, so inverse-accent is the LIGHT-mode pink (deeper rose)
     which reads beautifully against cream. */
  --inverse-accent: #C76681;
  --paper:         #222222;
  /* --paper-2 progression: #45413C → #524C45 → #4A4640 → #494847 →
     #4F4E4D → #3D3B3A → #322F2E. Closer to perceptual parity with light
     mode's page-vs-surface contrast (~1.45 ratio vs light mode's ~1.12).
     #3D3B3A still read ~42% more contrasted than light mode; #322F2E
     pulls into a calmer "subtly distinct, not abrupt" register at both
     band and footer scale. Near-zero chroma preserved. Banner AND footer
     share this value, mirroring the light mode pattern. */
  --paper-2:       #322F2E;
  /* --asset-bg: dedicated surface for asset containers (gallery,
     figure, BA stage). In dark mode this matches --paper-2 because
     dark mode has enough contrast room that one warm-dark elevation
     serves both row-hover chrome and asset framing. In light mode
     they DIVERGE (see light-mode token block below) — row hover
     wants quiet, asset containers want defined framing. */
  --asset-bg:      #322F2E;

  --ink:           #F5F0E8;
  --mid:           #9A938A;
  --mid-2:         #C5BDB1;

  --rule:          rgba(245, 240, 232, .10);
  --rule-strong:   rgba(245, 240, 232, .26);

  /* Dark mode pink progression: #EC7E9C (vivid) → #F4B5C0 (chalky) →
     #F0A1B0 (white-leaning) → #EE8DA8 (too far from old's warmth) →
     #ED85A1 (~60% pulled back toward original). Saturation lifted ~3pts,
     lightness pulled down ~1pt, hue slightly more coral — reclaims the
     warmth that made the original feel like a "moment" while staying
     softer than #EC7E9C. */
  --accent:        #ED85A1;
  --accent-strong: #ED85A1;
  --accent-soft:   rgba(237, 133, 161, .14);

  /* --accent-2 is now RESERVED for the You cursor only. Stays yellow. */
  --accent-2:        #F5D76E;
  --accent-2-strong: #E6C24A;
  --accent-2-soft:   rgba(245, 215, 110, .14);

  /* --deep mirrors --paper-2 (both #322F2E). The prior #0F0F0F dissolved
     into the page; #3D3B3A was distinct but read more contrasted than
     light mode's footer. #322F2E lands at perceptual parity with light
     mode's page-vs-footer contrast feel. Banner and footer share one
     surface vocabulary in dark mode, matching light mode's system. */
  --deep:          #322F2E;
  --deep-2:        #1A1716;
  --deep-mid:      #9A938A;
  --deep-mid-2:    #6A645C;
  --deep-rule:     rgba(245, 240, 232, .14);
  /* Contained form-field surface — sits one notch above the deep footer
     surface so inputs read as discrete containers. Uses ink-tinted overlay
     so the theme accent inherits cleanly. */
  --field-bg:        rgba(245, 240, 232, .035);
  --field-bg-hover:  rgba(245, 240, 232, .06);
  --field-border:    rgba(245, 240, 232, .14);
  --field-border-hover: rgba(245, 240, 232, .26);

  /* Page width + padding system — one source of truth.
     Content max-width is 1750px (excluding padding). Edge padding
     ramps down as the viewport narrows.
       Desktop  ≥ 1200px → 60px padding, content caps at 1750px
       Tablet   745–1199 → 32px padding, content fills viewport
       Mobile   < 745    → 20px padding, content fills viewport
     The 980px media block further down handles layout-collapse for
     the homepage's specific responsive logic (nav, type scale) — that
     is intentionally independent of this padding/width system. */
  --pad-x:     60px;
  --page-max:  1750px;
  --ease:  cubic-bezier(.2,.7,.2,1);
  --ease-soft: cubic-bezier(.4,.0,.2,1);

  --bulb-lit: #E8B935;

  /* Text color when sitting on a full --accent (pink) fill — see
     .row:hover state in the work section. Set to var(--ground) so it
     matches the page bg in each theme, creating a STENCIL/CUTOUT
     illusion: the pink fill reads as a layer, and the text reads as a
     hole through it revealing the page color beneath. Dark mode = dark
     text on pink (carved into page). Light mode = cream text on rose
     (cutout to page).
     For revert to hardcoded dark: --on-accent: #1A1716; */
  --on-accent: var(--ground);
}

/* =========================================================================
   Light theme — modern, contemporary, design-forward.
   Electric blue + purple-blue. Confident accents that read as creative,
   not corporate. Same designer made both themes.
========================================================================= */
:root[data-theme="light"]{
  /* Clean, restrained, hiring-manager-friendly palette.
     Surface hierarchy through colour, not strokes:
       --ground = warm-neutral page background
       --paper  = pure white artifact surfaces (clear elevation against
                  the page)
       --rule   = visible-but-subtle stroke when needed
     Borders are no longer the primary separator — surface delta does
     that work. Accents kept restrained (deep rose + muted teal). */
  /* Option C: lighter warm-white. Was #F3F0E8 (warm eggshell); this
     leans much closer to white but keeps a hint of warmth so chrome
     elements still feel like part of a warm-family palette (no
     cool-gray-on-warm-white mismatch). Trade-off: less editorial
     "handcrafted" character vs. the eggshell, but significantly
     better case-study legibility. */
  --ground:        #FBFAF6;
  /* In light mode, inverse-ground is the dark theme's surface. */
  --inverse-ground: #1A1716;
  /* In light mode the section is dark, so inverse-accent is the
     DARK-mode coral pink which pops crisp against dark surfaces. */
  --inverse-accent: #ED85A1;
  --paper:         #FFFFFF;
  /* --paper-2: warm-family but with the yellow saturation dialed back
     so it reads as "warm-neutral" rather than "sand/tan." The prior
     #EEEBE3 had R-B delta of 11 (clearly warm-yellow); this drops to
     R-B of 6, so it still belongs to the warm family but doesn't
     shout "khaki" on hover states.
     Updated to #F3F1EA — pulls --paper-2 closer to --ground for an
     even quieter chrome layer. Affects: row hover, manifesto back
     layer, case-study hero/TOC cards. Asset containers (gallery,
     figure, BA) now route through --asset-bg instead so they keep
     the more-defined #EBEAE5 framing — see below. */
  --paper-2:       #F3F1EA;
  /* --asset-bg: dedicated asset-container surface. Keeps the old
     --paper-2 value (#EBEAE5, ~16 darker than --ground) so figures,
     galleries, and before/after stages still read as defined
     "framed" containers even after --paper-2 went quieter for other
     chrome layers. Light mode is where the two tokens diverge;
     in dark mode they're the same value. */
  --asset-bg:      #EBEAE5;

  --ink:           #151515;
  --mid:           #6A6A6A;
  --mid-2:         #3D3D3D;

  /* Rule strokes lightened to be proportional to the new lighter
     --ground (#FBFAF6). The prior #E7E5E1 was calibrated against the
     old eggshell ground (#F3F0E8); on the new lighter bg it read as
     too prominent, especially on the homepage Work row dividers
     (where it's used at 80% opacity). Lifted ~8 RGB units to match
     the same visual subtlety against the new bg. */
  --rule:          #EFEDE8;
  --rule-strong:   #DAD7D1;

  /* Softer daytime pink — pulled toward the quiet ideation's territory.
     The prior #B83A5C read too "strawberry" on the cream paper: deep
     crimson saturation made the band an aggressive billboard rather than
     a confident accent. #C76681 is ~25% closer to quiet's #CB718A — still
     unambiguously raspberry, but stops shouting. Pairs more gracefully
     with the sage. */
  --accent:        #C76681;
  --accent-strong: #C76681;
  --accent-soft:   rgba(199, 102, 129, .10);

  /* Brighter sage — lifted from the deep #566B47 to bring it out of muddy
     territory. Brighter (lightness ~47% vs ~35%) AND more saturated (~29%
     vs ~20%) so it reads as confident classic sage instead of dusty olive.
     Now at matched depth with the pink (both ~47% lightness), so they pair
     as true siblings — rose + herb energy, classy without being heavy. */
  --accent-2:        #719A54;
  --accent-2-strong: #5C7843;
  --accent-2-soft:   rgba(113, 154, 84, .12);

  /* --deep stepped #ECE9E2 → #E6E3DA (matches quiet ideation's footer
     surface). Prior value was only ~3% darker than --ground — the footer
     barely registered as a different layer. New value is ~6% darker,
     enough contrast for the footer to read as a deliberate paper
     surface without losing the calm neutral tone we landed on. */
  --deep:          #E6E3DA;
  --deep-2:        #DAD7CD;
  --deep-mid:      #6A6A6A;
  --deep-mid-2:    #909090;
  --field-bg:        rgba(21, 21, 21, .035);
  --field-bg-hover:  rgba(21, 21, 21, .06);
  --field-border:    rgba(21, 21, 21, .14);
  --field-border-hover: rgba(21, 21, 21, .26);
  --deep-rule:     rgba(21, 21, 21, .12);
}

/* Theme transition */
body, nav.top .nav-inner, section.bands .band, footer, .preview-card, .theme-toggle,
.case-hero, .case-section, .decision-card, .module-quote, .placeholder-image, .placeholder-video, .placeholder-pdf{
  transition:background-color .35s var(--ease), color .35s var(--ease), border-color .35s var(--ease);
}

/* Light-mode component overrides */
:root[data-theme="light"] .bands .band[data-dir="left"]{
  /* Light mode override: white-ish text on the deep crimson fill. Bumped to
     .80 so the text reads cleanly against the saturated red — crimson is darker
     than the dark-mode pink, so it can support higher text opacity. */
  color:rgba(250, 246, 238, .80);
  box-shadow:
    0 18px 40px -16px rgba(20, 17, 15, .22),
    0 6px 16px -6px rgba(20, 17, 15, .14);
}
:root[data-theme="light"] .bands .band[data-dir="left"] .statement:hover{color:var(--ground);}
:root[data-theme="light"] .bands .band[data-dir="right"]{
  /* color inherits from base rule (var(--accent-2-strong)) which adapts
     per mode: marigold yellow in light, buttery yellow in dark. */
  box-shadow:
    0 12px 26px -12px rgba(20, 17, 15, .14),
    0 4px 10px -3px rgba(20, 17, 15, .10);
}
/* Light-mode hover override removed for the right band — same opacity-only
   resolve as dark mode (handled by the base .bands .band[data-dir="right"]
   .statement:hover rule). */
/* Principles in light mode.
   Inactive items: subdued but readable. Active item: jumps to teal.
   The active-state selector MUST be more specific than the inactive
   light-theme override — without this, the (0,3,1) inactive selector
   was overriding the (0,2,1) base active rule and bleaching the accent.
   These two selectors are (0,3,1) and (0,4,1) — active wins. */
/* Light-mode nav-inner chrome override removed — nav is now chromeless
   in both modes. Editorial split layout uses page typography colors. */
/* Light-mode submit override — the footer keeps its dark surface in light
   mode (var(--deep)), so the submit still sits on a dark background. The
   default light text color works there. No override needed unless the
   footer surface ever flips to a paper background. */
/* Footer + asset surfaces in light mode now resolve through theme
   tokens (footer = var(--deep), assets = var(--paper)). The hardcoded
   #ffffff overrides have been removed — the tokens carry refined
   warm-paper values that avoid both khaki and stark white. */

/* Soften atmospheric shadows in light mode. Dark mode opacities (.38, .42)
   are sized for elevation against #181818; on cream they'd read as smudges.
   Light-mode values are warm-tinted and lower-alpha so artifacts feel
   lifted without the chrome announcing itself. Restraint is the goal —
   light mode is the clean alternative, not a second dramatic identity. */
:root[data-theme="light"] .module-figure,
:root[data-theme="light"] .module-before-after{
  box-shadow:
    0 24px 56px -24px rgba(20, 17, 15, .18),
    0 6px 16px -8px rgba(20, 17, 15, .08);
}
:root[data-theme="light"] .module-gallery-viewer .gv-card{
  box-shadow:
    0 28px 64px -24px rgba(20, 17, 15, .20),
    0 8px 20px -8px rgba(20, 17, 15, .09);
}
:root[data-theme="light"] .module-pdf .pdf-frame{
  box-shadow:
    0 32px 70px -24px rgba(20, 17, 15, .22),
    0 10px 24px -8px rgba(20, 17, 15, .10);
}

/* =========================================================================
   Padding ramps for the breakpoint system. Order matters — smaller
   breakpoints override larger ones.
========================================================================= */
@media (max-width: 1199px){ :root{ --pad-x: 32px; } }
@media (max-width: 744px) { :root{ --pad-x: 20px; } }
/* (Decision cards no longer carry chrome — the light-mode shadow override
   that lived here has been removed.) */

*{box-sizing:border-box;margin:0;padding:0;}
html,body{background:var(--ground);color:var(--ink);}
/* Prevent horizontal scroll across the whole site. Several elements
   (the manifesto back-layer, big Antonio hero type, accent-color
   sweeping bands) can extend past the viewport edge on narrow
   screens. Clipping at the body level means the page never grows
   wider than its viewport, regardless of what's inside. Vertical
   scroll stays untouched. */
html,body{overflow-x:hidden;max-width:100vw;}
/* Disable overscroll bounce. Without this, trackpad-pulling at the top
   of the page rubber-bands the main element downward and reveals the
   fixed back-layer (.principles-back) underneath — which the back layer
   is intentionally hidden behind the main during normal scroll. The
   bounce broke that layering metaphor by exposing the back stage
   prematurely. */
html{overscroll-behavior-y:none;}

/* Previous body::before viewport-top gradient fade was removed.
   The fade approach affected everything at the top of the viewport
   (transition bands, hero letterforms, anything passing through),
   not just the nav-hero collision. The replacement is a solid
   background on the nav itself (see nav.top rule below) that matches
   the page color exactly — invisible at rest, masks content cleanly
   during scroll without applying a gradient to it. */
/* scroll-behavior is intentionally NOT set to smooth here. In-page
   anchor clicks are handled by the smooth-scroll JS IIFE further
   down, which calls preventDefault() and animates with easing.
   Leaving CSS scroll-behavior at the default ('auto') means initial-
   load hash navigation (e.g. landing on index.html#work from a case
   study's back-link) snaps directly to the anchor instead of doing
   a visible hero-to-work animation. */
body{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-size:16px;line-height:1.55;letter-spacing:-0.005em;font-weight:400;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;
}
body.cursor-on{cursor:none;}
body.cursor-on a, body.cursor-on button, body.cursor-on [data-row]{cursor:none;}
body.cursor-on input, body.cursor-on textarea{cursor:text;}
/* Higher-specificity overrides for UI controls that declare cursor:pointer
   in their own rules — those would otherwise win against body.cursor-on
   and reveal the native cursor alongside the custom one. Two-class
   compound selectors elevate specificity to (0,3,1) so they win against
   any single .class rule. */
body.cursor-on .module-gallery-viewer .gv-nav,
body.cursor-on .module-gallery-viewer .gv-thumb,
body.cursor-on .module-gallery-viewer .gv-slide,
body.cursor-on .module-gallery-viewer figure,
body.cursor-on .module-gallery-viewer img,
body.cursor-on .module-pdf .pdf-nav,
body.cursor-on .module-pdf iframe,
body.cursor-on .module-before-after .ba-toggle-btn,
body.cursor-on .module-gallery.is-lightbox figure,
body.cursor-on .module-comparison.is-lightbox figure,
body.cursor-on .bands .statement,
body.cursor-on .bands .band,
body.cursor-on .acc-item,
body.cursor-on .acc-item .acc-head,
body.cursor-on .toggle button{cursor:none;}

::selection{background:var(--accent);color:var(--ground);}

a{color:inherit;text-decoration:none;}
button{background:none;border:0;color:inherit;font:inherit;letter-spacing:inherit;}

/* =========================================================================
   Navigation — floating capsule
========================================================================= */
/* Editorial split nav — no chrome. Brand floats at the page's left edge,
   nav links float at the right edge. No background, border, or shadow —
   the typography IS the design. Hide-on-scroll behavior preserved via
   transform on the wrapper. Pointer events disabled on the wrapper +
   inner row so the empty middle doesn't block clicks on content
   beneath the nav area; only the brand and links capture pointer events. */
nav.top{
  position:fixed;
  /* Anchor to top:0 instead of top:24 so the nav element's bg can
     extend all the way to the viewport top. Top breathing room is now
     created via padding-top inside the nav. */
  top:0;left:0;right:0;z-index:100;
  pointer-events:none;
  /* Solid page-color background — invisible at rest (matches page bg
     exactly), masks any content scrolling up underneath it. This is
     the "invisible chrome" pattern: chromeless aesthetic preserved
     because you can never see the chrome, but the masking exists to
     prevent hero text and the nav typography from visually colliding. */
  background:var(--ground);
  padding:24px 0 12px;
  /* Default transition is the SHOW transition — graceful return on
     scroll-up (280ms ease). Hide transition is overridden below to be
     faster (140ms ease-in). background-color is also transitioned so
     the nav can adopt the back-layer surface (paper-2) when over it —
     see body.over-back-layer rule. */
  transition:transform .28s var(--ease), background-color .35s var(--ease);
  will-change:transform;
}
/* When the back layer (paper-2) is the surface sitting behind the nav
   strip, the nav adopts that surface so it stops reading as a dark bar
   floating on a lighter card. Toggled by JS based on the reveal zone's
   position relative to the nav's bottom edge. */
body.over-back-layer nav.top{
  background:var(--paper-2);
}
nav.top.is-hidden{
  transform:translateY(calc(-100% - 28px));
  transition:transform .14s ease-in;
}

nav.top .nav-inner{
  pointer-events:none;
  display:flex;align-items:center;justify-content:space-between;
  /* Nav aligns with the page's content width — homepage default uses
     --page-max (1750px); case study pages override to --v2-container
     (1280px) via the [data-case] selector below. This makes the brand
     and links anchor to the same left/right edges as the page content
     on each surface, so the nav never extends past the readable area. */
  max-width:var(--page-max);
  margin:0 auto;
  padding:0 var(--pad-x);
  box-sizing:content-box;
  /* No background, border, radius, padding-inside, or shadow — the
     editorial layout doesn't need a contained pill. */
}
/* On case study pages (html[data-case] set by case.html), narrow the
   nav-inner max-width to match the case study container. */
:root[data-case] nav.top .nav-inner{
  max-width:1280px;
}
nav.top .brand,
nav.top .nav-links{
  pointer-events:auto;
}

nav.top .brand{
  /* Plus Jakarta Sans (not Antonio) so the brand reads as the same
     typographic family as the nav links — they form one cohesive block
     with subtle differentiation (the name is slightly bolder + 1px
     larger than the links). Antonio at 800 was too visually heavy here
     and the "Product Designer" role label compounded the heaviness.
     Switching to Plus Jakarta keeps the role label visible without the
     whole left side claiming brand-of-a-company weight. */
  font-family:'Plus Jakarta Sans',sans-serif;
  font-weight:700;
  /* Tracking pulled from .08em → .05em. The 700 weight intrinsically
     "fills" letter-spacing gaps more than the 500-weight nav links do,
     so the same .08em that reads "comfortably spaced" on the nav reads
     "floaty" on the bolder brand text. Tighter tracking on bolder text
     is standard typographic practice. */
  font-size:13px;letter-spacing:.05em;
  text-transform:uppercase;
  color:var(--ink);
  line-height:1;
  display:inline-flex;align-items:center;
  /* translateY removed — was a 1px nudge to align Anton's cap-position
     to Satoshi-class caps in the nav links. With Plus Jakarta Sans the
     brand now shares the same cap-position as the nav links, so no
     compensation needed. */
  /* No hover color shift — the brand is identity, not interactive UI.
     Pink hover on the wordmark + role label was reinforcing "Shayna IS
     the pink accent" but ended up being too much animation now that
     "· Product Designer" extends the hovered area. Brand stays static;
     the click affordance is implicit (it's clearly the top-left logo
     position, links to #top). */
}
/* Role label after the brand name — separated by a dot. Inherits family
   from .brand (Plus Jakarta Sans). Weight 500 matches the nav links
   exactly, and color --mid matches them too — the role label reads as
   "the same register as nav links" rather than "second brand element."
   Effectively: the name is in brand register (bold + ink), the role is
   in nav-link register (medium + mid), and they sit together as one
   phrase via the dot separator. */
nav.top .brand .brand-role{
  font-weight:500;
  color:var(--mid);
  margin-left:.25em;
  /* No hover transition either — brand block is fully static. */
}

nav.top ul.nav-links{
  list-style:none;
  display:inline-flex;
  align-items:center;
  gap:22px;
}
/* LI needs explicit flex centering. Without this, the list item is a generic
   flex child whose anchor sits at its content-box top instead of vertically
   centered. This was the real source of the alignment drift — links sitting
   "low" inside the pill. */
nav.top ul.nav-links li{
  display:inline-flex;
  align-items:center;
  line-height:1;
}
nav.top .nav-links a{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:12px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mid);
  line-height:1;
  display:inline-flex;align-items:center;gap:7px;
  position:relative;
  transition:color .25s var(--ease);
}
nav.top .nav-links a:hover{color:var(--ink);}
nav.top .nav-links a .d{
  width:4px;height:4px;border-radius:50%;
  background:var(--ink);
  opacity:0;transform:scale(.4);
  transition:opacity .3s var(--ease), transform .35s var(--ease);
}
nav.top .nav-links a.is-current{color:var(--ink);}
nav.top .nav-links a.is-current .d{opacity:1;transform:scale(1);}

/* =========================================================================
   Hero (homepage)
========================================================================= */
/* Homepage main container. box-sizing override so max-width caps the
   CONTENT width (1750px); padding is added outside that, per the spec. */
main{
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--pad-x);
  box-sizing: content-box;
}

section.hero{
  height:100vh;min-height:100vh;
  position:relative;
  display:flex;align-items:flex-start;
  padding-top:26vh;
}
section.hero h1{
  font-family:'Antonio',sans-serif;
  font-weight:900;text-transform:uppercase;
  font-size:clamp(54px, 10.2vw, 168px);
  line-height:1;letter-spacing:-0.018em;
  max-width:14ch;
  color:var(--ink);
}
section.hero h1 .line{display:block;}

h1 .accent, h2 .accent{color:var(--accent-2);}

/* =========================================================================
   Transition bands
========================================================================= */
section.bands{
  padding:max(20px, 2.5vh) 0 max(48px, 6vh);
  margin-left:calc(-1 * var(--pad-x));
  margin-right:calc(-1 * var(--pad-x));
}
.bands .band{
  position:relative;
  overflow:hidden;
  padding:30px 0;
}
.bands .band[data-dir="left"]{
  background:var(--accent);
  /* Atmospheric but readable. Was bumped down to .35 (too ghosted), now at
     .65 — gives the text presence and legibility while still keeping the
     pink/crimson fill as the dominant moment. Hover resolves to full opacity. */
  color:rgba(20, 17, 15, .65);
  z-index:2;
  box-shadow:
    0 24px 52px -16px rgba(0, 0, 0, .62),
    0 10px 20px -6px  rgba(0, 0, 0, .45),
    0 2px 5px   -1px  rgba(0, 0, 0, .28);
}
.bands .band[data-dir="left"] .statement:hover{color:var(--ground);}
.bands .band + .band{margin-top:22px;}
.bands .band[data-dir="right"]{
  background:var(--paper-2);
  /* Yellow copy on the secondary band — uses --accent-2 (NOT -strong) so the
     band text matches the hero/H2 highlight color exactly. Previously used
     --accent-2-strong which was a darker yellow and read "aggressive" next
     to the lighter buttery yellow in the hero. */
  color:var(--accent-2);
  z-index:1;
  box-shadow:
    0 14px 30px -12px rgba(0, 0, 0, .50),
    0 4px 10px -3px  rgba(0, 0, 0, .30);
}
/* Hover color shift removed — interaction is now opacity-based only (handled
   in the .bands .statement rules below). Keeps the band hover consistent
   with the primary band's pattern. */
.bands .band-track{
  display:inline-flex;
  align-items:center;
  white-space:nowrap;
  will-change:transform;
}
.bands .statement{
  /* Anton (NOT Antonio) — bands are marquee/signage by intent. Anton's
     idiosyncratic letterforms read as "decoration first"; Antonio is
     too well-mannered for this job. Every other title on the page still
     uses Antonio for legibility — bands are the one stylized exception. */
  font-family:'Anton',sans-serif;
  font-weight:400;
  /* Dialed back from clamp(40, 5.8vw, 88) — bands were too focal at the
     prior scale, swallowing visual attention away from the hero + work.
     At ~60% of the prior size they read as marquee punctuation, more
     phrases visible per band, and the bands themselves hug tighter. */
  font-size:clamp(24px, 3.4vw, 52px);
  line-height:1;
  letter-spacing:-0.008em;
  text-transform:uppercase;
  /* Padding carries the rhythm between statements now that the bullet
     separators are gone. Tightened from 40 → 28 to match the smaller scale. */
  padding:0 28px;
  cursor:default;
  transition:color .3s var(--ease), opacity .3s var(--ease);
}
/* Secondary band — atmospheric opacity at rest, resolves on hover. Mirrors
   the primary band's "ghosted → full opacity" pattern so both bands work
   the same way (instead of secondary jumping to a different accent color
   on hover, which felt disjointed). Bumped rest from .65 → .85: against
   the warmer --paper-2, the accent color reads under-present at .65 — the
   text needs more weight for the band to feel like a deliberate moment. */
.bands .band[data-dir="right"] .statement{
  opacity: 0.85;
}
.bands .band[data-dir="right"] .statement:hover{
  opacity: 1;
}

/* =========================================================================
   Work (homepage)
========================================================================= */
section.work{padding:max(64px, 7vh) 0 max(110px, 13vh);}
section.work .work-head{
  text-align:center;margin:0 auto max(84px, 10vh);
}
section.work .work-head h2{
  font-family:'Antonio',sans-serif;font-weight:900;text-transform:uppercase;
  font-size:clamp(36px, 5.4vw, 92px);
  line-height:1;letter-spacing:-0.015em;
  max-width:22ch;margin:0 auto;
  color:var(--ink);
}
section.work .work-head h2 .line{display:block;}

/* Old cursor-follow preview-card system retired — replaced by row-anchored
   preview below. JS still runs harmlessly on the hidden element. */
.preview{display:none !important;}

.list{
  position:relative;       /* anchor for .row-preview absolute positioning */
  max-width:1400px;
  margin:0 auto;
  /* Fixed widths for title and pills columns so all 4 rows align. Title
     column narrowed from 520 → 460 max to free up ~60px of middle-gutter
     space and let the image breathe. "DIAGNOSTIC PANEL" (longest title)
     still fits on one line at 460px; the longest description wraps to
     2 lines cleanly. The dead space BETWEEN title and pills is the
     hover-image gutter. */
  --row-title-w: clamp(320px, 32vw, 460px);
  --row-pills-w: clamp(180px, 16vw, 240px);
}
.row{
  display:grid;
  /* Four columns: number / title-body / image gutter (flex) / pills.
     The number is back — it provides a small left-side editorial anchor
     that balances the pills on the right, and reads as a curated chapter
     marker ("01 AI Coworker") rather than just a title in isolation. */
  grid-template-columns:60px var(--row-title-w) 1fr var(--row-pills-w);
  gap:36px;
  align-items:center;
  padding:28px 24px;
  position:relative;
  border-radius:10px;
  /* Asymmetric transitions — fast IN, slow OUT. When hover engages
     (.row:hover rule below), bg & text use the .2s in transition so the
     row snaps into focus. When hover releases, this base rule's .5s
     out transition is used, so the row lingers — feels like the page is
     releasing the row instead of dropping it.
     Original symmetric values (for revert): background .4s, color .35s */
  transition:
    background .5s cubic-bezier(.4, 0, .2, 1),
    color .5s cubic-bezier(.4, 0, .2, 1);
}
/* Row dividers removed — the row is now defined by its typography
   (row number left-anchor, pills right-anchor, 28px vertical
   padding rhythm) plus the accent bar on hover. The list reads as
   editorial typographic column instead of a "table of rows."
   ::before and ::after are both free; ::after is reused below for
   the hover/focus bar.
   Original divider rules removed for revert reference:
     .row::before { hairline at top:0 inset 10px, opacity ~40-80% of --rule }
     :root[data-theme="light"] .row::before { 80% --rule mix }
     .row:last-of-type::after { hairline at bottom:0 inset 10px } */
/* Hover/focus bar removed — once the row gained the accent-soft pink
   wash on hover, the bar started reading as redundant pink chrome
   (the wash already signaled "row in the pink family"). Bar
   vocabulary still lives on .v2-callout in the case studies, so the
   "pink bar marks a focus moment" pattern is preserved system-wide;
   the work-section hover trades the bar for the wash as its
   activation gesture.
   To restore: re-add .row::after { transform:scaleY(0); ... } base
   and the .row:hover::after { transform:scaleY(1); ... } trigger. */
/* Row hover — mode-specific activation patterns. Each mode uses the
   vocabulary that lands in that mode's color space:
     Dark mode (base):
       paper-2 warm-dark surface fill + pink title + pink arrow.
       The dark "card pulled forward" lights up with pink content —
       neutral surface, typographic activation color.
     Light mode (override below):
       accent-soft pink wash fill + ink title + ink arrow.
       A pink frame around the row carries the activation; the
       title stays sharp black for contrast.
   These ARE different rules — not a token-inversion of the same
   pattern. Dark and light have different visual physics, and the
   "activation moment" lands via different elements in each.
   System consistency lives at the token layer (paper-2, accent-soft,
   ink, accent-strong all resolve per-mode); the COMPOSITION of
   those tokens differs intentionally to preserve the right feel
   in each mode. Asymmetric transitions: .2s in, .5s out (latter
   from .row base). */
.row:hover, .row.is-focused{
  /* Final hover state — consistent rule across modes via --paper-2:
       Dark: #322F2E (warm dark, lighter than --ground — "lifts")
       Light: #EBEAE5 (warm tan, darker than --ground — "presses
              gently into the page")
     The lift direction inverts per mode by happy accident of token
     values. Light mode is more subtle than dark by nature of the
     color space; the pink title + arrow (via inherit) carry the
     primary activation moment in both. Tested --paper (pure white)
     in light mode for a true lift, but the contrast was so minimal
     it disappeared against --ground (#FBFAF6). Paper-2 wins for
     visibility. Asymmetric transitions: .2s in, .5s out (latter
     from .row base). */
  background:var(--paper-2);
  transition:
    background .2s cubic-bezier(.4, 0, .2, 1),
    color .2s cubic-bezier(.4, 0, .2, 1);
}
.row:hover h3,
.row.is-focused h3{
  color:var(--accent-strong);
}

/* Number column — muted at rest so the pink is reserved for activation
   moments. Shifts to pink on hover in lockstep with the title. Smaller +
   tracked letter-spacing (14px / .04em) matches quiet's "running editorial
   number" treatment — reads as a book chapter marker rather than a UI list
   index. */
.row-num{
  font-family:'Antonio',sans-serif;
  font-weight:700;
  /* 14px — pulled down from 18 so the number reads as a quiet running
     page-folio rather than a labeled list marker. At 18 the number was
     visually competing with the description (15-17px). At 14, it sits
     as the smallest text element in the row, anchoring the left edge
     editorially without claiming attention. */
  font-size:14px;
  letter-spacing:.04em;
  color:var(--mid);
  line-height:1;
  /* Vertically center against the title + description block. */
  align-self:center;
  transition:color .35s cubic-bezier(.4, 0, .2, 1);
}
/* Number on hover regains opacity to --ink rather than activating to
   accent. Same logic as the pills: pink is reserved for the title and
   arrow (the focal activation signals). Number and pills both "brighten"
   to ink on hover — they become more readable without competing with
   the title for focal status. Cleaner hover hierarchy:
     pink (title + arrow) = "this is activated, click here"
     ink (number + pills) = "supporting content, more readable now"
     mid (description) = stays at its always-readable level */
/* Number on the hovered row — brightens to --ink. With the paper-2
   sibling-shade fill, normal text colors are readable so we don't
   need a cutout override; the brightness shift signals "supporting
   content of an active row." */
.row:hover .row-num,
.row.is-focused .row-num{
  color:var(--ink);
}

/* Title + description stack inside the content column. Gap at 14 —
   the previous 8 read as too cramped (title and description were on top
   of each other). 14 is the sweet spot: tight enough to read as a unit
   (marquee + dek), spaced enough that the description has its own beat.
   The prior compound spacing (12 gap + 14 margin = 26 total) was way
   too loose; this single 14 replaces both. */
.row-body{
  display:flex; flex-direction:column;
  gap:14px;
  align-self:center;
}
/* Bar markup kept (not removed) so it's easy to restore if you want it
   back. Currently inactive — the animation trigger below is commented out,
   so the bar stays at scaleY(0) (invisible) regardless of hover.

   Hover signaling is now carried by:
   1. Title color shift to pink
   2. ↗ arrow appearing after the period (editorial clickability cue)
   3. Floating preview card
   The bar would be a fourth signal, and given the arrow is more editorial-
   convention and works in the spot the eye is already looking, it earned
   the cut. */
/* Vestigial bar ::before rule removed — it was overriding the new divider
   ::before that creates the straight hairline between rows. The bar was
   inactive anyway (scaleY(0)), but its later position in the cascade meant
   it was winning specificity and replacing the divider. */
/* Bar animation trigger disabled — the ↗ arrow + title color shift carry
   the hover signaling. To restore, uncomment the rule below.

   .row:hover::before, .row.is-focused::before{
     animation: barIn .5s var(--ease-soft) forwards;
   }
*/
@keyframes barIn{
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* h3 is the editorial "marquee" label — short concept noun (2-3 words),
   not a descriptive sentence. The actual descriptive sentence lives in
   .row-desc below. */
.row h3{
  font-family:'Antonio',sans-serif;font-weight:800;text-transform:uppercase;
  font-size:clamp(28px, 3.6vw, 60px);
  line-height:1;letter-spacing:-0.012em;
  color:var(--ink);
  transition:color .35s cubic-bezier(.4, 0, .2, 1);
  margin:0;
}

/* Descriptive sentence below the label — Inter sentence case, calm
   weight, muted color so it doesn't compete with the Anton label.
   Reads as a "dek" beneath the marquee headline. Sized at clamp(15-17px)
   — meaningfully smaller than the original 15-18 (sharpens marquee/dek
   hierarchy) but bumped up from a brief 14-16 experiment which dipped
   into "caption" territory and read as too quiet. */
.row-desc{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:clamp(15px, 1.1vw, 17px);
  line-height:1.45;
  letter-spacing:-0.003em;
  color:var(--mid);
  /* margin removed — was a leftover 14px top-margin from pre-flex layout
     that compounded with .row-body's gap. .row-body gap is the single
     source of truth for title→description spacing now. */
  margin:0;
  max-width:55ch;
  transition:color .3s var(--ease);
}

/* "↗" clickability indicator — appended to each row title via JS. Sized
   in em so it scales with the title. Container 0.65em + 80%-fill SVG
   gives a visible arrow ~0.52em — noticeably smaller than Anton's cap
   height (~0.72em), reading as a delicate punctuation mark rather than
   a chunky icon.

   Animation: snappy slide-and-fade. Arrow starts offset down-left + invisible,
   snaps into final position with a sharp ease-out (220ms). Reads decisive,
   like a stamp landing — not tentative like a draw-in. The direction of
   the snap (up-right) matches what the arrow points to, reinforcing "go." */
.row-arr{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:0.65em;
  height:0.65em;
  margin-left:0.12em;
  /* Inherits color from the parent h3 (was hard-set to --accent-strong
     pink). With the title staying at --ink on hover, a pink arrow next
     to a black title read as disconnected — like two separate
     activation signals. Inheriting unifies title + arrow into one
     editorial unit; the pink moment lives entirely in the wash. */
  color:inherit;
  opacity:0;
  transform:translate(-5px,5px);
  /* Synced with the rest of the hover event: same Material Standard curve,
     duration scaled to .3s (smallest change → shortest duration). Previously
     used a custom snap curve at .22s which made the arrow "pop" out of
     rhythm with everything else. */
  transition:opacity .3s cubic-bezier(.4, 0, .2, 1),
             transform .3s cubic-bezier(.4, 0, .2, 1);
}
.row-arr svg{
  width:100%;height:100%;display:block;
}
.row:hover .row-arr,
.row.is-focused .row-arr{
  opacity:1;
  transform:translate(0,0);
}
/* Row pills column — 3 small text labels stacked vertically, right-aligned
   to the row's right edge. grid-column:4 explicitly places the pills DIV
   in the 4th grid column (the var(--row-pills-w) track) — otherwise grid
   auto-flow would put it in column 3 (the 1fr image gutter) and the pills
   would float in the middle. align-items:flex-end then pushes each span
   to the right edge of that 4th column. */
.row-pills{
  grid-column:4;
  display:flex;
  flex-direction:column;
  align-items:flex-end;
  gap:6px;
  align-self:center;
  white-space:nowrap;
}
.row-pills span{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-weight:600;
  font-size:11px;
  letter-spacing:.08em;
  text-transform:uppercase;
  /* Pills are taxonomy/annotation, not content — should read as the
     lightest element in the row's resting hierarchy (lighter than the
     number and description, both at --mid). 65% derived from --mid via
     color-mix keeps the brand color system intact while positioning
     pills correctly in editorial register. Hover state below still
     shifts to full accent so activation reads as a clear color step. */
  color:color-mix(in oklab, var(--mid) 65%, transparent);
  transition:color .35s cubic-bezier(.4, 0, .2, 1);
}
/* On hover, pills "regain opacity" to --ink rather than activating to
   accent. The accent (pink) is reserved for the title — it's the focal
   signal of "this row is activated." Pills moving to ink means they
   become readable taxonomy without competing with the title for the
   focal color. Effectively a color swap: title takes the activation
   color, pills take on what the title's rest color is. */
/* Pills on the hovered row — brighten to --ink. Same activation
   pattern as .row-num. */
.row:hover .row-pills span,
.row.is-focused .row-pills span{
  color:var(--ink);
}

/* =========================================================================
   Row-anchored hover image preview — replaces the prior cursor-follow card.
   Positioned absolutely inside .list, JS sets `top` to the vertical center
   of the currently hovered row. Visible only on hover; slides between rows
   for the active-row-anchored feel.
========================================================================= */
.row-preview{
  position:absolute;
  top:0;
  /* Bounded by the middle gutter — left bound is the end of the title
     column (+ gap); right bound is the start of the pills column (+ gap).
     Wrapper auto-sizes to its content (no width/aspect-ratio); the img
     inside centers horizontally within the gutter via flex. */
  left:calc(24px + 60px + 36px + var(--row-title-w) + 36px);
  right:calc(24px + var(--row-pills-w) + 36px);
  display:flex;
  justify-content:center;
  align-items:center;
  margin:0 auto;
  pointer-events:none;
  opacity:0;
  /* Y-translate centers the image vertically on the JS-set top (row's
     vertical center). */
  transform:translateY(-50%) scale(.97);
  z-index:5;
  will-change: top, transform, opacity;
  transition:
    opacity .25s ease,
    transform .35s cubic-bezier(.2, .8, .2, 1),
    top .5s cubic-bezier(.2, .8, .2, 1);
}
.row-preview.visible{
  opacity:1;
  transform:translateY(-50%) scale(1);
}
/* Container-less treatment: the img IS the preview. No card chrome,
   no background, no stroke. Border-radius rounds the asset's 90° source
   corners; box-shadow gives it lift off the page. Brand identity and
   composition come entirely from the artifact itself. */
.row-img{
  height:240px;
  width:auto;
  max-width:100%;
  border-radius:10px;
  display:block;
  /* Dark-mode default. Shadow needs to lift the asset off near-black
     page; alpha is high so the silhouette reads even when asset bg is
     also dark. */
  box-shadow: 0 32px 72px rgba(0, 0, 0, .60);
}
:root[data-theme="light"] .row-img{
  /* Stronger lift than the original .14 — light-bg assets were
     dissolving into the cream page. .26 gives visible edge without
     reading as a "sticker" shadow. */
  box-shadow: 0 26px 60px rgba(0, 0, 0, .26);
}

/* Per-project background fills removed — the preview card is now a
   stroke-only frame (background:var(--ground), border:1px solid
   var(--rule), both set on .row-preview base above). Brand identity
   moves to the image content. The JS still sets data-row on the
   preview element (used elsewhere; harmless), but no CSS overrides
   it visually anymore. */

/* Pills — standardized with case study styling.
   - Title case (matches projects.js data + case study render)
   - Lower letter-spacing (no uppercase = no compensation needed)
   - Filled pill uses inverted-contrast pattern: bg=var(--ink),
     text=var(--ground). In dark mode this is light pill / dark text
     (max legibility against dark page); in light mode it flips to
     dark pill / light text. Same approach as case study --v2-pill-bg. */
.pill{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:0;
  padding:6px 12px 5px;border-radius:999px;
  color:var(--mid);
  border:1.25px solid var(--rule-strong);
  background:transparent;
}
.pill.filled{
  background:var(--ink);
  color:var(--ground);
  border-color:var(--ink);
  font-weight:600;
}

/* Focus outline removal — the bg fill + title color shift carry the
   focused-row signal; the default browser focus ring would compete. */
.row.is-focused{outline:none;}
/* Description on the hovered row — brightens to --ink, joining the
   "supporting content readable" pattern of .row-num and pills. */
.row:hover .row-desc,
.row.is-focused .row-desc{
  color:var(--ink);
}

/* =========================================================================
   Preview (homepage hover)
========================================================================= */
.preview{
  /* Wrapper only — children are individually fixed-positioned.
     Mobile hides this whole tree via the responsive rule below. */
  pointer-events:none;
}
.preview-card{
  position:fixed;top:0;left:0;
  /* 4:3 aspect ratio. 440×330 is roughly 12% smaller than the prior 500×375
     and reads as a comfortable "card on the right side" rather than a
     dominating panel. */
  width:440px;height:330px;
  border-radius:10px;
  overflow:hidden;
  opacity:0;
  pointer-events:none;
  z-index:40;
  will-change:transform, opacity;
  box-shadow:
    0 28px 60px -18px rgba(0,0,0,.62),
    0 12px 24px -6px  rgba(0,0,0,.42),
    0 3px 6px  -1px  rgba(0,0,0,.28);
}
/* Light-mode shadow override — the dark-mode shadow stack (.62/.42/.28)
   was sized for high contrast against #181818. On cream, the same opacities
   read as harsh smudges. Drop opacities by ~75% and shift hue toward the
   page's ink so the elevation feels soft and warm. */
:root[data-theme="light"] .preview-card{
  box-shadow:
    0 24px 48px -20px rgba(20, 17, 15, .14),
    0 10px 20px -6px  rgba(20, 17, 15, .10),
    0 3px 6px  -1px   rgba(20, 17, 15, .06);
}
.preview-card .card-fill{
  position:absolute;inset:0;
  background:var(--accent);
  transition:background .55s var(--ease);
}

/* =========================================================================
   About (homepage)
========================================================================= */
section.about{
  /* Viewport-tall so the about content gets a SOLO moment on screen —
     no other section competing for attention before the manifesto
     reveal zone enters from below. min-height (not height) so the
     section can grow if content is taller than the viewport (e.g.,
     long accordion items, smaller viewports). */
  min-height:100vh;
  padding:max(100px, 12vh) 0 max(100px, 12vh);
  display:grid;grid-template-columns:1.2fr 1fr;gap:80px;
  /* Top-aligned within the row: both columns anchor at the top of the
     row. When the accordion is all-collapsed, empty space accumulates
     at the bottom of the COLUMN (clearly "user collapsed it") instead
     of surrounding it on both sides. */
  align-items:start;
  /* Center the grid row VERTICALLY in the viewport-tall section so the
     content lands as a centered solo moment. align-items still keeps
     the two columns top-aligned to each other inside the row. */
  align-content:center;
}

/* Used by the measurement JS below to suppress accordion-body transitions
   while we're temporarily toggling items open/closed to measure heights.
   Without this, the transition would fire during measurement and the
   measured value would not reflect the final state. */
/* Suppress ALL transitions inside a set during measurement, not just the
   acc-body grid transition. Without this, the padding transition I added
   on .acc-body .inner could race the measurement and yield a wrong height
   (which breaks the min-height pin and re-introduces the left-column shift). */
.acc-set.measuring,
.acc-set.measuring *{transition:none !important;}

/* Smooth opacity transition when toggling between Professional/Personal
   accordion sets. The toggle slider animates ~350ms; without this, the
   accordion content swaps instantly while the slider glides — feels
   abrupt. 200ms ease ties the two animations together visually. */
.acc-set{transition:opacity .2s ease;}
/* No inter-item margin — items separate via the hairline border below. */
section.about h2{
  font-family:'Antonio',sans-serif;font-weight:900;text-transform:uppercase;
  font-size:clamp(34px, 4.9vw, 84px);
  line-height:1;letter-spacing:-0.018em;
  max-width:22ch;
  color:var(--ink);
}
section.about h2 .line{display:block;}

/* ============= About metadata block (left column under H2) =============
   Editorial masthead pattern — label + small facts. Three groups in a row
   on desktop, stacks gracefully at narrower widths. */
/* gap bumped 48 → 72 — metadata was reading too close to the H2.
   Now there's a clear breath of whitespace between the title and the
   facts block, so they don't visually fuse. */
.about .left{ display:flex; flex-direction:column; gap:72px; }
.about .right{ display:flex; flex-direction:column; gap:32px; }

.about .meta{
  display:grid;
  /* Proportional columns instead of equal 1fr/1fr/1fr. Disciplines holds
     "Information Architecture" (24ch) and Currently holds "Consulting on
     complex products" (28ch), so they need more room; Tools' items are
     short by design (Figma / Claude / FigJam, ~6ch each) and reads better
     as a concentrated middle than as a half-empty equal-width column. */
  grid-template-columns:1.5fr 1fr 1.5fr;
  gap:32px;
  align-items:start;
  /* Narrowed so the metadata block doesn't lean all the way into the
     accordion's territory — leaves visible whitespace on the right edge. */
  max-width:78%;
  /* Small left inset so the small metadata text sits tucked inside the H2
     column rather than starting flush with the H2's left edge. Without
     this the small text reads as visually "more left" than the giant H2
     above — even though both share the same x-coordinate — because the
     H2's optical mass anchors the column edge more strongly than the
     thin metadata text does. */
  padding-left:12px;
}
.about .meta-group{
  display:flex; flex-direction:column;
  gap:12px;
}
.about .meta-label{
  font-family:'Plus Jakarta Sans',sans-serif;
  /* Slightly smaller + tighter letter-spacing — metadata steps back from
     "competing content" to "supporting info." Hairline underline removed
     to reduce the section's line-driven density (toggle line + accordion
     hairlines were already a lot). Spacing + uppercase tracking carries
     the label-vs-items distinction. */
  font-size:10px; font-weight:600;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--mid);
}
.about .meta-list{
  list-style:none; padding:0; margin:0;
  display:flex; flex-direction:column;
  gap:6px;
}
.about .meta-list li{
  font-family:'Plus Jakarta Sans',sans-serif;
  /* Smaller body text matches the smaller label — supporting role. */
  font-size:13px; font-weight:500;
  letter-spacing:-0.003em;
  color:var(--ink);
  line-height:1.45;
}

.toggle{
  position:relative;display:inline-flex;align-items:flex-end;
  /* Editorial underline pattern — no pill, no chrome. Two text buttons sit
     inline with a small gap; the pink "slider" becomes a thin line beneath
     the active button. A faint full-width track below signals the toggle's
     clickable extent (otherwise it could read as decorative text). */
  gap:28px;
  padding:0 0 6px 0;
  background:transparent;
  border:0;
  border-radius:0;
}
/* Faint full-width track — sits at the bottom edge across both buttons,
   so the toggle reads as a clickable strip rather than just a pink mark. */
.toggle::after{
  content:'';
  position:absolute;
  bottom:0; left:0; right:0;
  height:1px;
  background:var(--rule-strong);
  pointer-events:none;
  z-index:0;
}
/* Center the toggle horizontally in the right column instead of left-aligned. */
.about .right .toggle{ margin-top:0; align-self:center; }
.toggle .slider{
  position:absolute;
  /* Pinned to the bottom as a thin underline instead of a pill behind text.
     Width + X position still set by JS (offsetWidth + offsetLeft of the
     active button), so the underline matches each button's width exactly. */
  top:auto; bottom:0; left:0; width:0;
  height:2px;
  background:var(--accent-strong);
  border-radius:0;
  transition:transform .42s var(--ease-soft), width .42s var(--ease-soft);
  z-index:1;
}
.toggle button{
  position:relative;z-index:2;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:12px;letter-spacing:.075em;text-transform:uppercase;
  /* No horizontal padding — buttons hug their text width, the toggle's
     `gap:28px` handles the spacing between them. Vertical padding gives
     the underline a small distance from the text baseline. */
  padding:6px 0;
  border-radius:0;
  background:transparent;
  color:var(--mid);
  transition:color .25s var(--ease);
}
.toggle button:hover{color:var(--ink);}
.toggle button[aria-pressed="true"]{color:var(--ink);}

.acc{}
/* Quiet-style accordion (A/B against the Pinch-style):
   - No hairline dividers — items separate by margin + soft pink fill on hover/open
   - Titles ink at rest, shift to pink on open
   - Hover: pink soft fill (accent-soft)
   - Open: pink soft fill (sticky from hover) + pink title + rotated icon
   - Rounded corners on the fill, horizontal padding so it doesn't bleed all
     the way to the column edges */
.acc-item{
  border-radius:10px;
  transition:background .25s var(--ease);
}
/* Small breath between items so adjacent open/hover fills don't touch. */
.acc-item + .acc-item{ margin-top:2px; }
.acc-item .acc-head{
  width:100%;text-align:left;
  display:flex;justify-content:space-between;align-items:center;
  padding:16px 20px;
  font-family:'Antonio',sans-serif;font-weight:800;text-transform:uppercase;
  font-size:clamp(17px, 1.7vw, 22px);
  letter-spacing:-0.005em;line-height:1;
  color:var(--ink);
  background:transparent;
  border:0;
  border-radius:10px;
  cursor:pointer;
  transition:color .3s var(--ease), background .25s var(--ease);
}
.acc-item .acc-head:hover{ background:var(--accent-soft); }
.acc-item.open .acc-head{
  color:var(--accent-strong);
  background:var(--accent-soft);   /* sticky fill from hover */
}

/* Icon: mid (muted) at rest, pink on open. + → × rotation preserved.
   Switched from text "+" (Plus Jakarta Sans) back to SVG because the text
   glyph's bounding box ≠ its optical center, so rotate(45deg) swung the
   character around an off-center pivot instead of spinning it in place.
   SVG with a symmetric viewBox rotates around its true geometric center.
   Bonus: stroke-linecap:round gives soft round line ends (the prior text
   "+" had blunt rectangular terminals — too cut-off against the page's
   softer round elements). */
.acc-icon{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:20px;height:20px;
  color:var(--mid);
  flex:0 0 auto;
  transform-origin:center;
  transition:transform .3s var(--ease), color .3s var(--ease);
}
.acc-icon svg{
  width:18px;
  height:18px;
  display:block;
}
.acc-item.open .acc-icon{
  transform:rotate(45deg);
  color:var(--accent-strong);
}

.acc-item .acc-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .5s cubic-bezier(.22, 1, .36, 1);}
.acc-item .acc-body > div{overflow:hidden;}
.acc-item.open .acc-body{grid-template-rows:1fr;}
.acc-item .acc-body .inner{
  /* Horizontal padding (20px) matches the title's horizontal padding so
     the body text aligns with the title text inside the fill. */
  padding:0 20px;
  color:var(--mid);
  font-size:15px;line-height:1.7;max-width:54ch;
  transition:padding .5s cubic-bezier(.22, 1, .36, 1);
}
.acc-item.open .acc-body .inner{
  padding:8px 20px 22px;
}
.acc-item .acc-body .inner strong{color:var(--ink);font-weight:500;}

/* =========================================================================
   Principles — layered scroll model (back layer + reveal zone)

   Mental model: the page has been in front of another page the whole time.
   The principles text lives on the BACK layer, position:fixed in the
   viewport background, behind everything. <main> and <footer> are the
   FRONT layers — opaque, scrolling normally on top of the back layer
   and hiding it. Between </main> and <footer> sits a transparent
   .principles-reveal-zone — a scroll spacer with no background, so
   during its scroll range the front is out of the way and the back
   (which has been there all along) is visible.

   Scroll experience:
     1. Top of page → end of about. Main covers back layer. Principles hidden.
     2. Past about → reveal zone scrolls under viewport. No front layer
        covering anything. Back layer (principles text on var(--paper-2)
        surface) becomes visible. JS rotates spotlight through 6 lines.
     3. Footer enters viewport from below. Footer is opaque, covers back
        layer. Principles disappears (still there, just hidden again).
========================================================================= */

/* Back layer — position:fixed, full viewport, behind everything.
   The deboss tokens are scoped here so the principle text styles below
   resolve correctly regardless of theme.

   The back layer's BG (paper-2 surface) stays static the whole time.
   Only the LIST (manifesto text) translates upward during exit — see
   .principles-back .principles-list below. The footer (layer 1) rises
   into viewport from below and covers the back-layer bg naturally via
   z-index, so the user perceives the footer literally passing over
   the paper-2 surface while the text lifts off out of its way. */
.principles-back{
  position:fixed;
  inset:0;
  z-index:0;
  /* --paper-2 sibling shade — slightly lighter than ground in dark,
     slightly darker in light. Reads as "another surface" without
     overriding the theme choice. */
  background:var(--paper-2);
  display:flex;
  align-items:center;
  justify-content:center;
  pointer-events:none;

  /* Letterpress deboss tokens. Dark mode default (dark bg recipe). */
  --principles-text-color:color-mix(in oklab, var(--paper-2) 80%, var(--ink) 20%);
  --principles-text-shadow:
    0 1px 0 rgba(255, 255, 255, 0.25),
    0 -1px 0 rgba(0, 0, 0, 0.85);
}
:root[data-theme="light"] .principles-back{
  /* Light-bg recipe — darker text, prominent white rim below, gentler
     dark shadow above. */
  --principles-text-color:color-mix(in oklab, var(--paper-2) 85%, var(--ink) 15%);
  --principles-text-shadow:
    0 2px 0 rgba(255, 255, 255, 0.85),
    0 -1px 0 rgba(0, 0, 0, 0.30);
}

.principles-back .principles-list{
  margin:0;
  padding:0 var(--pad-x);
  list-style:none;
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:clamp(10px, 2vh, 32px);
  text-align:center;
  /* Exit translation only — JS sets --manifesto-y during the last 25vh
     of the reveal zone so the text lifts upward and out of the viewport
     top as the footer rises in to cover the back layer behind it. */
  transform:translate3d(0, var(--manifesto-y, 0px), 0);
  will-change:transform;
}

/* Reveal zone — transparent scroll spacer between </main> and <footer>.
   190vh sized so the footer arrives shortly after the spotlight ends —
   the manifesto-text exit happens DURING the footer's rise-in, not
   before it. No dead-time on the back layer.
   Budget breakdown (relative to scroll past main exit):
     0   → 5vh    : beat after main leaves (no spotlight yet)
     5   → 80vh   : spotlight wave through 6 lines (~12.5vh per line)
     80  → 90vh   : settle beat (short)
     90vh         : footer's top edge crosses viewport bottom — rise-in begins
     90  → 130vh  : manifesto-text exit (translateY upward), overlaps with footer rising */
.principles-reveal-zone{
  height:190vh;
  background:transparent;
  position:relative;
  z-index:1;
  pointer-events:none;
}

/* Front layers — opaque backgrounds + z-index above the back layer.
   These are the "front page" that covers the principles text until the
   user scrolls past about / before the user reaches the footer.

   Rounded bottom corners + subtle bottom shadow make main look like
   a literal "front page" / card sitting on top of the back layer. As
   the user scrolls past, the curved edges visibly lift off, exposing
   the paper-2 back layer beneath. Reinforces the page-on-top-of-page
   metaphor explicitly. Same surface vocabulary as the case study hero
   card (both use rounded corners + paper-2 surface relationship). */
main{
  position:relative;
  z-index:2;
  background:var(--ground);
  border-radius:0 0 28px 28px;
  /* Soft shadow at the bottom edge — strengthens the "lifted card"
     read. Subtle in both themes so it doesn't dominate. */
  box-shadow:0 24px 48px -16px rgba(0,0,0,.35);
}
footer{
  position:relative;
  z-index:2;
  /* footer's own rule (further down) sets background and padding.
     Top corners rounded outward to mirror main's bottom corners — both
     front-page cards on the back layer (the manifesto). Soft shadow
     above the top edge punctuates the "card rising from below" read.
     Dark-mode default; light-mode override below uses a much softer
     value because cream + paper-2 surfaces don't have enough tonal
     gap to support a strong shadow. */
  border-radius:28px 28px 0 0;
  box-shadow:0 -20px 40px -16px rgba(0,0,0,.22);
}
:root[data-theme="light"] footer{
  box-shadow:0 -12px 28px -12px rgba(0,0,0,.08);
}

.principle{
  margin:0;
  font-family:'Anton',sans-serif;
  font-weight:400;
  text-transform:uppercase;
  font-size:clamp(34px, 5.2vw, 84px);
  line-height:1;
  letter-spacing:-0.012em;
  white-space:nowrap;
  /* DEFAULT — letterpress deboss via the classic two-shadow technique.
     Lifted same-family text color + light shadow below + dark shadow
     above reads as carved into the back-layer surface. */
  color:var(--principles-text-color);
  text-shadow:var(--principles-text-shadow);
  /* Asymmetric transitions — the SLOW transition (used when leaving
     .is-current) lets each line linger in pink as it un-lights, so at
     fast scroll the wave reads as a cascading trail of fading lights
     instead of vanishing before the eye can catch it. */
  transition:color .45s ease, text-shadow .45s ease;
}
/* SPOTLIGHT — the line lights up with the brand accent (pink). One
   line at a time gets .is-current as the user scrolls through the
   reveal zone. The FAST transition (used when entering .is-current)
   makes the line snap to pink immediately so the wave's leading edge
   is crisp even when scroll is moving quickly. */
.principle.is-current{
  color:var(--accent);
  text-shadow:none;
  transition:color .05s ease, text-shadow .05s ease;
}
.principle-text{
  display:inline-block;
}

/* =========================================================================
   Footer (homepage and case studies)
========================================================================= */
footer{
  /* Layered scroll model: footer matches layer 1 (var(--ground), same as
     main) — the front page returns to bookend the manifesto reveal.
     Layer 1 → back layer (paper-2 manifesto) → Layer 1 (footer). */
  background:var(--ground);
  /* Padding matched to quiet ideation's footer rhythm — calm vertical
     breath above and below, scales with viewport. Quiet uses
     `clamp(80px, 12vh, 140px)` on the bottom; we mirror it top + bottom
     now that the footer is a single-column composition (no form column
     to lift heavily). */
  padding:clamp(80px, 12vh, 140px) var(--pad-x);
  position:relative;
}
/* Single-column footer content (quiet ideation pattern). h2 statement,
   then contact links, then back-to-top. Vertical gap between groups
   carries the rhythm — no chrome, no two-column form layout. */
.footer-inner{
  max-width: var(--page-max);
  margin: 0 auto;
  display:flex;
  flex-direction:column;
  gap:64px;
}
/* Back-to-top link — replaces the prior copyright stamp. Same Satoshi
   uppercase-tracked vocabulary as the form labels and other UI eyebrows,
   so it reads as part of the design system rather than a foreign utility.
   Arrow lifts on hover to reinforce the "up" action. */
.footer-totop{
  margin:0;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:.10em;text-transform:uppercase;
  /* line-height:1 tightens the link's line box to the cap height, so the
     arrow's geometric center aligns with the optical center of the uppercase
     text rather than the descender-inclusive line-box middle. */
  line-height:1;
  color:var(--deep-mid-2);
  display:inline-flex;align-items:center;gap:8px;
  cursor:pointer;
  text-decoration:none;
  transition:color .25s var(--ease), gap .25s var(--ease);
}
.footer-totop:hover{
  color:var(--ink);
  gap:10px;
}
.footer-totop .totop-arr{
  display:inline-flex;align-items:center;justify-content:center;
  color:inherit;
  transition:transform .25s var(--ease);
}
.footer-totop:hover .totop-arr{
  transform:translateY(-2px);
}
.footer-totop .totop-arr svg{display:block;}
.footer-inner h2{
  font-family:'Antonio',sans-serif;font-weight:900;text-transform:uppercase;
  /* Larger scale (quiet ideation's footer scale) so the statement reads
     as a *moment*, not a closing note. The footer should land with
     weight equal to the hero — it's the page's other anchor. */
  font-size:clamp(40px, 7vw, 112px);
  line-height:1;letter-spacing:-0.02em;
  color:var(--ink);
  margin:0;
}
.footer-inner h2 br{display:block;}
/* Footer links — quiet ideation styling with work-section arrow pattern.
   Antonio 500 (was 700: title was too dominated) so the links read as
   clickable display type without competing with the H2. Each link reserves
   space for a ↗ arrow that fades + slides in on hover (mirrors the work
   row arrow vocabulary — visual rhyme across the page). Left-inset 12px
   for the same reason the about metadata is inset — keeps the small text
   visually tucked inside the H2's column rather than reading as anchoring
   further left than the H2 above. */
.footer-links{
  display:flex;flex-direction:row;flex-wrap:wrap;
  align-items:baseline;gap:32px;
  padding-left:12px;
}
.footer-links a{
  font-family:'Antonio',sans-serif;font-weight:500;
  font-size:clamp(18px, 1.8vw, 24px);
  letter-spacing:-0.005em;
  text-transform:lowercase;
  color:var(--ink);
  text-decoration:none;
  display:inline-flex;
  align-items:baseline;
  gap:8px;
  transition:color .25s var(--ease);
}
.footer-links a:hover{color:var(--accent-strong);}
.footer-links a .link-arr{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:0.7em;
  height:0.7em;
  flex-shrink:0;
  opacity:0;
  transform:translateX(-6px);
  transition:opacity .3s var(--ease), transform .3s var(--ease);
}
.footer-links a:hover .link-arr{
  opacity:1;
  transform:translateX(0);
}
.footer-links a .link-arr svg{
  width:100%;
  height:100%;
  display:block;
}
.footer-right{display:flex;flex-direction:column;}

/* =========================================================================
   Contact form — Contained-pill design (Option 2: 12px radius)
   Inputs are discrete containers with subtle surface lift + hairline border.
   Submit is a solid accent (pink) pill at the end. The shape language is
   shared (12px radius throughout) so the form reads as one composition.
========================================================================= */
.form{display:flex;flex-direction:column;gap:24px;width:100%;position:relative;}
.field{display:flex;flex-direction:column;gap:10px;}
.field label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:12px;letter-spacing:.10em;text-transform:uppercase;
  color:var(--mid);
  transition:color .3s var(--ease);
}

/* Inputs sit as filled, rounded containers — 12px radius (Option 2). Subtle
   ink-tinted background lifts them above the deep footer surface. Padding
   sized so a 17px input renders at a comfortable ~50px tall pill. */
.field input,.field textarea{
  width:100%;
  background:var(--field-bg);
  border:1px solid var(--field-border);
  border-radius:12px;
  padding:14px 18px;
  font-family:'Plus Jakarta Sans',sans-serif;font-size:17px;color:var(--ink);
  transition:border-color .3s var(--ease), background .3s var(--ease);
}
.field input::placeholder,.field textarea::placeholder{color:var(--deep-mid-2);}

/* Hover — border + bg lift simultaneously so the field signals it's interactive
   before you click in. Suppressed when focused so accent takes precedence. */
.field input:hover:not(:focus),
.field textarea:hover:not(:focus){
  border-color:var(--field-border-hover);
  background:var(--field-bg-hover);
}

/* Focus — border shifts to accent; label color matches in lockstep via :has().
   bg also lifts slightly to reinforce "this is the active field." */
.field input:focus,.field textarea:focus{
  outline:none;
  border-color:var(--accent-strong);
  background:var(--field-bg-hover);
}
.field:has(input:focus) label,
.field:has(textarea:focus) label{
  color:var(--accent-strong);
}

.field textarea{min-height:128px;resize:vertical;line-height:1.55;}

/* =========================================================================
   Submit — Solid accent (pink) pill
   Now confidently colored because the form's contained-pill vocabulary
   gives it permission to be a discrete pop of color. Anton uppercase keeps
   the display voice. Custom SVG arrow with shortened tail (vs the wider →
   character) and tightened text-to-arrow gap (8px / 10px hover, halved from
   the prior 14px / 18px).
========================================================================= */
.form .send{
  align-self:flex-start;margin-top:14px;
  background:var(--accent);
  color:var(--ground);
  font-family:'Antonio',sans-serif;font-weight:800;text-transform:uppercase;
  font-size:18px;letter-spacing:.04em;
  padding:10px 18px;
  border:0;
  border-radius:12px;
  display:inline-flex;align-items:center;gap:8px;
  cursor:pointer;
  transition:transform .25s var(--ease), gap .25s var(--ease),
             background .3s var(--ease), opacity .3s var(--ease);
}
.form .send .arr{
  display:inline-flex;align-items:center;justify-content:center;
  color:inherit;
  /* Tiny baseline nudge so the arrow's optical center aligns with Anton's
     cap height. SVG glyphs render against the line box, not the cap line. */
  transform:translateY(-1px);
  transition:transform .25s var(--ease);
}
.form .send .arr svg{display:block;}
.form .send:hover{
  transform:translateY(-1px);
  gap:10px;
}
.form .send:hover .arr{
  transform:translateY(-1px) translateX(2px);
}

/* Success state — accent lightens slightly + arrow swaps to ✓ via JS.
   Lift translates further for "received" feedback. */
.form .send.is-sent{
  background:color-mix(in oklab, var(--accent) 84%, white 16%);
  transform:translateY(-1px);
  pointer-events:none;
}

/* Error state — desaturated/dimmed; arrow swaps to ↺ via JS so user can
   immediately re-submit. */
.form .send.is-err{
  background:var(--accent);
  opacity:.6;
}

.form .send:disabled{opacity:.55;cursor:default;}

/* Status messages — kept for aria-live announcements + supplementary
   confirmation. Mid-tone Satoshi so they don't compete with the button morph. */
.form .sent{
  margin-top:14px;
  font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;color:var(--mid);
  opacity:0;height:0;overflow:hidden;
  transition:opacity .3s var(--ease);
}
.form .sent.on{opacity:1;height:auto;}

/* =========================================================================
   Cursors
========================================================================= */
.peer{
  position:absolute;
  display:inline-block;
  pointer-events:none;user-select:none;
  z-index:150;
  will-change:transform;
}
.peer.you{position:fixed;z-index:200;}

.peer .ptr{
  display:block;
  width:22px;height:24px;
  filter:drop-shadow(0 2px 3px rgba(0,0,0,.30));
}

.peer .name{
  position:absolute;left:14px;top:18px;
  padding:5px 11px 6px;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:12.5px;letter-spacing:0;line-height:1.2;
  /* Rounded rectangle (was 999px fully-rounded pill) — matches the
     "rectangle with softened edges" vocabulary used elsewhere on
     the site. 6px is the sweet spot: clearly rectangular but soft
     enough not to read as a hard button/tag. */
  border-radius:6px;
  white-space:nowrap;
  box-shadow:0 2px 6px rgba(0,0,0,.25);
}

.peer.shayna .ptr path{fill:var(--accent);}
.peer.shayna .name{background:var(--accent);color:var(--ground);}
.peer.you .ptr path{fill:var(--accent-2);}
.peer.you .name{background:var(--accent-2);color:var(--ground);opacity:0;transition:opacity .4s var(--ease) .05s;}
.peer.you.named .name{opacity:1;}
.peer.you.hidden{opacity:0;}

.peer.shayna{transition: transform 1.6s var(--ease-soft);}

/* =========================================================================
   Theme toggle — pill switch with sliding knob
   Designed as part of the design system, not a floating utility icon.
   Visual language matches the nav pill and bands: paper surface, rounded,
   subtle border, layered shadow.

   Build:
   • Pill-shaped track (the button itself).
   • Sliding knob — accent color in both themes (pink in dark, teal in
     light). Carries the active-state icon (moon when dark, sun when light).
   • Spring-eased translate on theme change. Slight overshoot.
   • Icon morphs via opacity + rotation crossfade.
========================================================================= */
.theme-toggle{
  position:fixed;
  bottom:56px;right:56px;
  width:88px;height:42px;
  border-radius:999px;

  background:var(--paper);
  border:1px solid rgba(245, 240, 232, .14);
  display:flex;align-items:center;
  padding:3px;
  cursor:pointer;
  z-index:90;

  /* Same shadow language as the nav pill — siblings in the design system. */
  box-shadow:
    0 16px 36px -14px rgba(0, 0, 0, .55),
    0 6px 14px -4px rgba(0, 0, 0, .35),
    0 1px 2px rgba(0, 0, 0, .25),
    inset 0 1px 0 rgba(255, 255, 255, .06);

  transition:
    background-color .35s var(--ease),
    border-color .35s var(--ease),
    box-shadow .35s var(--ease);
}
.theme-toggle:hover{
  border-color:rgba(245, 240, 232, .26);
  box-shadow:
    0 20px 40px -12px rgba(0, 0, 0, .62),
    0 8px 16px -4px rgba(0, 0, 0, .40),
    0 1px 2px rgba(0, 0, 0, .28),
    inset 0 1px 0 rgba(255, 255, 255, .08);
}
.theme-toggle:focus-visible{
  outline:none;
  border-color:var(--accent-strong);
  box-shadow:
    0 0 0 3px var(--accent-soft),
    0 16px 36px -14px rgba(0, 0, 0, .55),
    0 6px 14px -4px rgba(0, 0, 0, .35),
    inset 0 1px 0 rgba(255, 255, 255, .06);
}

.theme-toggle .knob{
  position:relative;
  width:36px;height:36px;
  border-radius:50%;
  background:var(--accent);
  color:var(--ground);
  display:flex;align-items:center;justify-content:center;
  transform:translateX(0);
  /* Spring transition — slight overshoot on the knob, smooth on color */
  transition:
    transform .48s cubic-bezier(.34, 1.5, .64, 1),
    background-color .35s var(--ease),
    color .35s var(--ease),
    box-shadow .35s var(--ease);
  box-shadow:
    0 2px 6px rgba(0, 0, 0, .42),
    0 1px 2px rgba(0, 0, 0, .28);
}

.theme-toggle .icon{
  position:absolute;
  width:17px;height:17px;
  transform-origin:center;
  transition:
    opacity .28s var(--ease),
    transform .42s var(--ease-soft);
}
.theme-toggle .icon-moon{
  opacity:1;
  transform:rotate(0) scale(1);
}
.theme-toggle .icon-sun{
  opacity:0;
  transform:rotate(-70deg) scale(.6);
}

/* Light theme — knob slides right, color flips, sun replaces moon.
   Knob travel = track-width − 2*padding − knob-width = 88 − 6 − 36 = 46px. */
:root[data-theme="light"] .theme-toggle{
  background:var(--paper);
  border-color:rgba(20, 17, 15, .14);
  box-shadow:
    0 12px 28px -10px rgba(20, 17, 15, .22),
    0 4px 10px -3px rgba(20, 17, 15, .12),
    0 1px 2px rgba(20, 17, 15, .08),
    inset 0 1px 0 rgba(255, 255, 255, .24);
}
:root[data-theme="light"] .theme-toggle:hover{
  border-color:rgba(20, 17, 15, .26);
  box-shadow:
    0 16px 32px -10px rgba(20, 17, 15, .26),
    0 6px 12px -3px rgba(20, 17, 15, .14),
    0 1px 2px rgba(20, 17, 15, .08),
    inset 0 1px 0 rgba(255, 255, 255, .30);
}
:root[data-theme="light"] .theme-toggle:focus-visible{
  border-color:var(--accent-strong);
  box-shadow:
    0 0 0 3px var(--accent-soft),
    0 12px 28px -10px rgba(20, 17, 15, .22),
    inset 0 1px 0 rgba(255, 255, 255, .24);
}
:root[data-theme="light"] .theme-toggle .knob{
  transform:translateX(46px);
  /* Knob color inherits the new --accent (teal) automatically */
  background:var(--accent);
  /* Icon color flips to --ground (which is cream in light mode) for
     contrast against the teal knob. Same variable, adapts per theme. */
  color:var(--ground);
  box-shadow:
    0 2px 6px rgba(20, 17, 15, .22),
    0 1px 2px rgba(20, 17, 15, .14);
}
:root[data-theme="light"] .theme-toggle .icon-moon{
  opacity:0;
  transform:rotate(70deg) scale(.6);
}
:root[data-theme="light"] .theme-toggle .icon-sun{
  opacity:1;
  transform:rotate(0) scale(1);
}

/* =========================================================================
   Reveal
========================================================================= */
.reveal{opacity:0;transform:translateY(16px);transition:opacity .9s var(--ease), transform .9s var(--ease);}
.reveal.in{opacity:1;transform:none;}
.reveal.d1{transition-delay:.08s;}
.reveal.d2{transition-delay:.14s;}


/* ##########################################################################
   CASE STUDY TEMPLATE
   Reusable architecture for project case studies. All four project pages
   (case.html?id=0..3) use this. Pulls title + pills from projects.js so
   the homepage and case studies stay in sync.
########################################################################## */

.case-main{
  padding:0 var(--pad-x);
}

/* ============================== CASE HERO ============================== */
.case-hero{
  /* Reduced ~25-30% from the prior 140/18vh top, 64/8vh bottom — the
     hero opens the story, it shouldn't dominate the experience. The
     work itself deserves more of the first viewport. */
  padding:max(96px, 13vh) 0 max(40px, 5vh);
  border-bottom:1px solid var(--rule);
  max-width:1440px;margin:0 auto;
}
.case-back{
  display:inline-flex;align-items:center;gap:6px;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:12px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mid);
  transition:color .25s var(--ease), gap .25s var(--ease);
  margin-bottom:48px;
}
.case-back:hover{color:var(--ink);gap:10px;}
.case-back .arr{font-size:11px;}

.case-title{
  font-family:'Antonio',sans-serif;
  font-weight:800;text-transform:uppercase;
  /* Bumped +6-8px from the prior clamp(22, 2.6vw, 40) so the project
     title carries its weight. Spacing below was the actual hierarchy
     problem — fixed via the pills margin (now 24px). */
  font-size:clamp(28px, 3vw, 46px);
  line-height:1.05;letter-spacing:-0.014em;
  text-wrap:balance;
  color:var(--ink);
  margin-bottom:16px;
}
.case-hero-pills{
  display:flex;flex-wrap:wrap;gap:7px;
  /* Was max(72, 9vh) — wildly oversized gap was the main reason the hero
     felt stretched vertically. */
  margin-bottom:24px;
}
/* Overview supports single string OR array of paragraphs.
   Spans the full hero width — no character-count cap. The hero itself
   (.case-hero) is the only constraint, so the intro aligns with the meta
   grid below it instead of floating as a narrower text block inside a
   wider container. */
.case-overview{
  max-width:64ch;
  margin-bottom:28px;
}
/* Overview reads as normal body copy, not caption-size text. Same
   typography as .case-body p so the opening paragraph feels continuous
   with the reading copy used throughout the rest of the case study. */
.case-overview p{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:16.5px;
  line-height:1.7;letter-spacing:-0.002em;
  color:var(--ink);
}
.case-overview p + p{margin-top:16px;}

/* Meta grid — three proportional columns (Role · Tools · Timeline).
   Role and Tools carry longer comma-separated values so they get more
   width; Timeline keeps a dedicated narrower column. */
.case-meta{
  display:grid;
  grid-template-columns:1.5fr 1.5fr 1fr;
  gap:32px;
}
.case-meta-item{display:flex;flex-direction:column;gap:8px;}
.case-meta-item .meta-label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:.10em;text-transform:uppercase;
  color:var(--mid);
}
.case-meta-item .meta-value{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:15px;letter-spacing:0;
  color:var(--ink);
  line-height:1.5;
}

/* (Hero visual markup removed from the case template; a future opt-in
   hero asset can be reintroduced as a data-driven module if needed.) */

/* ============================== STORY LAYOUT ============================== */
.case-story{
  display:grid;
  grid-template-columns:220px 1fr;
  gap:96px;
  padding:max(120px, 14vh) 0 max(120px, 14vh);
  max-width:1440px;
  margin:0 auto;
  align-items:start;
}

.case-toc{
  position:sticky;
  top:120px;
  align-self:start;
}
.case-toc-inner{
  display:flex;flex-direction:column;
}
.case-toc .toc-label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:.10em;text-transform:uppercase;
  color:var(--mid);
  margin-bottom:18px;
  display:block;
}
.case-toc .toc-list{
  list-style:none;display:flex;flex-direction:column;gap:14px;
}
.case-toc .toc-link{
  display:inline-flex;align-items:center;gap:10px;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:13px;letter-spacing:.04em;
  color:var(--mid);
  transition:color .25s var(--ease);
  position:relative;
  padding:2px 0;
}
.case-toc .toc-link:hover{color:var(--ink);}
.case-toc .toc-link .d{
  width:5px;height:5px;border-radius:50%;
  background:var(--accent);
  opacity:0;transform:scale(.4);
  transition:opacity .3s var(--ease), transform .35s var(--ease);
}
.case-toc .toc-link.is-current{color:var(--ink);}
.case-toc .toc-link.is-current .d{opacity:1;transform:scale(1);}

/* ============================== CONTENT ==============================
   Right column of the story grid. Wider cap so artifacts (galleries,
   before/after, frameworks, PDF) get more horizontal presence — body
   text below stays comfortably narrow at 68ch. */
.case-content{
  display:flex;flex-direction:column;gap:max(120px, 14vh);
  max-width:1040px;
}

/* Section flow: label → heading → body → modules.
   Margin-based spacing scale (template-wide rhythm):
     label    → heading : 8px   (tight; eyebrow attached to heading)
     heading  → body    : 24px  (heading → first reading copy)
     body     → module  : 40px  (reading copy → first asset)
     module   → module  : 48px  (asset → asset)
     module   → body    : 40px  (asset → next reading copy)
   The catch-all default is 40px so any unhandled adjacency stays
   editorial. Subsection breaks get extra breathing room (64px). */
.case-section{display:flex;flex-direction:column;}
.case-section > * + *{margin-top:40px;}
.case-section > .case-label + .case-heading{margin-top:8px;}
.case-section > .case-heading + *{margin-top:24px;}
.case-section > .case-module + .case-module{margin-top:48px;}
.case-section > * + .case-subsection{margin-top:64px;}

/* Section labels — subtle caption/eyebrow, not a pill. Section-level
   classification ("Systems / Research / Customer Experience") should
   feel like editorial running text, not a content marker chip.
   Pills are reserved for in-card labels (.decision-label, etc.) so the
   visual language stays:  pill = inside a card, eyebrow = section-level. */
.case-label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:12px;letter-spacing:.10em;text-transform:uppercase;
  color:var(--mid);
}

/* Section headings — editorial and approachable. Trimmed another ~5px
   from the top of the scale. The hero title is the only billboard. */
.case-heading{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-weight:600;
  text-transform:none;
  font-size:clamp(19px, 2.2vw, 26px);
  line-height:1.28;letter-spacing:-0.014em;
  color:var(--ink);
}

.case-body{
  display:flex;flex-direction:column;gap:22px;
  /* Body text keeps a comfortable reading measure even though the
     content column is wider — artifacts get the full 1040px, paragraphs
     stay at a readable ~68ch (~640px at 17px). */
  max-width:68ch;
}
.case-body p{font-size:16.5px;line-height:1.7;}
.case-body p{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:17px;line-height:1.65;letter-spacing:-0.003em;
  color:var(--ink);
}
.case-body p strong{font-weight:600;}
.case-body a{
  color:var(--accent-strong);
  border-bottom:1px solid var(--accent-soft);
  transition:border-color .25s var(--ease);
}
.case-body a:hover{border-color:var(--accent-strong);}

/* ============================== VISUAL MODULES ==============================
   Modules carry no implicit margin — the parent .case-section uses flex
   gap to set rhythm consistently. */
.case-module{
  margin:0;
}

/* Placeholder visuals — replaced with real assets later */
.placeholder-image{
  width:100%;
  aspect-ratio:4 / 3;
  background:var(--paper);
  border:1px solid var(--rule);
  border-radius:8px;
  position:relative;
  overflow:hidden;
}
/* When nested inside .module-figure (which now provides its own bg +
   shadow), strip the placeholder's redundant styling. */
.module-figure .placeholder-image{
  background:transparent;
  border:0;
  border-radius:0;
  aspect-ratio:auto;
  min-height:240px;
}
.placeholder-image::before{
  content:attr(data-label);
  position:absolute;top:50%;left:50%;
  transform:translate(-50%, -50%);
  font-family:'Plus Jakarta Sans',sans-serif;
  font-size:11px;letter-spacing:.10em;text-transform:uppercase;
  color:var(--mid);
  opacity:.7;
}
.placeholder-image[data-label=""]::before, .placeholder-image:not([data-label])::before{
  content:'Image';
}
.placeholder-video{
  width:100%;
  aspect-ratio:16 / 9;
  background:var(--paper);
  border:1px solid var(--rule);
  border-radius:8px;
  position:relative;
  display:flex;align-items:center;justify-content:center;
  gap:10px;
  color:var(--mid);
  font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;letter-spacing:.10em;text-transform:uppercase;
}
.placeholder-video::before{
  content:'';
  width:14px;height:14px;
  border-style:solid;border-width:7px 0 7px 12px;
  border-color:transparent transparent transparent var(--mid);
}
.placeholder-pdf{
  width:100%;
  aspect-ratio:3 / 4;
  background:var(--paper);
  border:1px solid var(--rule);
  border-radius:8px;
  position:relative;
  display:flex;align-items:center;justify-content:center;
  color:var(--mid);
  font-family:'Plus Jakarta Sans',sans-serif;font-size:11px;letter-spacing:.10em;text-transform:uppercase;
  max-width:480px;margin:0 auto;
}

figcaption, .module-caption{
  margin-top:14px;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:13px;line-height:1.5;color:var(--mid);
  letter-spacing:-0.002em;
  max-width:48ch;
}

/* Module: single image */
.module-single{}
.module-single figure{margin:0;}

/* Module: two-column comparison (e.g. before / after) */
.module-comparison{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:18px;
}
.module-comparison figure{margin:0;}
.module-comparison figcaption{
  margin-top:10px;
  font-size:12px;
  letter-spacing:.06em;text-transform:uppercase;
}

/* =================== Module visual containers ===================
   Option B (contained showcase): paper-surface frame with 28px internal
   padding so artifacts never touch the container edges. Image uses
   `contain` so the whole artifact is always visible — letterbox shows
   the paper surface, not the page. The container is a curated frame
   around the work, not a transparent pedestal. */
.module-figure{
  width:100%;
  position:relative;
  padding:28px;
  background:var(--paper);
  border-radius:12px;
  border:1px solid var(--rule);
  box-shadow:
    0 24px 50px -28px rgba(0, 0, 0, .28),
    0 6px 14px -8px rgba(0, 0, 0, .14);
}
.module-figure img{
  display:block;
  width:100%;
  height:auto;
  max-height:78vh;
  object-fit:contain;
  border-radius:4px;
}

/* =================== Module: before / after — toggle pattern ===================
   Replaces the previous drag-slider with a tab-style toggle (Before / After).
   Mirrors the about-page toggle in the design system: pill track + sliding
   accent indicator + crossfade between states. Optional per-state notes
   render below as "what wasn't working" / "what changed."

   Structure:
     .module-before-after          (figure — wrapper + caption owner)
       .ba-toggle                  (pill toggle group with sliding indicator)
         .ba-toggle-btn × 2
         .ba-toggle-indicator      (animated background)
       .ba-frame                   (padded paper-colored frame)
         .ba-stage                 (aspect-locked viewport)
           .ba-state × 2           (crossfade between states)
       .ba-notes (optional)
         .ba-note × 2              (matched explanatory copy)
       <figcaption> (optional)                                                */

/* Single cohesive card holding: toggle, stage, notes.
   Everything sits inside one paper-surface container with editorial
   padding. Toggle, image, and explanation read as one designed module
   instead of three stacked components. */
.module-before-after{
  display:flex;
  flex-direction:column;
  gap:0;
  padding:24px 24px 26px;
  background:var(--paper);
  border:1px solid var(--rule);
  border-radius:14px;
  box-shadow:
    0 24px 50px -28px rgba(0, 0, 0, .28),
    0 6px 14px -8px rgba(0, 0, 0, .14);
}
.module-before-after .ba-toggle{align-self:center;margin-bottom:20px;}
.module-before-after .ba-frame{width:100%;}
.module-before-after .ba-notes{width:100%;margin-top:20px;}
.module-before-after > figcaption{margin-top:16px;}

/* Editorial tabs — not a pill widget. Two text labels separated by
   space, with a thin accent underline marking the active state. Reads
   like a magazine section divider, not a UI control. */
.module-before-after .ba-toggle{
  position:relative;
  display:inline-flex;
  align-items:center;
  gap:32px;
  padding:0 0 12px;
  border-bottom:1px solid var(--rule);
  background:transparent;
}
.module-before-after .ba-toggle-indicator{
  position:absolute;
  bottom:-1px;
  left:0;
  width:0;
  height:2px;
  background:var(--accent);
  transition:transform .42s var(--ease-soft), width .42s var(--ease-soft);
}
.module-before-after .ba-toggle-btn{
  background:transparent;
  border:0;
  padding:4px 2px;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--mid);
  transition:color .35s var(--ease);
}
.module-before-after .ba-toggle-btn:hover{color:var(--ink);}
.module-before-after .ba-toggle-btn.is-current{color:var(--ink);}
.module-before-after .ba-toggle-btn:focus-visible{
  outline:none;
  color:var(--ink);
}

/* Frame is just a positioning wrapper inside the cohesive card. */
.module-before-after .ba-frame{
  width:100%;
}

/* Stage — sits inside the cohesive card. No own bg/border/shadow
   (card provides chrome). Image uses `contain` so it never touches
   stage edges; letterbox shows the ground color underneath. */
.module-before-after .ba-stage{
  position:relative;
  width:100%;
  aspect-ratio:16 / 10;
  max-height:72vh;
  border-radius:8px;
  overflow:hidden;
  background:var(--ground);
}
.module-before-after .ba-state{
  position:absolute;
  inset:0;
  opacity:0;
  pointer-events:none;
  /* Longer (700ms) and softer (ease-in-out) crossfade so the swap feels
     deliberate rather than abrupt. will-change hints the browser to
     hardware-accelerate the opacity change. */
  transition:opacity .7s var(--ease-soft);
  will-change:opacity;
}
.module-before-after .ba-state.is-current{
  opacity:1;
  pointer-events:auto;
  z-index:2;
}
.module-before-after .ba-state img{
  display:block;
  width:100%;
  height:100%;
  /* contain (not cover) so the whole artifact is visible — image never
     touches the stage edges. */
  object-fit:contain;
}

/* Notes — the "what wasn't working / what changed" copy that turns
   screenshot↔screenshot into problem↔change. Show only the active note. */
.module-before-after .ba-notes{
  width:100%;
  margin-top:4px;
}
.module-before-after .ba-note{
  display:none;
  flex-direction:column;
  gap:12px;
}
.module-before-after .ba-note.is-current{display:flex;}
.module-before-after .ba-note-label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--mid);
}
.module-before-after .ba-note p{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:15px;line-height:1.6;
  color:var(--ink);
  max-width:64ch;
}

/* =================== Module: two-column (image + slider, etc.) ===================
   30/70 — more dramatic asymmetry per the editorial-rhythm direction.
   Supporting framework artifact reads as the reference; the focal piece
   (typically a before/after comparison) commands the visual weight. */
.module-two-column{
  display:grid;
  grid-template-columns:30fr 70fr;
  gap:32px;
  align-items:start;
}
.module-two-column .tc-col > .case-module{margin:0;}
.module-two-column figcaption{font-size:12px;}

/* =================== Lightbox =================== */
.lightbox{
  position:fixed;
  inset:0;
  background:rgba(8, 6, 4, .92);
  z-index:300;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:18px;
  padding:5vh 5vw;
  opacity:0;
  pointer-events:none;
  transition:opacity .3s var(--ease);
}
.lightbox.is-open{opacity:1;pointer-events:auto;}
.lightbox-stage{
  display:flex;align-items:center;justify-content:center;
  max-width:100%;max-height:80vh;
}
.lightbox-img{
  max-width:100%;
  max-height:80vh;
  object-fit:contain;
  border-radius:6px;
  box-shadow:0 40px 100px rgba(0,0,0,.55);
}
.lightbox-caption{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-size:13px;
  color:rgba(255,255,255,.78);
  max-width:60ch;
  text-align:center;
  letter-spacing:-.002em;
}
.lightbox-close{
  position:absolute;
  top:24px;right:24px;
  width:44px;height:44px;
  border-radius:50%;
  background:rgba(255,255,255,.10);
  border:1px solid rgba(255,255,255,.22);
  color:#fff;
  font-size:24px;
  line-height:1;
  cursor:pointer;
  transition:background .25s var(--ease), border-color .25s var(--ease);
}
.lightbox-close:hover{
  background:rgba(255,255,255,.20);
  border-color:rgba(255,255,255,.36);
}

/* Lightbox-enabled gallery cues */
.module-gallery.is-lightbox figure,
.module-comparison.is-lightbox figure{
  cursor:zoom-in;
  transition:transform .35s var(--ease-soft);
}
.module-gallery.is-lightbox figure:hover,
.module-comparison.is-lightbox figure:hover{transform:translateY(-3px);}
.module-gallery.is-lightbox figure:focus-visible,
.module-comparison.is-lightbox figure:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:4px;
}

/* =========================================================================
   Module: gallery viewer — single integrated card.
   One paper-colored container holding: counter (top), image stage with
   floating nav overlays, caption, and thumbnail strip. Reads as one
   editorial gallery experience, not a collection of separate components.
========================================================================= */
.module-gallery-viewer{outline:none;}

/* One cohesive card: counter top, stage center, caption + thumbnail
   navigator at the bottom. Everything inside one paper surface so the
   gallery reads as a single showcase. */
.module-gallery-viewer .gv-card{
  display:flex;
  flex-direction:column;
  gap:16px;
  padding:24px;
  background:var(--paper);
  border:1px solid var(--rule);
  border-radius:14px;
  box-shadow:
    0 24px 50px -28px rgba(0, 0, 0, .28),
    0 6px 14px -8px rgba(0, 0, 0, .14);
}
.module-gallery-viewer .gv-header{
  display:flex;
  align-items:center;
  justify-content:flex-start;
  min-height:14px;
}
.module-gallery-viewer .gv-counter{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:10.5px;letter-spacing:.20em;text-transform:uppercase;
  color:var(--mid);
}
.module-gallery-viewer .gv-counter-sep{opacity:.55;}
.module-gallery-viewer .gv-caption{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:14px;line-height:1.55;
  color:var(--ink);
  max-width:62ch;
}

/* Stage — sits inside the cohesive card. No own bg/border/shadow
   (card provides chrome). Aspect-locked viewport for crossfading
   slides. Letterbox shows ground color so artifacts of any aspect
   display whole without touching edges. */
.module-gallery-viewer .gv-stage{
  position:relative;
  width:100%;
  aspect-ratio:4 / 3;
  border-radius:8px;
  overflow:hidden;
  background:var(--ground);
}
.module-gallery-viewer .gv-slide{
  position:absolute;
  inset:0;
  margin:0;
  display:flex;
  align-items:center;
  justify-content:center;
  opacity:0;
  pointer-events:none;
  transition:opacity .45s var(--ease);
}
.module-gallery-viewer .gv-slide.is-current{
  opacity:1;
  pointer-events:auto;
  z-index:1;
}
.module-gallery-viewer .gv-slide img{
  display:block;
  max-width:100%;
  max-height:100%;
  width:auto;
  height:auto;
  object-fit:contain;
}

/* Subtle nav arrows — small, low-contrast by default. Strengthen on
   hover over the stage so they don't compete with the artifact at rest.
   On touch devices (no hover), they default to a more visible state via
   the @media block. */
.module-gallery-viewer .gv-nav{
  position:absolute;
  top:50%;
  transform:translateY(-50%);
  width:36px;height:36px;
  border-radius:50%;
  background:rgba(255, 255, 255, .60);
  border:0;
  color:#181818;
  display:flex;align-items:center;justify-content:center;
  z-index:3;
  opacity:.55;
  backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);
  transition:opacity .25s var(--ease), background .25s var(--ease);
}
.module-gallery-viewer .gv-nav svg{width:16px;height:16px;}
.module-gallery-viewer .gv-stage:hover .gv-nav{opacity:1;}
.module-gallery-viewer .gv-nav:hover{
  background:rgba(255, 255, 255, .92);
}
.module-gallery-viewer .gv-nav:focus-visible{
  outline:2px solid var(--accent);
  outline-offset:2px;
  opacity:1;
}
.module-gallery-viewer .gv-prev{left:12px;}
.module-gallery-viewer .gv-next{right:12px;}

/* Thumbnail navigator — small previews inside the gallery card.
   Active thumb gets an accent outline. Horizontally scrollable if needed. */
.module-gallery-viewer .gv-thumbs{
  display:flex;
  gap:6px;
  overflow-x:auto;
  padding:4px 0 2px;
  scrollbar-width:thin;
  scrollbar-color:var(--rule-strong) transparent;
}
.module-gallery-viewer .gv-thumb{
  flex:0 0 76px;
  height:56px;
  background:var(--ground);
  border:0;
  border-radius:5px;
  overflow:hidden;
  padding:0;
  opacity:.55;
  transition:opacity .22s var(--ease), outline-color .22s var(--ease);
  outline:1.5px solid transparent;
  outline-offset:2px;
}
.module-gallery-viewer .gv-thumb img{
  display:block;
  width:100%;height:100%;
  object-fit:cover;
}
.module-gallery-viewer .gv-thumb:hover{opacity:1;}
.module-gallery-viewer .gv-thumb.is-current{
  opacity:1;
  outline-color:var(--accent-strong);
}
.module-gallery-viewer .gv-thumb:focus-visible{
  outline-color:var(--accent-strong);
}

/* (Standalone caption rule removed — caption now lives inside .gv-foot
   alongside the counter, styled by the foot-row rules above.) */

/* Module: image gallery (responsive grid) */
.module-gallery{
  display:grid;
  grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));
  gap:16px;
}
.module-gallery .placeholder-image{aspect-ratio:1;}

/* Module: video embed */
.module-video figure{margin:0;}

/* Module: PDF — embedded iframe (native browser viewer).
   Stronger elevation than other artifacts: research evidence deserves
   visual presence. Cursor conflict is handled by the iframe-cursor IIFE. */
.module-pdf{display:flex;flex-direction:column;gap:16px;}
.module-pdf .pdf-frame{
  width:100%;
  aspect-ratio:4 / 3;
  border-radius:10px;
  overflow:hidden;
  background:var(--paper);
  box-shadow:
    0 36px 80px -28px rgba(0, 0, 0, .45),
    0 12px 28px -10px rgba(0, 0, 0, .22);
}
.module-pdf .pdf-iframe{
  display:block;
  width:100%;
  height:100%;
  border:0;
  background:#fff;
}
.module-pdf .pdf-meta{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:16px;
  flex-wrap:wrap;
}
.module-pdf .pdf-label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--mid);
}
.module-pdf .pdf-open{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:12px;letter-spacing:.02em;
  color:var(--accent-strong);
  transition:opacity .2s var(--ease);
}
.module-pdf .pdf-open:hover{opacity:.7;}

/* Module: metric callout (numbers + labels in a row) */
/* Metrics — borderless. The only horizontal divider in the case study
   content area is the one beneath the hero metadata; everything else
   relies on spacing. */
.module-metrics{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(160px, 1fr));
  gap:32px;
}
.metric{
  display:flex;flex-direction:column;gap:6px;
}
.metric-value{
  font-family:'Antonio',sans-serif;
  font-weight:800;
  font-size:clamp(40px, 4.2vw, 64px);
  line-height:1;letter-spacing:-0.012em;text-transform:uppercase;
  color:var(--ink);
}
.metric-label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:11px;letter-spacing:.10em;text-transform:uppercase;
  color:var(--mid);
}

/* Module: decisions — cards back, content-driven (no generic eyebrow).
   Subtle paper-surface card with editorial padding. The card title
   carries the meaning; the body explains. Cards read as self-contained
   ideas, not labeled chips. */
.module-decisions{
  display:grid;
  gap:18px;
}
.module-decisions[data-count="1"]{grid-template-columns:1fr;}
.module-decisions[data-count="2"]{grid-template-columns:1fr 1fr;}
.module-decisions[data-count="3"]{grid-template-columns:1fr 1fr 1fr;}
.decision-card{
  display:flex;
  flex-direction:column;
  gap:10px;
  padding:24px 24px 26px;
  background:var(--paper);
  border:1px solid var(--rule);
  border-radius:12px;
}
.decision-title{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-weight:600;
  text-transform:none;
  font-size:18px;
  line-height:1.3;
  letter-spacing:-0.012em;
  color:var(--ink);
}
.decision-body{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:14.5px;line-height:1.6;color:var(--mid);
}

/* Module: callout — short narrative bridge or section takeaway.
   Centered editorial paragraphs, no chrome. Variants change weight:
     .variant-transition = lighter bridge between sections
     .variant-takeaway   = stronger end-of-section conclusion */
.module-callout{
  max-width:62ch;
  margin:8px auto;
  text-align:center;
  display:flex;
  flex-direction:column;
  gap:10px;
}
.module-callout p{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-weight:400;
  font-size:17px;
  line-height:1.55;
  color:var(--mid-2);
  letter-spacing:-0.005em;
}
.module-callout.variant-transition p{
  color:var(--mid-2);
}
.module-callout.variant-takeaway{
  max-width:64ch;
}
.module-callout.variant-takeaway p{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-weight:500;
  font-size:19px;
  line-height:1.5;
  color:var(--ink);
}

/* Module: quote / testimonial */
.module-quote{
  padding:24px 0 24px 32px;
  border-left:2px solid var(--accent);
  max-width:60ch;
}
.module-quote p{
  font-family:'Antonio',sans-serif;
  font-weight:800;
  font-size:clamp(22px, 2.4vw, 30px);
  line-height:1.25;letter-spacing:-0.008em;
  color:var(--ink);
  text-transform:none;
}
.module-quote cite{
  display:block;margin-top:18px;
  font-family:'Plus Jakarta Sans',sans-serif;font-style:normal;
  font-size:12px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mid);
}

/* Module: large impact statistic */
.module-impact{
  padding:max(60px, 8vh) 0;
  text-align:center;
  display:flex;flex-direction:column;align-items:center;gap:18px;
}
.impact-value{
  font-family:'Antonio',sans-serif;
  font-weight:800;
  font-size:clamp(80px, 12vw, 200px);
  line-height:1;letter-spacing:-0.022em;text-transform:uppercase;
  color:var(--accent-strong);
}
.impact-label{
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:500;
  font-size:14px;letter-spacing:.04em;
  color:var(--mid);
  max-width:36ch;
  line-height:1.5;
}

/* ============================== SUBSECTIONS ==============================
   Inside a parent section, two or more sub-narratives can live together
   (e.g. "Applying the System" → Contact Us + Understanding My Bill).
   They share the parent section's TOC entry but get their own heading. */
/* Subsection flow uses the same spacing scale as .case-section:
     subsection title → supporting paragraph : 16px
     paragraph        → asset                : 32px
     asset            → asset                : 48px
   No top border — the 96px gap above the subsection establishes the
   break on its own. */
.case-subsection{
  margin-top:96px;
  display:flex;flex-direction:column;
}
.case-subsection > * + *{margin-top:32px;}
.case-subsection > .case-subsection-title + *{margin-top:16px;}
.case-subsection > .case-module + .case-module{margin-top:48px;}
.case-subsection-title{
  font-family:'Plus Jakarta Sans',sans-serif;
  font-weight:600;
  text-transform:none;
  font-size:clamp(22px, 2.6vw, 32px);
  line-height:1.2;letter-spacing:-0.012em;
  color:var(--ink);
}

/* ============================== GALLERY LAYOUTS ==============================
   Default = auto-fill responsive grid. Variants for specific compositions. */
.module-gallery.layout-2x2{
  grid-template-columns:1fr 1fr;
}
.module-gallery.layout-2x2 .placeholder-image{aspect-ratio:4/3;}

.module-gallery.layout-3col{
  grid-template-columns:1fr 1fr 1fr;
}
.module-gallery.layout-3col .placeholder-image{aspect-ratio:3/4;}

/* Cards layout — used in Section 1 (5 design-system artifacts).
   auto-fit lets it wrap intelligently from 5 across down to 2/1. */
.module-gallery.layout-cards{
  grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));
  gap:16px;
}
.module-gallery.layout-cards .module-figure{
  aspect-ratio:4/3;
  display:flex;align-items:center;justify-content:center;
}
.module-gallery.layout-cards .module-figure img{
  width:100%;height:100%;object-fit:cover;
}

.module-gallery figure{margin:0;display:flex;flex-direction:column;}
.module-gallery figcaption{
  margin-top:10px;
  font-family:'Plus Jakarta Sans',sans-serif;font-weight:400;
  font-size:12px;line-height:1.5;letter-spacing:0;
  color:var(--mid);
}

/* ============================== METRICS VARIANTS ==============================
   variant=frequency = descriptive labels rather than numeric values.
   Use a smaller Anton size so phrases like "Daily office hours" sit cleanly. */
.module-metrics.variant-frequency .metric-value{
  font-size:clamp(20px, 2.4vw, 34px);
  line-height:1.1;
  letter-spacing:-0.005em;
}

/* ============================== QUOTE VARIANTS ==============================
   variant=large = the thesis-style closer. Full-width, larger type. */
.module-quote.variant-large{
  max-width:none;
  padding:48px 0 48px 40px;
  border-left-width:3px;
  margin:32px 0;
}
.module-quote.variant-large p{
  font-size:clamp(26px, 3.2vw, 44px);
  line-height:1.2;
}

/* ============================== MOBILE — case study additions ============================== */
@media (max-width: 980px){
  .case-subsection{margin-top:48px;padding-top:36px;}
  .module-gallery.layout-2x2,
  .module-gallery.layout-3col{grid-template-columns:1fr;}
  .module-gallery.layout-3col .placeholder-image{aspect-ratio:4/3;}
  .module-two-column{grid-template-columns:1fr;gap:32px;}
  .module-pdf .pdf-frame{aspect-ratio:3 / 4;}
  .module-pdf .pdf-meta{flex-direction:column;align-items:flex-start;gap:6px;}
  .module-before-after .ba-stage{aspect-ratio:4 / 5;max-height:none;}
  .module-before-after .ba-frame{padding:12px;}
  .module-figure{padding:14px;}
  .module-gallery-viewer .gv-card{padding:16px;gap:12px;}
  .module-gallery-viewer .gv-nav{
    width:34px;height:34px;
    opacity:1;  /* always visible on touch */
    background:rgba(255, 255, 255, .85);
  }
  .module-gallery-viewer .gv-prev{left:8px;}
  .module-gallery-viewer .gv-next{right:8px;}
  .module-gallery-viewer .gv-thumb{flex:0 0 64px;height:48px;}
  .module-before-after{padding:16px 16px 18px;}
  .module-figure{padding:18px;}
  .module-decisions{gap:14px;}
  .decision-card{padding:20px 20px 22px;}
  .lightbox-close{top:14px;right:14px;width:40px;height:40px;}
}

/* ============================== UP NEXT ============================== */
.case-next{
  max-width:1440px;
  margin:0 auto;
  /* Bumped top padding (~96/12vh) compensates for the removed border-top.
     Spacing alone establishes the CTA as a separate beat. */
  padding:max(96px, 12vh) 0 max(120px, 14vh);
}
.case-next .case-label{margin-bottom:20px;display:inline-block;}
.case-next-link{
  display:flex;align-items:baseline;gap:14px;
  transition:gap .3s var(--ease);
}
.case-next-link:hover{gap:20px;}
.case-next-link .case-heading{
  /* Title matches the case-hero scale — was clamp(36, 5vw, 80) which
     made the closing CTA bigger than anything else on the page. Anton
     treatment preserved because this is the CTA, not a section header. */
  font-family:'Antonio',sans-serif;
  font-weight:800;
  text-transform:uppercase;
  font-size:clamp(28px, 3vw, 46px);
  line-height:1.04;
  letter-spacing:-0.014em;
  max-width:24ch;
  color:var(--ink);
  transition:color .3s var(--ease);
}
.case-next-link:hover .case-heading{color:var(--accent-strong);}
.case-next-link .arrow{
  /* Arrow scales with the title and sits tight (14px gap) so it feels
     attached to the end of the title — one interaction, not two
     elements separated by space. */
  font-family:'Plus Jakarta Sans',sans-serif;
  font-size:clamp(20px, 2.2vw, 32px);
  line-height:1;
  color:var(--mid);
  transition:color .3s var(--ease), transform .3s var(--ease);
}
.case-next-link:hover .arrow{color:var(--accent-strong);transform:translate(4px, -4px);}

/* ============================== MOBILE ============================== */
/* Note: --pad-x is set by the breakpoint system near the top of this
   stylesheet (60/32/20 at 1200/745). This block handles homepage-specific
   responsive logic — type scale, nav layout, hero padding, etc. */
@media (max-width: 980px){
  body.cursor-on{cursor:auto;}
  .peer{display:none;}
  .preview{display:none !important;}
  /* Mobile nav — chromeless editorial split, sits 16px from top with
     the page's --pad-x (20px at mobile width). Slightly smaller type. */
  nav.top{top:16px;}
  nav.top .nav-inner{padding:0 var(--pad-x);}
  nav.top .brand{font-size:12px;}
  nav.top ul.nav-links{gap:14px;}
  nav.top .nav-links a{font-size:11px;letter-spacing:.06em;}
  section.hero{padding-top:18vh;}
  section.hero h1{max-width:18ch;}
  section.work{padding:80px 0 120px;}
  section.work .work-head{margin-bottom:48px;}
  section.about{grid-template-columns:1fr;gap:40px;padding-top:80px;padding-bottom:80px;}
  /* Bar disabled site-wide; no padding-shift needed. The mobile bar
     position override stays here in case the bar is restored later. */
  .row::before{left:-4px;}
  /* Principles mobile — shorter section, smaller buffer, smaller type
     with wrap allowed, tighter gap. Letterpress shadow is theme-driven
     via the CSS variables on section.principles, so it carries through
     unchanged from desktop. */
  section.principles{
    height:200vh;
    padding-top:8vh;
  }
  section.principles .principle{
    font-size:clamp(26px, 7vw, 50px);
    white-space:normal;
    line-height:1.05;
    letter-spacing:-0.008em;
  }
  .principles-list{
    gap:clamp(6px, 1.5vh, 18px);
  }
  /* Mobile toggle is at bottom:32px — match footer padding-bottom so the
     back-to-top link bottom-aligns with the toggle bottom at this width too. */
  footer{padding-bottom:32px;}
  .footer-grid{grid-template-columns:1fr;gap:56px;}
  .footer-left{min-height:0;}
  .footer-left-top{gap:32px;}
  .footer-totop{margin-top:24px;}
  section.bands{padding:24px 0 36px;}
  .bands .band{padding:22px 0;}
  .bands .band + .band{margin-top:20px;}
  .bands .statement{font-size:clamp(30px, 7.5vw, 52px);padding:0 28px;}
  .theme-toggle{bottom:32px;right:28px;width:74px;height:38px;padding:3px;}
  .theme-toggle .knob{width:32px;height:32px;}
  .theme-toggle .icon{width:15px;height:15px;}
  :root[data-theme="light"] .theme-toggle .knob{transform:translateX(36px);}

  /* ===== Case study mobile ===== */
  .case-hero{padding:120px 0 56px;}
  .case-meta{grid-template-columns:repeat(2, 1fr);gap:24px;}
  .case-story{
    grid-template-columns:1fr;
    gap:48px;
    padding:80px 0;
  }
  .case-toc{
    position:relative;top:auto;
    padding-bottom:24px;
    border-bottom:1px solid var(--rule);
  }
  .case-toc .toc-list{flex-direction:row;flex-wrap:wrap;gap:14px 18px;}
  .case-content{gap:80px;}
  .module-comparison{grid-template-columns:1fr;}
  .module-decisions[data-count="2"],
  .module-decisions[data-count="3"]{grid-template-columns:1fr;}
  .module-metrics{padding:30px 0;}
}

/* =========================================================================
   Tablet refinements (721-980px). The broad 980px block above is tuned
   primarily for phones — at tablet widths several rules feel too tight
   (about collapsing to single column when 2-col still fits, hero h1
   narrower than needed, work-section padding cut too aggressively).
   This block overrides those specific rules so tablet portrait gets
   intermediate values: looser than phone, tighter than desktop.
========================================================================= */
@media (min-width: 721px) and (max-width: 980px){
  /* Hero — keep ~22vh top instead of 18vh (more breathing room than
     phone), title gets a 15ch ceiling (vs 18ch on phone — text wraps
     to fewer lines), font caps lower than desktop so headings don't
     dominate the smaller viewport. */
  section.hero{ padding-top: 22vh; }
  section.hero h1{
    max-width: 15ch;
    font-size: clamp(48px, 8.5vw, 110px);
  }
  /* Work section — proportional to tablet, lighter than desktop. */
  section.work{ padding: max(56px, 6vh) 0 max(90px, 10vh); }
  section.work .work-head{ margin-bottom: max(48px, 6vh); }
  /* About — restore 2-column layout (phones get the 1-column stack
     from the broader 980px block). At tablet width the two columns
     still fit cleanly and reading two parallel beats > one tall one. */
  section.about{
    grid-template-columns: 1fr 1fr;
    gap: 56px;
    padding-top: max(80px, 10vh);
    padding-bottom: max(80px, 10vh);
  }
}

/* =========================================================================
   Mobile refinements (<= 720px). Tightens a few things the broader
   980px block leaves at intermediate values — phones get the tightest
   spacing because every pixel of vertical space matters more on a
   short viewport.
========================================================================= */
@media (max-width: 720px){
  /* Nav — restore top:0 (mobile breakpoint above set top:16px which
     left a 16px gap between viewport top and the nav bar background,
     letting hero text peek through above the nav on scroll-up). */
  nav.top{
    top: 0;
    padding: 18px 0 14px;
  }
  /* Hero — drop to content-sized + small top/bottom padding rather
     than 100vh. The desktop 100vh hero left ~70% of the section empty
     on phones (h1 sits in the top, vast dark space below before bands
     start). Auto-height removes that empty gap. */
  section.hero{
    height: auto;
    min-height: auto;
    padding-top: 14vh;
    padding-bottom: 8vh;
  }
  /* Work rows — restructure the 4-column desktop grid (num / title /
     image-gutter / pills) into 2 columns (num + body). Pills hidden
     since they were already aria-hidden, and the image-gutter column
     has no value on a touch screen (no hover preview). */
  .row{
    grid-template-columns: 36px 1fr;
    gap: 16px;
    padding: 22px 14px;
  }
  .row .row-pills{ display: none; }
  .row .row-num{ font-size: 12px; }
  .row .row-body h3{ font-size: clamp(20px, 5.5vw, 28px); }
  .row .row-body .row-desc{ font-size: 14px; line-height: 1.45; }
  /* Section bands — tighter padding + spacing between bands. */
  section.bands{ padding: 18px 0 28px; }
  .bands .band{ padding: 18px 0; }
  .bands .band + .band{ margin-top: 16px; }
  /* Work — even less than tablet/980 default. */
  section.work{ padding: 60px 0 100px; }
  section.work .work-head{ margin-bottom: 36px; }
  /* Footer — tighter gap when stacking on phone. */
  .footer-grid{ gap: 40px; }
  /* Theme toggle stays at 24px from corners on phone (matches v2). */
  .theme-toggle{ bottom: 24px; right: 24px; }
  /* Manifesto back-layer + reveal-zone — the desktop layered-scroll
     effect doesn't work on phone (text overflows, can't time the
     reveal to scroll). Collapse the reveal-zone scroll-spacer to zero
     so it doesn't create a 190vh empty gap above the footer. The
     manifesto principles are shown instead as a static .manifesto-mobile
     section that lives in normal document flow (see HTML + rules below). */
  .principles-reveal-zone{
    height: 0;
    display: none;
  }
  /* Static mobile manifesto — visible only on phone, lives inside
     main between about and footer. Same six principles as the desktop
     layered version, no animation, no "active line" state. */
  .manifesto-mobile{
    display: block;
    padding: 64px var(--pad-x, 20px) 80px;
  }
  .manifesto-mobile .manifesto-mobile-eyebrow{
    font-family: 'Plus Jakarta Sans', sans-serif;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--mid);
    margin: 0 0 28px;
  }
  .manifesto-mobile ol.manifesto-mobile-list{
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 18px;
  }
  .manifesto-mobile li{
    font-family: 'Antonio', sans-serif;
    font-weight: 700;
    text-transform: uppercase;
    font-size: clamp(26px, 7.5vw, 44px);
    line-height: 1.02;
    letter-spacing: -0.012em;
    color: var(--ink);
  }
}
/* Hide the static mobile manifesto on widths above 720px (desktop /
   tablet use the layered back-layer instead). */
.manifesto-mobile{ display: none; }

/* =========================================================================
   Mobile nav — hamburger button + full-screen overlay menu.
   Below 720px, the inline nav-links (Work / About / Contact) don't
   fit alongside the brand and are replaced by a hamburger button
   that opens a full-screen editorial menu. Above 720px the overlay
   stays hidden and the inline nav-links are visible (default).
========================================================================= */
.nav-menu-toggle{
  display:none;
  flex-direction:column;
  gap:5px;
  align-items:flex-end;
  justify-content:center;
  background:transparent;
  border:0;
  cursor:pointer;
  padding:8px;
  margin:-8px;
  color:var(--ink);
}
.nav-menu-toggle-line{
  display:block;
  width:22px;
  height:1.5px;
  background:currentColor;
}
.nav-menu-toggle-line:nth-child(2){ width:16px; }

.nav-menu-overlay{
  position:fixed;
  inset:0;
  z-index:9998;
  background:var(--paper-2);
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  opacity:0;
  pointer-events:none;
  transition:opacity .35s var(--ease);
}
.nav-menu-overlay.is-open{
  opacity:1;
  pointer-events:auto;
}
.nav-menu-close{
  position:absolute;
  top:24px;
  right:24px;
  width:44px;
  height:44px;
  background:transparent;
  border:0;
  cursor:pointer;
  color:var(--ink);
  display:flex;
  align-items:center;
  justify-content:center;
  padding:0;
}
.nav-menu-close svg{ width:24px; height:24px; }
.nav-menu-list{
  list-style:none;
  padding:0;
  margin:0;
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:24px;
  text-align:center;
}
.nav-menu-list a{
  font-family:'Antonio', sans-serif;
  font-weight:700;
  font-size:clamp(40px, 10vw, 64px);
  line-height:1.05;
  letter-spacing:-0.012em;
  color:var(--ink);
  text-decoration:none;
  transition:color .25s var(--ease);
}
.nav-menu-list a:hover{ color:var(--accent-strong); }

/* Lock background scroll while the menu is open. */
body.menu-open{ overflow:hidden; }

@media (max-width: 720px){
  nav.top ul.nav-links{ display:none; }
  .nav-menu-toggle{ display:flex; }
  /* Hide the desktop manifesto back-layer on mobile. The layered-scroll
     effect was designed for wide viewports where the big Antonio text
     can sit behind content and reveal between sections. On phones the
     text overflows, fights the foreground, and adds nothing — clean
     to drop entirely. The reveal-zone scroll spacer is harmless when
     the back layer is hidden. */
  .principles-back{ display:none; }
}

@media (prefers-reduced-motion: reduce){
  *{transition:none !important;animation:none !important;}
  .reveal{opacity:1;transform:none;}
  .peer.shayna{transition:none !important;}
}
