/* aworkinprocess_ — site styles
   Big condensed display sans + JetBrains Mono utility text.
   Paper bg, black ink, bold yellow accent. */

:root {
  --fg: #0e0e0c;
  --bg: #f4f1ea;
  --bg-2: #ece8df;
  --ink: #0e0e0c;
  --muted: #6b6a64;
  --line: rgba(14, 14, 12, 0.12);
  --accent: #f0c419;
  --accent-ink: #0e0e0c;

  /* Flat archive-tint swatches — same four hue families as the post__img--N
     placeholder gradients (rust, mustard-black, warm taupe, gold), reduced to
     one dominant flat tone each so the Archive resting state reads as a
     composed field of color instead of a busy multi-stop gradient. */
  --archive-tint-1: #6b3010;
  --archive-tint-2: #2b220f;
  --archive-tint-3: #5a4830;
  --archive-tint-4: #b98d2e;

  --font-display: "Anton", "Bebas Neue", "Oswald", system-ui, sans-serif;
  --font-body: "Inter Tight", "Helvetica Neue", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "IBM Plex Mono", ui-monospace, monospace;

  --radius: 4px;
  --pad: clamp(20px, 4vw, 56px);
  --maxw: 1480px;

  /* Project-page vertical rhythm. Two distinct scales, not one reused everywhere:
     --section-rhythm is the space BETWEEN major sections (a narrative beat change).
     --block-gap is the LOCAL space between blocks inside one section (a paragraph
     next to an image) — deliberately tighter so related content reads as one
     composition instead of floating in its own band. */
  --section-rhythm: clamp(40px, 5vw, 80px);
  --block-gap: clamp(24px, 3vw, 40px);
}

[data-theme="dark"] {
  --fg: #f4f1ea;
  --bg: #0e0e0c;
  --bg-2: #18181550;
  --ink: #f4f1ea;
  --muted: #9b9a92;
  --line: rgba(244, 241, 234, 0.14);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; }

/* ── Skip link ───────────────────────────────────────────────────────────
   Rendered as Nav's own first child (see components.jsx) so every page gets
   it for free without touching each entry file's markup beyond adding the
   #main-content id its href targets. Off-screen until focused -- standard,
   well-tested pattern, no visual effect for mouse/touch users. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 1000;
  background: var(--bg); color: var(--fg);
  padding: 12px 20px; font-family: var(--font-mono); font-size: 13px;
  letter-spacing: 0.08em; text-transform: uppercase;
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus {
  left: 0; top: 0;
}

/* ── Reduced motion ──────────────────────────────────────────────────────
   The standard, well-tested reset: collapse every animation/transition to
   an imperceptible duration rather than deleting them outright, so every
   element still resolves to its correct final state (fill-mode/keyframe end
   values included) instead of risking getting stuck at an animation's
   starting opacity/transform. This is what actually stops the homepage
   ticker's scroll, the .reveal fade/slide-up used throughout Story and
   elsewhere, and the menu/cart-drawer slide transitions, while every state
   change (menu open, reveal-in, etc.) still happens and is still understood
   -- just instantly instead of animated. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── Nav ─────────────────────────────────────────────────────────────── */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  padding: 20px var(--pad);
  gap: 40px;
  transition: background 0.3s ease, border-color 0.3s ease, padding 0.3s ease;
  border-bottom: 1px solid transparent;
  color: var(--bg);
  mix-blend-mode: difference;
}
.nav--scrolled {
  background: color-mix(in oklab, var(--bg) 85%, transparent);
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  border-bottom-color: var(--line);
  padding-top: 14px; padding-bottom: 14px;
  color: var(--fg);
  mix-blend-mode: normal;
}
.nav__links {
  display: flex; gap: 28px; justify-self: center;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em;
  text-transform: uppercase; font-weight: 500;
}
.nav__links a {
  position: relative; padding: 4px 0;
  opacity: 0.85; transition: opacity 0.2s;
}
.nav__links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px;
  height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.25s ease;
}
.nav__links a:hover { opacity: 1; }
.nav__links a:hover::after { transform: scaleX(1); }

/* nav__cart: only rendered when a page passes onCartClick (Collection/Product) --
   Home/Archive/Project never render it, so their nav keeps its exact current
   layout even though the grid now reserves a 4th (auto-sized, empty) track. */
.nav__cart {
  display: inline-flex; align-items: center; gap: 6px;
  background: transparent; border: none; color: inherit; cursor: pointer;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em;
  text-transform: uppercase; font-weight: 500; padding: 4px 0;
}
.nav__cart-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 999px; background: var(--accent); color: var(--accent-ink);
  font-size: 10px; font-weight: 700; line-height: 1;
}

.nav__menu {
  width: 44px; height: 44px; border: 1px solid currentColor;
  background: transparent; border-radius: 999px;
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; cursor: pointer; transition: background 0.2s;
  color: inherit;
}
.nav__menu span { display: block; width: 16px; height: 1.5px; background: currentColor; }
.nav--scrolled .nav__menu:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

@media (max-width: 880px) {
  .nav__links { display: none; }
}

/* ── Hero ────────────────────────────────────────────────────────────── */
.hero {
  position: relative; min-height: 100vh;
  padding: 100px var(--pad) 32px;
  display: flex; flex-direction: column; justify-content: space-between;
  overflow: hidden;
  color: var(--bg);
  background: var(--ink);
}
@media (max-width: 880px) {
  /* Density refinement: a modest height trim only -- .hero__inner's own
     gap/hierarchy is untouched, this just reduces the flexible empty space
     around it (min-height) and the clearance above the eyebrow (top
     padding) slightly, so the section still reads as a full, cinematic
     opening rather than a cramped one. Desktop (>880px) is untouched. */
  .hero { min-height: 90vh; padding-top: 80px; }
}
.hero__bg {
  position: absolute; inset: -10% 0; z-index: 0;
  background:
    radial-gradient(ellipse 80% 50% at 50% 35%, rgba(240, 196, 25, 0.20), transparent 65%),
    radial-gradient(ellipse 50% 30% at 15% 90%, rgba(240, 196, 25, 0.10), transparent 60%),
    radial-gradient(ellipse 50% 30% at 90% 15%, rgba(240, 196, 25, 0.06), transparent 60%),
    var(--ink);
  will-change: transform;
}
.hero__beams {
  position: absolute; inset: 0;
  background-image:
    repeating-linear-gradient(90deg, transparent 0 calc(100%/12 - 1px), rgba(255,255,255,0.035) calc(100%/12 - 1px) calc(100%/12));
}
.hero__grain {
  position: absolute; inset: 0; opacity: 0.55; pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.06 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}

.hero__top {
  position: relative; z-index: 1;
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  padding-top: 24px;
}

.hero__inner {
  position: relative; z-index: 1;
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  display: flex; flex-direction: column; gap: 36px;
  flex: 1; justify-content: center;
}

.hero__eyebrow {
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  text-transform: uppercase; color: rgba(244, 241, 234, 0.7);
  animation: fadeUp 0.7s ease both;
}
.hero__eyebrow .dot {
  width: 8px; height: 8px; border-radius: 999px; background: var(--accent);
  box-shadow: 0 0 0 4px rgba(240, 196, 25, 0.18);
  animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.4 } }

.hero__headline {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(64px, 14vw, 240px);
  line-height: 0.86;
  letter-spacing: -0.01em;
  margin: 0;
  text-transform: uppercase;
  text-wrap: balance;
}
.hero__word {
  display: inline-block; opacity: 0; transform: translateY(60px);
  animation: fadeUp 0.9s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(40px); }
  to { opacity: 1; transform: translateY(0); }
}

.hero__sub-row {
  display: flex; justify-content: space-between; align-items: end; gap: 40px;
  border-top: 1px solid rgba(244, 241, 234, 0.16);
  padding-top: 22px;
  flex-wrap: wrap;
  animation: fadeUp 0.9s ease both 0.6s;
}
.hero__sub {
  font-family: var(--font-mono);
  font-size: 13px; line-height: 1.6;
  max-width: 52ch; color: rgba(244, 241, 234, 0.7);
  margin: 0;
}
.hero__cta {
  display: inline-flex; align-items: center; gap: 12px;
  height: 48px; padding: 0 24px;
  background: var(--accent); color: var(--accent-ink);
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em;
  font-weight: 600;
  border-radius: 0;
  transition: transform 0.2s, background 0.2s;
}
.hero__cta:hover { background: #ffd424; transform: translate(-2px, -2px); box-shadow: 4px 4px 0 rgba(244, 241, 234, 0.9); }

.hero__meta {
  position: relative; z-index: 1;
  max-width: var(--maxw); margin: 0 auto; width: 100%;
  display: grid; grid-template-columns: 1fr 1fr 1fr;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em;
  color: rgba(244, 241, 234, 0.5); padding-top: 16px;
  border-top: 1px solid rgba(244, 241, 234, 0.14);
  animation: fadeUp 0.9s ease both 0.8s;
}
.hero__meta-mid { text-align: center; }
.hero__meta > :last-child { text-align: right; }

@media (max-width: 720px) {
  .hero__meta { grid-template-columns: 1fr; gap: 4px; }
  .hero__meta-mid, .hero__meta > :last-child { text-align: left; }
}

/* Desktop-only: tighten the hero composition so 100% zoom reads like the
   previous ~75%-zoom density (headline + spacing scaled down, mobile untouched). */
@media (min-width: 881px) {
  .hero { padding: 80px var(--pad) 26px; }
  .hero__top { padding-top: 20px; }
  /* justify-content is superseded by the auto margins below, which split the
     real leftover space between the headline and the sub-row — the headline's
     centered position is unaffected, and the sub-row is pushed toward the
     bottom rule instead of hanging directly under the headline. Matching
     padding above and below the sub-row's own content (rather than letting it
     sit flush against the bottom rule) is what actually centers the
     paragraph/button within that lower band. */
  .hero__inner { gap: 28px; justify-content: flex-start; }
  .hero__eyebrow { font-size: 10px; }
  .hero__headline { font-size: clamp(56px, 11vw, 180px); margin-top: auto; }
  .hero__sub-row { padding-top: 18px; padding-bottom: 18px; margin-top: auto; }
  .hero__sub { font-size: 12px; }
  .hero__cta { height: 42px; padding: 0 20px; font-size: 11px; }
  .hero__meta { padding-top: 14px; font-size: 9px; }
}

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  height: 46px; padding: 0 22px;
  border-radius: var(--radius);
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em;
  font-weight: 600;
  transition: transform 0.2s ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s;
  cursor: pointer; border: 1px solid transparent;
  white-space: nowrap;
}
.btn--accent { background: var(--accent); color: var(--accent-ink); }
.btn--accent:hover { background: #ffd424; transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--ink); }
.btn--solid { background: var(--ink); color: var(--bg); }
.btn--solid:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 rgba(14,14,12,0.2); }
[data-theme="dark"] .btn--solid { background: var(--bg); color: var(--ink); }
.btn--ghost-dark {
  background: transparent; color: var(--bg);
  border-color: rgba(244,241,234,0.25);
}
.btn--ghost-dark:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn--ghost { background: transparent; color: var(--fg); border-color: var(--line); }
.btn--ghost:hover { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.btn--lg { height: 64px; padding: 0 32px; font-size: 12px; }

/* ── Strip ────────────────────────────────────────────────────────────── */
.strip {
  background: var(--bg);
  color: var(--fg);
  padding: 32px 0 36px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  position: relative;
  /* Offsets the ENTER_ anchor jump so the fixed nav doesn't clip the top of
     the strip — matches the scrolled nav's rendered height. */
  scroll-margin-top: 84px;
}
.strip__label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--muted);
  text-align: center; padding-bottom: 22px;
}
.strip__track {
  display: flex; gap: 64px;
  animation: marquee 60s linear infinite;
  width: max-content;
  white-space: nowrap;
}
.strip__track--measure {
  position: absolute; visibility: hidden; pointer-events: none;
  animation: none;
}
.strip__logo {
  font-family: var(--font-display);
  font-size: 36px; letter-spacing: 0.01em; color: var(--fg);
  text-transform: uppercase; opacity: 0.85;
}
@media (max-width: 880px) {
  /* Density refinement: the ticker becomes a thinner architectural datum /
     proof strip on mobile rather than a full-scale marquee -- heading text,
     collaborator sequence, and the seamless-loop logic (which measures
     actual rendered width, so it adapts to this smaller scale
     automatically) are all unchanged. Desktop (>880px) is untouched. */
  .strip { padding: 16px 0 18px; }
  .strip__label { padding-bottom: 12px; }
  .strip__track { gap: 36px; }
  .strip__logo { font-size: 22px; }
}
@keyframes marquee {
  from { transform: translateX(0) }
  to { transform: translateX(-50%) }
}

/* ── Studio panel ────────────────────────────────────────────────────── */
.wwd {
  max-width: var(--maxw); margin: 0 auto;
  padding: 100px var(--pad);
  display: grid; grid-template-columns: 1.05fr 1fr;
  gap: 24px;
}
.wwd__panel {
  background: var(--accent); color: var(--accent-ink);
  border-radius: var(--radius);
  padding: clamp(32px, 5vw, 64px);
  display: flex; flex-direction: column; gap: 22px;
  position: relative; overflow: hidden;
  min-height: 540px;
}
.wwd__panel::before {
  content: ""; position: absolute; inset: 0;
  background-image: repeating-linear-gradient(135deg, rgba(0,0,0,0.04) 0 1px, transparent 1px 22px);
  pointer-events: none;
}
.wwd__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  font-weight: 600;
}
.wwd__title {
  font-family: var(--font-display);
  font-weight: 400; font-size: clamp(56px, 7vw, 112px);
  margin: 0; line-height: 0.9; letter-spacing: -0.01em;
  text-transform: uppercase;
}
.wwd__copy {
  font-family: var(--font-mono);
  font-size: 13px; line-height: 1.7; margin: 0; max-width: 50ch;
  color: rgba(14, 14, 12, 0.78);
}
.wwd__quote {
  border-left: 2px solid var(--ink);
  padding-left: 16px;
  font-style: italic;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink);
}
.wwd__panel .btn--solid { align-self: flex-start; margin-top: auto; }

.wwd__media { display: grid; grid-template-rows: auto 1fr 1fr; gap: 24px; }
.wwd__card {
  border-radius: var(--radius); overflow: hidden;
  position: relative; min-height: 140px;
}
.wwd__card--accent {
  background: var(--ink); color: var(--bg);
  padding: 24px; display: flex; flex-direction: column; justify-content: space-between;
  min-height: 200px;
}
.wwd__bars { display: flex; gap: 6px; align-items: end; height: 80px; }
.wwd__bars span {
  flex: 1; background: var(--accent); border-radius: 1px;
  animation: bar 1.4s ease-in-out infinite alternate;
}
.wwd__bars span:nth-child(1) { height: 30%; animation-delay: 0s; }
.wwd__bars span:nth-child(2) { height: 70%; animation-delay: 0.15s; }
.wwd__bars span:nth-child(3) { height: 50%; animation-delay: 0.3s; }
.wwd__bars span:nth-child(4) { height: 85%; animation-delay: 0.45s; }
.wwd__bars span:nth-child(5) { height: 60%; animation-delay: 0.6s; }
@keyframes bar { from { transform: scaleY(0.5) } to { transform: scaleY(1) } }
.wwd__cardLabel {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  display: flex; justify-content: space-between; opacity: 0.7;
}

.wwd__card--photo {
  display: flex; align-items: end; padding: 20px;
  min-height: 200px;
}
/* Cultural Archive_: horizontal swipe between projects. Each project's
   photo + project-number/title pill live together inside one
   .wwd__archiveFrame, so they always slide as a single unit -- there's no
   way for one project's image to pair with another's title mid-transition.
   CULTURAL ARCHIVE_ (.wwd__photoTag) is a sibling of these frames, never
   inside one, so it never moves. At rest only the current frame exists (no
   modifier class, translateX(0)); during the ~850ms transition both an
   --out (leaving) and --in (arriving) frame exist together -- the
   .is-swiping toggle (added a couple of frames after mount, see WhatWeDo in
   components.jsx) is what actually triggers the animation, so the browser
   paints the start position first. Movement only, no opacity -- both frames
   stay fully opaque throughout.
   Travel is a full +/-100%, not a short peek: outgoing goes from 0 to
   -100% while incoming goes from +100% to 0, in lockstep. That's not an
   aesthetic choice, it's the only geometry where outgoing's trailing edge
   and incoming's leading edge stay mathematically coincident at every
   instant (both equal 100%*(1 - t/T)) -- a true edge-to-edge handoff with
   no gap and no overlap. A shorter travel distance (e.g. 12%, used in an
   earlier version alongside an opacity dissolve) doesn't have this
   property: since both frames are full-card-size and .wwd__archiveFrame--in
   is later in the DOM, even a "12%" starting offset still leaves the
   arriving (opaque) frame covering the other 88% of the card from the very
   first frame, before any real animation happens -- a stacking glitch, not
   a slide. That short-travel geometry only ever worked because opacity was
   smoothly hiding the overlap; with opacity removed, a full-width slide is
   required for a clean result. */
.wwd__archiveFrame {
  position: absolute; inset: 0;
  transition: transform 850ms cubic-bezier(0.4, 0, 0.2, 1);
  transform: translateX(0);
}
.wwd__archiveFrame--in { transform: translateX(100%); transition: none; }
.wwd__archiveFrame--out.is-swiping { transform: translateX(-100%); }
.wwd__archiveFrame--in.is-swiping {
  transform: translateX(0);
  transition: transform 850ms cubic-bezier(0.4, 0, 0.2, 1);
}
/* Cultural archive card reuses the .post__img--N archive gradients (see Spotlight)
   as the instant color fallback beneath the real project photo below.
   Default object-position is plain center -- a blanket non-center value here
   previously fought individual photos (a shared "center 60%" shifted the
   crop window down the source for every project, which made Indian
   Classical/Inkquisitive's head-and-hand cropping WORSE, not better, since
   their subjects already sit in the upper-middle of a much-taller source
   image). Per-project focal fixes are separate modifier classes below,
   desktop only -- no element movement (no transform), no exposed
   background, same object-fit:cover/scale. Mobile resets everything back to
   plain center further down (Chair's own far-right override there is a
   more specific selector and still wins on top of that reset). */
.wwd__cardPhoto {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center; display: block;
  opacity: 0; transition: opacity 0.5s ease;
}
.wwd__cardPhoto.is-loaded { opacity: 1; }
/* Both source photos are ~16:9 stretched into a much wider ~3.2:1 card, so
   cover only shows ~56% of the source's height. Values set by rendering the
   actual 636x200 card box against the real source image and visually
   comparing candidates, not computed/guessed blind. Indian Classical's
   value was previously 25%, which over-corrected and sat Akshitha too high
   with excess headroom -- 45% is much closer to plain center and reads as
   a naturally balanced portrait. */
.wwd__cardPhoto--indian-classical { object-position: center 45%; }
.wwd__cardPhoto--inkquisitive { object-position: center 25%; }
/* Flat dark scrim over the real photo -- keeps the card reading as moody/
   editorial rather than a bright conventional thumbnail; both text pills
   (carouselMeta/photoTag) carry their own background so legibility never
   depends on this. Duplicated per-frame (rather than one shared layer)
   because .wwd__archiveFrame's transform gives it its own stacking context --
   nesting the tint and the meta pill inside the same frame keeps their
   z-index order (tint below, pill above) self-contained and correct
   regardless of which frame is sliding. */
.wwd__cardTint {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(160deg, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.15) 45%, rgba(0,0,0,0.55) 100%);
}
.wwd__carouselMeta {
  position: absolute; top: 16px; left: 16px; z-index: 2;
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em; font-weight: 600;
  color: white; background: rgba(0,0,0,0.45); padding: 6px 10px; border-radius: 999px;
  backdrop-filter: blur(6px); text-transform: uppercase;
}
/* Entrance fade plays only for the arriving/current frame's pill, not the
   one that's leaving -- otherwise the outgoing pill would fade-in-and-up
   while simultaneously sliding away. */
.wwd__archiveFrame:not(.wwd__archiveFrame--out) .wwd__carouselMeta {
  animation: carouselFade 0.6s ease both;
}
@keyframes carouselFade {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}
/* Collection 02 — reuses the existing AWIP product-card gradient language
   from .coll__item--1 (WEARABLE IN PROCESS_, see CollectionStrip below):
   same dark linear wash + soft yellow radial highlight blended via `screen`,
   at the same low 0.18 alpha -- already the site's established way to give a
   dark card holding garment photography some dimensionality without reading
   as a lighting effect. The radial's x-position stays centered (50%, moved
   from .coll__item--1's original 30%) since this card shows a triptych, not
   one item. Shape is responsive: desktop's wide card gets the default
   `ellipse` (its own farthest-corner sizing naturally stretches horizontally
   to match the wide box, no explicit radii needed), while the mobile
   override below restores `circle` for the roughly-square single-jersey
   card -- same colors/alpha/blend either way, only the geometry adapts. */
.wwd__card--photo-2 {
  background:
    linear-gradient(180deg, #2a2520 0%, #0a0907 100%),
    radial-gradient(ellipse at 50% 30%, rgba(240,196,25,0.18), transparent 60%);
  background-blend-mode: screen;
}
/* Desktop: Basant/Neel/Mitti side by side as one triptych composition, filling
   the card behind the fixed COLLECTION 02_ label.
   image-rendering: at this box size the source art (3213-4315px squares) is
   downscaled ~13-20x, which is well within available resolution but softens
   the jerseys' fine linework under Chromium's default minification filter --
   -webkit-optimize-contrast asks for a sharper resampling filter instead
   (no oversharpening/outline/filter hacks, no source-asset change). Reset to
   `auto` on mobile below since that's not one of the two permitted mobile
   changes. */
.wwd__jerseys { position: absolute; inset: 0; display: flex; }
.wwd__jersey { position: relative; flex: 1; overflow: hidden; }
.wwd__jerseyImg {
  width: 100%; height: 100%; object-fit: cover; object-position: top; display: block;
  image-rendering: -webkit-optimize-contrast;
}
.wwd__photoTag {
  position: relative; z-index: 2;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.18em;
  color: white; text-transform: uppercase; font-weight: 600;
  background: rgba(0,0,0,0.4); padding: 7px 11px; border-radius: 999px;
  backdrop-filter: blur(8px);
}

@media (max-width: 880px) {
  .wwd { grid-template-columns: 1fr; padding: 60px var(--pad); }
  /* Density refinement: same copy/hierarchy/CTA, just less unused space --
     trims internal padding and the gap between eyebrow/headline/body/quote/
     CTA slightly, and lets the panel settle to its natural content height
     instead of a fixed 540px floor sized for the desktop 2-column layout. */
  .wwd__panel { padding: 26px; gap: 16px; min-height: 0; }
  /* Density experiment: the yellow panel above is untouched. Below it, all
     3 existing media cards show simultaneously in ONE equal-weight row
     (no dominant/supporting hierarchy) -- same content/destinations, but
     each card's internal layout is recomposed for its ~110px width rather
     than shrunk from the desktop/stacked-mobile treatment. */
  .wwd__media { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
  .wwd__media > .wwd__card { min-height: 132px; }
  .wwd__card--accent { padding: 10px; }
  .wwd__card--photo { padding: 8px; }
  .wwd__bars { height: 34px; gap: 3px; }
  .wwd__cardLabel { flex-direction: column; gap: 2px; font-size: 7px; opacity: 0.85; }
  .wwd__carouselMeta {
    top: 8px; left: 8px; right: 8px; gap: 4px;
    font-size: 7px; padding: 4px 6px; max-width: calc(100% - 16px);
  }
  .wwd__carouselMeta span:last-child {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .wwd__photoTag { font-size: 7px; padding: 4px 6px; letter-spacing: 0.1em; }
  /* The approved mobile image positions are untouched: the desktop-only
     per-project reframes above (Indian Classical/Inkquisitive) are reset to
     center here -- their modifier classes are more specific than a bare
     .wwd__cardPhoto rule, so they need to be named explicitly, not just
     covered by resetting the base class -- then Chair_ gets its own
     approved far-right override on top of that reset. */
  .wwd__cardPhoto,
  .wwd__cardPhoto--indian-classical,
  .wwd__cardPhoto--inkquisitive {
    object-position: center;
  }
  /* The Chair_ only, mobile only: shift the visible crop window toward the
     far-right side of the existing hero image (per editorial review), not a
     new/edited/generated asset. Desktop is untouched -- no rule for this
     class exists outside this media query. */
  .wwd__cardPhoto--chair { object-position: 100% 50%; }
  /* Mobile: the card is roughly square, so the desktop triptych doesn't fit --
     show one jersey at a time and crossfade (Basant -> Neel -> Mitti -> repeat),
     same three images/order, no separate data source.
     Root cause of the earlier cropping bug: .wwd__card--photo-2 is `display:
     flex` (row, inherited from .wwd__card--photo) with TWO normal-flow
     children -- .wwd__jerseys and .wwd__photoTag -- so they competed for
     row width and the label's content-width squeezed .wwd__jerseys down to
     a sliver (measured ~34px of a ~111px-wide card), which object-fit:cover
     then blew up/cropped almost entirely. Fixing the sizing means switching
     this card to a column flex (image area on top, label below) instead of
     patching the symptom with cover/positioning tricks. */
  .wwd__card--photo-2 {
    flex-direction: column; align-items: stretch; gap: 4px;
    /* Same colors/alpha/blend as desktop -- shape only reverts to `circle`
       for this roughly-square single-jersey card. */
    background:
      linear-gradient(180deg, #2a2520 0%, #0a0907 100%),
      radial-gradient(circle at 50% 30%, rgba(240,196,25,0.18), transparent 60%);
    background-blend-mode: screen;
  }
  .wwd__jerseys { position: relative; flex: 1; min-height: 0; width: auto; height: auto; }
  /* Short dissolve, not a blink: JS (see WhatWeDo in components.jsx) starts
     the outgoing garment's fade-out immediately, then delays the incoming
     garment's fade-in by JERSEY_OVERLAP_MS (195ms) so it begins while the
     outgoing one is still mid-fade -- this 455ms transition just matches
     JERSEY_FADE_MS, it isn't sequencing the overlap by itself. */
  .wwd__jersey {
    position: absolute; inset: 0; flex: none;
    opacity: 0; transition: opacity 0.455s ease;
  }
  .wwd__jersey.is-active { opacity: 1; }
  /* CONTAIN, not cover: the complete garment (collar, both sleeves, hem)
     must always be visible -- letterboxing/negative space is intentional.
     image-rendering reset to `auto`: the desktop clarity fix above isn't one
     of the two permitted mobile changes, so this keeps mobile unaffected. */
  .wwd__jerseyImg { object-fit: contain; object-position: center; image-rendering: auto; }
  /* Scoped to Collection 02_ only -- this card's mobile layout is a column
     (image on top, label below), so its tag needs flex-start (left) instead
     of the row-based .wwd__card--photo's `align-items: end`. Cultural
     Archive_ (the other .wwd__photoTag) stays row-based and must keep
     `align-items: end` from its parent to sit at the bottom -- an earlier,
     unscoped version of this rule caught that tag too and pinned it to the
     top, overlapping the project-number pill; scoping it here is the fix. */
  .wwd__card--photo-2 .wwd__photoTag { align-self: flex-start; }
}

/* ── Lanes / services ────────────────────────────────────────────────── */
.services {
  background: var(--ink); color: var(--bg);
  padding: 120px var(--pad);
}
.services__head {
  max-width: var(--maxw); margin: 0 auto 56px;
  display: grid; grid-template-columns: auto 1fr; gap: 56px;
  align-items: end;
}
.services__head > div:first-child { display: flex; flex-direction: column; gap: 14px; }
.services__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  color: rgba(244, 241, 234, 0.5); font-weight: 600;
}
.services__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(56px, 7vw, 112px); margin: 0;
  line-height: 0.9; letter-spacing: -0.01em;
  text-transform: uppercase;
}
.services__lede {
  font-family: var(--font-mono); font-size: 13px; line-height: 1.7;
  color: rgba(244, 241, 234, 0.65); max-width: 48ch; margin: 0;
  padding-bottom: 12px;
}
@media (max-width: 880px) {
  .services__head { grid-template-columns: 1fr; gap: 24px; }
}
.services__list {
  list-style: none; padding: 0; margin: 0;
  max-width: var(--maxw); margin: 0 auto;
  border-top: 1px solid rgba(244, 241, 234, 0.14);
}
.services__item {
  display: grid;
  grid-template-columns: 80px 320px 1fr auto auto;
  gap: 32px; align-items: center;
  padding: 28px 0;
  border-bottom: 1px solid rgba(244, 241, 234, 0.14);
  cursor: pointer; outline: none;
  transition: padding 0.3s ease, color 0.3s ease;
  /* .services__item is now the <a> itself (was a plain <li> before) --
     reset link defaults so making the row a real link doesn't change how
     it looks. No new visual treatment. */
  text-decoration: none; color: inherit;
}
.services__item.is-active { padding-left: 24px; }
.services__item.is-active .services__name { color: var(--accent); }
.services__num {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  color: rgba(244, 241, 234, 0.5); font-weight: 600;
}
.services__name {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(28px, 3.6vw, 52px);
  letter-spacing: -0.005em; line-height: 1;
  text-transform: uppercase;
  transition: color 0.3s ease;
}
.services__desc {
  font-family: var(--font-mono);
  font-size: 12px; line-height: 1.65; color: rgba(244, 241, 234, 0.65);
  max-width: 56ch;
}
.services__tags {
  display: flex; gap: 6px; flex-wrap: wrap;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
}
.services__tags em {
  font-style: normal; padding: 5px 9px; border-radius: 999px;
  background: rgba(244, 241, 234, 0.08);
  border: 1px solid rgba(244, 241, 234, 0.14);
  color: rgba(244, 241, 234, 0.7);
}
.services__arrow {
  width: 36px; height: 36px; border-radius: 999px;
  border: 1px solid rgba(244, 241, 234, 0.2);
  display: inline-flex; align-items: center; justify-content: center;
  color: rgba(244, 241, 234, 0.5);
  transition: background 0.2s, color 0.2s, transform 0.2s;
}
.services__item.is-active .services__arrow {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
  transform: translateX(4px);
}

@media (max-width: 1100px) {
  .services__item {
    grid-template-columns: 60px 1fr auto;
    grid-template-areas:
      "num name arrow"
      ".   desc desc"
      ".   tags tags";
    gap: 12px 20px;
  }
  .services__num { grid-area: num; }
  .services__name { grid-area: name; }
  .services__desc { grid-area: desc; }
  .services__tags { grid-area: tags; }
  .services__arrow {
    grid-area: arrow;
    /* Mobile conversion pass: the whole row is already one tap target, but
       the is-active fill (its main "this is clickable" signal) is only
       ever reached via hover/focus, which touch never triggers at rest.
       Bring the resting circle up to ~44px and a touch stronger resting
       contrast so it reads as actionable without needing that state --
       same arrow/icon, no new chrome. Desktop (>1100px) keeps the exact
       36px/current-contrast circle unchanged. */
    width: 44px; height: 44px;
    border-color: rgba(244, 241, 234, 0.35);
    color: rgba(244, 241, 234, 0.75);
  }
}

/* ── Spotlight / Archive ─────────────────────────────────────────────── */
.spotlight {
  background: var(--bg);
  padding: 120px var(--pad);
}
.spotlight__head {
  max-width: var(--maxw); margin: 0 auto 56px;
  display: flex; justify-content: space-between; align-items: end;
  gap: 24px; flex-wrap: wrap;
}
.spotlight__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  color: var(--muted); margin-bottom: 14px; font-weight: 600;
}
.spotlight__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(56px, 7vw, 112px); margin: 0;
  line-height: 0.9; letter-spacing: -0.01em;
  text-transform: uppercase;
}
.spotlight__all {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  font-weight: 600;
  padding: 10px 0; border-bottom: 1px solid var(--fg);
}
.spotlight__grid {
  max-width: var(--maxw); margin: 0 auto;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}

.post {
  background: var(--bg-2);
  border-radius: var(--radius); overflow: hidden;
  display: flex; flex-direction: column;
  border: 1px solid var(--line);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  cursor: pointer;
}
.post:hover { transform: translateY(-4px); box-shadow: 0 24px 48px -24px rgba(0,0,0,0.18); }
.post__img {
  position: relative; aspect-ratio: 4/5;
  display: flex; flex-direction: column; padding: 14px;
}
/* post__img--N gradients are now the pre-photo/no-thumbnail fallback rather
   than the visible cover -- .post__photo (real project thumbnail, same
   object-fit: cover treatment as .archive-card__photo) paints over them
   once loaded. Kept as-is so a card with no thumbnail still reads as a
   deliberate flat-color field instead of an empty box. */
.post__img--1 { background: linear-gradient(180deg, #6b3010 0%, #2a1006 70%, #0e0604 100%); }
.post__img--2 { background: linear-gradient(180deg, #1a1a1a 0%, #050505 100%), radial-gradient(ellipse at 50% 65%, rgba(240,196,25,0.5), transparent 55%); background-blend-mode: screen; }
.post__img--3 { background: linear-gradient(135deg, #2a2825 0%, #5a4830 50%, #0e0e0c 100%); }
.post__img--4 { background: linear-gradient(180deg, #c19c3a 0%, #6b4f1a 60%, #1a0f04 100%); }
.post__photo {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  opacity: 0; transition: opacity 0.5s ease;
}
.post__photo.is-loaded { opacity: 1; }
.post__num, .post__cat {
  /* Sit above .post__photo, which is an absolutely-positioned sibling painted
     after normal-flow content by default -- these two need to stay on top. */
  position: relative; z-index: 1;
}
.post__num {
  align-self: flex-start;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.18em; font-weight: 600;
  color: white; background: rgba(0,0,0,0.5); padding: 5px 9px; border-radius: 4px;
  backdrop-filter: blur(6px);
}
.post__cat {
  margin-top: auto; align-self: flex-start;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em;
  color: var(--accent-ink); background: var(--accent);
  padding: 5px 9px; border-radius: 4px; text-transform: uppercase; font-weight: 700;
}
.post__body {
  padding: 18px; display: flex; flex-direction: column; gap: 14px; flex: 1;
}
.post__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: 24px; line-height: 1; letter-spacing: -0.005em;
  margin: 0; color: var(--fg);
  text-wrap: pretty; text-transform: uppercase;
}
.post__meta {
  margin-top: auto; display: flex; justify-content: space-between; align-items: center;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  color: var(--muted);
}
.post__arrow {
  width: 28px; height: 28px; border-radius: 999px;
  border: 1px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--fg); transition: background 0.2s, color 0.2s;
}
.post:hover .post__arrow { background: var(--accent); border-color: var(--accent); }

@media (max-width: 1024px) {
  .spotlight__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 540px) {
  /* All 4 PROJECT_INDEX-derived cards stay visible in a compact 2x2 grid
     instead of collapsing to one giant column -- "four glimpses of the
     body of work," not four full case studies to scroll through. Same
     data/order/routes/images/metadata throughout.
     Density refinement: a moderately squarer image crop (was 4/5, a tall
     portrait) plus tighter body padding/gaps bring these to a middle scale
     -- clearly larger than the 3-across Wearable/Studio cards, clearly
     more compact than a full project-hero card. Photography is still the
     dominant read; title/category/date remain fully legible. */
  .spotlight__grid { gap: 12px; }
  .post__img { padding: 10px; aspect-ratio: 1/1; }
  .post__num { font-size: 9px; padding: 4px 7px; }
  .post__cat { font-size: 9px; padding: 4px 7px; }
  .post__body { padding: 10px; gap: 4px; }
  .post__title { font-size: 15px; }
  .post__meta { font-size: 9px; }
  .post__arrow { width: 24px; height: 24px; }
}

/* ── Collection ──────────────────────────────────────────────────────── */
.coll {
  background: var(--ink); color: var(--bg);
  padding: 120px var(--pad);
  border-top: 1px solid rgba(244,241,234,0.08);
}
.coll__inner {
  max-width: var(--maxw); margin: 0 auto;
  display: flex; flex-direction: column; gap: 56px;
}
.coll__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  color: rgba(244,241,234,0.5); font-weight: 600; margin-bottom: 14px;
}
.coll__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(56px, 7vw, 112px); margin: 0;
  line-height: 0.9; letter-spacing: -0.01em;
  text-transform: uppercase;
}
.coll__grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;
}
.coll__item {
  aspect-ratio: 3/4;
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 22px;
  border-radius: var(--radius);
  position: relative; overflow: hidden;
  text-decoration: none; color: inherit;
}
/* coll__item--N gradients are the pre-photo/no-image fallback -- .coll__photo
   (real product photo, same object-fit: cover crop as Recent Work's
   .post__photo) paints over them once loaded. */
.coll__item--1 { background: linear-gradient(180deg, #2a2520 0%, #0a0907 100%), radial-gradient(circle at 30% 30%, rgba(240,196,25,0.18), transparent 60%); background-blend-mode: screen; }
.coll__item--2 { background: linear-gradient(180deg, #c19c3a 0%, #6b4f1a 60%, #1a0f04 100%); }
.coll__item--3 { background: linear-gradient(180deg, #1a1a18 0%, #050504 100%); border: 1px solid rgba(244,241,234,0.08); }
.coll__photo {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  opacity: 0; transition: opacity 0.5s ease;
}
.coll__photo.is-loaded { opacity: 0.8; }
.coll__item:hover .coll__photo.is-loaded { opacity: 1; }
.coll__top, .coll__bottom {
  /* Sit above .coll__photo, an absolutely-positioned sibling painted after
     normal-flow content by default. */
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 8px;
}
.coll__bottom { flex-direction: column; align-items: flex-start; gap: 4px; }
.coll__sku {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.18em; font-weight: 600;
  color: rgba(255,255,255,0.6);
  text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}
.coll__soldout {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; font-weight: 600;
  color: var(--accent); text-transform: uppercase;
}
.coll__name {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(24px, 2.4vw, 36px); line-height: 1;
  text-transform: uppercase; color: var(--bg);
  text-shadow: 0 1px 3px rgba(0,0,0,0.5), 0 2px 14px rgba(0,0,0,0.35);
}
.coll__price {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.04em;
  color: rgba(244,241,234,0.75);
  text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}
.coll .btn--ghost-dark { align-self: flex-start; }
/* Editorial nav card for the mobile 4th grid cell -- hidden entirely on
   desktop (the real .coll__grid there is a full 3-up row with no 4th
   cell, and the standalone SHOP PRODUCTS_ button stays as the only CTA).
   .coll__name is reused as-is for the label; only the row wrapper and
   arrow circle are new, and the arrow circle mirrors the same bordered-
   circle treatment already used elsewhere (e.g. .services__arrow). */
.coll__cta { display: none; }
.coll__ctaRow {
  margin-top: auto; width: 100%;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 8px;
}
.coll__ctaArrow {
  flex-shrink: 0;
  width: 28px; height: 28px; border-radius: 999px;
  border: 1px solid rgba(244, 241, 234, 0.3);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--bg);
}
@media (max-width: 720px) {
  /* 2-column grid experiment (replaces the flat 3-across row): a true
     2-column grid gives every card substantially more width for the
     graphic/silhouette to actually register, while keeping all 3 cards
     exactly equal in size/hierarchy -- same
     config/order/Shopify data/routes/pricing/availability/backgrounds/
     aspect-ratio (photography crops exactly as it already does, just
     larger). 3 items in a 2-column grid naturally leaves row 2's second
     cell empty via normal grid auto-flow -- no span, no centering, no
     placeholder; that's genuine, unstyled negative space. */
  .coll__grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .coll__item { padding: 10px; }
  .coll__top, .coll__bottom { gap: 4px; }
  .coll__bottom { gap: 2px; }
  .coll__sku, .coll__soldout { font-size: 8px; }
  .coll__name { font-size: 11px; }
  .coll__price { font-size: 9px; }
  /* Checkerboard/CTA-cell refinement: Eagle Wings (position 3) takes on
     Shiva Jersey's exact yellow/gold background -- mobile only, image/
     crop/data untouched -- so the 2x2 reads DARK/YELLOW top,
     YELLOW/DARK bottom. The previously-empty 4th cell becomes the
     .coll__cta nav card (dark/brown, matching position 1's background),
     and the standalone button below the grid is hidden since the new
     cell now performs the exact same action. */
  .coll__item--3 { background: linear-gradient(180deg, #c19c3a 0%, #6b4f1a 60%, #1a0f04 100%); border: none; }
  .coll__cta { display: flex; }
  .coll .btn--ghost-dark { display: none; }
}

/* ── CTA ─────────────────────────────────────────────────────────────── */
.cta {
  padding: 140px var(--pad);
  background: var(--accent); color: var(--accent-ink);
  position: relative; overflow: hidden;
}
.cta::before {
  content: ""; position: absolute; inset: 0;
  background-image: repeating-linear-gradient(135deg, rgba(0,0,0,0.04) 0 1px, transparent 1px 22px);
}
.cta__inner {
  max-width: var(--maxw); margin: 0 auto;
  /* minmax(0, 1fr), not 1fr: a bare 1fr track still floors at its content's
     min-content width, so a long unbroken headline word (e.g. "CONVERSATION_"
     on /contact/) forces the track wider than its fair half and pushes the
     form past the viewport edge. minmax(0, ..) removes that floor. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 100px;
  align-items: start;
  position: relative;
}
.cta__intro { display: flex; flex-direction: column; gap: 24px; }
.cta__right { display: flex; flex-direction: column; }
@media (max-width: 880px) {
  .cta__inner { grid-template-columns: 1fr; gap: 24px; }
}
.cta__eyebrow { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em; font-weight: 600; }
.cta__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(64px, 11vw, 200px); margin: 0;
  line-height: 0.86; letter-spacing: -0.01em;
  text-transform: uppercase;
}
@media (min-width: 881px) {
  /* Headline is sized for full-section width above; scale it down to fit
     the intro column here. Must come after the base .cta__title rule --
     same specificity, so source order decides, and this needs to win. */
  .cta__title { font-size: clamp(40px, 4vw, 84px); }
  /* .cta__success has no eyebrow above it the way .cta__intro's title does,
     so with .cta__inner's align-items: start it was sitting 40.5px too high
     against .cta__title's top (.cta__eyebrow's 16.5px line-height + the
     24px gap .cta__intro's flex column puts beneath it) -- pushing it out
     above the primary headline instead of matching it. Desktop-only since
     .cta__inner stacks to a single column below 881px, where the two no
     longer sit side by side and this offset would just add unwanted gap. */
  .cta__success { margin-top: 40.5px; }
}
.cta__sub {
  font-family: var(--font-mono);
  max-width: 56ch; font-size: 13px; line-height: 1.65; margin: 8px 0;
  color: rgba(14, 14, 12, 0.78);
}
.cta .btn--accent {
  background: var(--ink); color: var(--bg); align-self: flex-start;
}
.cta .btn--accent:hover { background: #2a2724; box-shadow: 4px 4px 0 rgba(0,0,0,0.15); }

.cta__form {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 20px 24px; max-width: 720px; margin-top: 8px;
}
.cta__field { display: flex; flex-direction: column; gap: 8px; }
.cta__field--full { grid-column: 1 / -1; }
.cta__field label {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; font-weight: 600; color: var(--accent-ink);
}
.cta__field input, .cta__field textarea {
  font: 13px/1.5 var(--font-mono); padding: 12px 14px;
  border: 1px solid rgba(14, 14, 12, 0.3); border-radius: var(--radius);
  background: rgba(244, 241, 234, 0.55); color: var(--ink); outline: none;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.cta__field input:focus, .cta__field textarea:focus {
  border-color: var(--ink); background: rgba(244, 241, 234, 0.85);
}
.cta__field textarea { resize: vertical; min-height: 96px; font-family: var(--font-body); }
.cta__submit { grid-column: 1 / -1; justify-self: start; margin-top: 4px; }
.cta__submit:disabled { opacity: 0.6; cursor: default; }

/* Honeypot: real users must never see, tab to, or hear this field announced --
   off-screen (not display:none/visibility:hidden), since some unsophisticated
   spam bots specifically skip those two but still blindly fill anything else
   they find in the form. Doesn't affect the visible field layout at all. */
.cta__honeypot {
  position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden;
}

.cta__error {
  grid-column: 1 / -1; margin: 0;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6;
  color: rgba(14, 14, 12, 0.85);
}
.cta__error strong { text-transform: uppercase; letter-spacing: 0.06em; }
.cta__error a { color: inherit; text-decoration: underline; }

.cta__success { display: flex; flex-direction: column; gap: 14px; max-width: 56ch; animation: fadeUp 0.6s ease both; }
.cta__successTitle {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(40px, 5vw, 64px); line-height: 0.9;
  letter-spacing: -0.01em; text-transform: uppercase;
}
.cta__successCopy {
  font-family: var(--font-mono); font-size: 13px; line-height: 1.7;
  color: rgba(14, 14, 12, 0.78); margin: 0;
}

@media (max-width: 720px) {
  .cta__form { grid-template-columns: 1fr; }
}

/* Mobile conversion pass: the 120px/140px vertical section padding above is
   sized for desktop and, applied unchanged to a ~390-430px phone, reads as
   noticeably excessive dead space relative to the viewport. This is a light
   reduction only (roughly 30-40%, not a squeeze) -- chapter separation,
   editorial rhythm, and breathing room are still clearly present, just no
   longer desktop-scaled. Desktop (>640px) padding is completely untouched. */
@media (max-width: 640px) {
  /* Verified via computed layout: the space between each section's own top
     edge (the visual "divider" -- a background-color change, or Wearable's
     literal 1px border-top) and its eyebrow tag is exactly this
     padding-top value, nothing else. Tightened further than the bottom
     padding specifically to shorten that lead-in without touching the
     section's internal composition (head->content spacing, grid, cards)
     at all -- bottom padding stays 72px. */
  .services { padding-top: 40px; padding-bottom: 72px; }
  .spotlight { padding-top: 40px; padding-bottom: 72px; }
  .coll { padding-top: 40px; padding-bottom: 72px; }
  .cta { padding-top: 88px; padding-bottom: 88px; }
}

/* ── Footer ──────────────────────────────────────────────────────────── */
.footer {
  background: var(--ink); color: var(--bg);
  padding: 40px var(--pad) 24px;
}
.footer__top {
  max-width: var(--maxw); margin: 0 auto;
  display: grid; grid-template-columns: 1fr 1.4fr;
  gap: 48px; align-items: start;
  padding-bottom: 24px;
  border-bottom: 1px solid rgba(244, 241, 234, 0.14);
}
.footer__brand { display: flex; flex-direction: column; gap: 12px; max-width: 40ch; }
.footer__desc {
  font-family: var(--font-mono); font-size: 11px; line-height: 1.6;
  color: rgba(244, 241, 234, 0.55); margin: 0;
}
/* footer__tagline: same text system as footer__desc (mono, same 11px size)
   so it reads as a stronger LINE within the body copy, not a separate
   headline -- weight and full-opacity ink do the emphasis work instead of
   size. footer__kicker stays the smallest/quietest close beneath it (most
   muted, widest tracking). */
.footer__tagline {
  font-family: var(--font-mono); font-weight: 700;
  font-size: 11px; line-height: 1.6; letter-spacing: 0.01em;
  text-transform: uppercase; color: var(--bg); margin: 0;
}
.footer__kicker {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.2em;
  text-transform: uppercase; color: rgba(244, 241, 234, 0.5);
}
.footer__cols {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px;
}
.footer__cols > div { display: flex; flex-direction: column; gap: 8px; }
.footer__h {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.2em;
  color: rgba(244, 241, 234, 0.5); text-transform: uppercase;
  margin-bottom: 6px; font-weight: 600;
}
.footer__cols a {
  font-family: var(--font-display); font-size: 22px;
  color: var(--bg); transition: color 0.2s; padding: 2px 0;
  text-transform: uppercase; letter-spacing: -0.005em;
}
.footer__cols a:hover { color: var(--accent); }
.footer__bottom {
  max-width: var(--maxw); margin: 12px auto 0;
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  color: rgba(244, 241, 234, 0.5);
}
/* footer__legal: discreet policy links in the existing bottom/legal area --
   same quiet mono treatment as footer__bottom, not a new visual block. The
   button matches the surrounding links' look exactly (reset to plain text)
   so "Cookie Preferences_" doesn't read as a separate UI control. */
/* footer__legal: true fine-print scale -- deliberately smaller than
   footer__bottom's tiny copyright/version metadata (8px vs 11px), not just
   equal to it, per the second size-reduction pass. Color/opacity is
   UNCHANGED at 0.5 -- that's the minimum that still clears WCAG AA contrast
   against --ink regardless of font size (contrast ratio doesn't depend on
   size below the 18px/14px-bold "large text" threshold, so 0.5 remains the
   floor at 8px too) -- "quieter" is achieved through the smaller visible
   text, not by weakening contrast below the readability floor. No uppercase
   transform (labels are already sentence-case in components.jsx, e.g.
   "Privacy_"). Vertical padding on each link/button is INCREASED versus the
   text-size reduction (6px vs the prior 5px) so the tap target stays at the
   same ~20px height even though the visible glyph is smaller -- tap comfort
   is intentionally decoupled from visible text size. */
.footer__legal {
  max-width: var(--maxw); margin: 16px auto 0;
  display: flex; flex-wrap: wrap; gap: 2px 10px;
  font-family: var(--font-mono); font-size: 8px; letter-spacing: 0.04em;
  color: rgba(244, 241, 234, 0.5);
}
.footer__legal a, .footer__legal button {
  background: none; border: none; padding: 6px 2px; margin: -6px -2px; cursor: pointer;
  font: inherit; letter-spacing: inherit;
  color: rgba(244, 241, 234, 0.5); transition: color 0.2s;
  display: inline-block; line-height: 1;
}
.footer__legal a:hover, .footer__legal button:hover { color: var(--bg); }
@media (max-width: 880px) {
  /* Density refinement, same pattern as the other sections' own mobile
     trims (e.g. .wwd/.services above) -- same content, tighter rhythm.
     .footer__top's 60px gap/padding-bottom double as the vertical space
     between the stacked brand block and nav columns once this collapses to
     a single column, and .footer__cols's 32px gap becomes its mobile
     row-gap too, so both were the biggest contributors to excess mobile
     footer height. */
  .footer { padding-top: 32px; padding-bottom: 16px; }
  .footer__top { grid-template-columns: 1fr; gap: 20px; padding-bottom: 20px; }
  .footer__cols { grid-template-columns: repeat(2, 1fr); gap: 20px; }
  .footer__cols > div { gap: 6px; }
  .footer__bottom { flex-direction: column; gap: 6px; margin-top: 10px; }
}

/* ── Side menu ───────────────────────────────────────────────────────── */
.menu { position: fixed; inset: 0; z-index: 100; pointer-events: none; }
.menu--open { pointer-events: auto; }
.menu__backdrop {
  position: absolute; inset: 0;
  background: rgba(14, 14, 12, 0.55);
  backdrop-filter: blur(8px);
  opacity: 0; transition: opacity 0.3s ease;
}
.menu--open .menu__backdrop { opacity: 1; }
.menu__panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(460px, 100%);
  background: var(--ink); color: var(--bg);
  display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.menu--open .menu__panel { transform: translateX(0); }
.menu__head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 24px var(--pad);
  border-bottom: 1px solid rgba(244, 241, 234, 0.14);
}
.menu__close {
  width: 44px; height: 44px; border-radius: 999px; border: 1px solid rgba(244, 241, 234, 0.2);
  background: transparent; color: inherit; cursor: pointer;
}
.menu__close:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.menu__links { flex: 1; display: flex; flex-direction: column; padding: 32px var(--pad); gap: 4px; }
.menu__links a {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 18px;
  padding: 18px 0; border-bottom: 1px solid rgba(244, 241, 234, 0.1);
  font-family: var(--font-display); font-size: 38px;
  letter-spacing: -0.005em;
  text-transform: uppercase;
  opacity: 0; transform: translateX(20px);
}
.menu--open .menu__links a { animation: slideIn 0.5s ease both; }
@keyframes slideIn {
  from { opacity: 0; transform: translateX(20px); }
  to { opacity: 1; transform: translateX(0); }
}
.menu__links a:hover { color: var(--accent); }
.menu__links a:hover svg { transform: translateX(4px); }
.menu__num {
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: 0.14em; color: rgba(244, 241, 234, 0.5);
  font-weight: 600;
}
.menu__links svg { transition: transform 0.2s; }
.menu__foot {
  padding: 24px var(--pad);
  border-top: 1px solid rgba(244, 241, 234, 0.14);
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  color: rgba(244, 241, 234, 0.5);
}

/* ── Reveal on scroll ────────────────────────────────────────────────── */
.reveal { opacity: 0; transform: translateY(30px); transition: opacity 0.8s ease, transform 0.8s ease; }
.reveal.in { opacity: 1; transform: translateY(0); }

/* ── Archive page ────────────────────────────────────────────────────── */
.archive-page { padding-top: 140px; }
.archive-head { max-width: var(--maxw); margin: 0 auto 56px; padding: 0 var(--pad); }
.archive-head__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  color: var(--muted); margin-bottom: 14px; font-weight: 600;
}
.archive-head__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(56px, 7vw, 112px); margin: 0;
  line-height: 0.9; letter-spacing: -0.01em;
  text-transform: uppercase;
}

.archive-grid {
  max-width: var(--maxw); margin: 0 auto;
  padding: 0 var(--pad) 120px;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}
.archive-card {
  position: relative; display: block;
  border-radius: var(--radius); overflow: hidden;
}
.archive-card__img {
  position: relative; aspect-ratio: 1 / 1;
  display: flex; align-items: flex-end; padding: 18px;
}
/* Starts invisible and fades in once decoded (ArchivePhoto in components.jsx sets
   is-loaded via onLoad, checked against img.complete for already-cached images).
   The archive-tint color layer below sits on top regardless, so the grid already
   reads as a complete field of color before this ever resolves — this fade is a
   quiet finish for the photo underneath, not something the page waits on. */
.archive-card__photo {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  opacity: 0; transition: opacity 0.5s ease;
}
.archive-card__photo.is-loaded { opacity: 1; }
/* archive-card__tint: one flat archive-tint-N swatch (see :root) layered directly
   over the real thumbnail — no gradient, no second darkening layer. At rest the
   archive reads as a deliberately composed field of color (COLOR FIRST); on
   hover/focus the tint recedes almost entirely to reveal the photograph and the
   title (IMAGE FIRST). The title carries its own text-shadow for legibility once
   it's sitting directly over the photo, so no separate scrim element is needed. */
.archive-card__tint {
  position: absolute; inset: 0; z-index: 1;
  opacity: 0.86;
  transition: opacity 0.45s ease;
}
.archive-tint--1 { background: var(--archive-tint-1); }
.archive-tint--2 { background: var(--archive-tint-2); }
.archive-tint--3 { background: var(--archive-tint-3); }
.archive-tint--4 { background: var(--archive-tint-4); }
.archive-card:hover .archive-card__tint,
.archive-card:focus-visible .archive-card__tint { opacity: 0.08; }

.archive-card__title {
  position: relative; z-index: 3;
  font-family: var(--font-display); font-weight: 400;
  font-size: 20px; line-height: 1.05; letter-spacing: -0.005em;
  color: #f4f1ea; text-transform: uppercase; text-wrap: pretty;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5), 0 2px 14px rgba(0, 0, 0, 0.35);
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
.archive-card:hover .archive-card__title,
.archive-card:focus-visible .archive-card__title { opacity: 1; transform: translateY(0); }

/* Touch devices have no hover — keep project identity legible by default: a
   lighter resting tint and an always-visible title, rather than requiring a tap
   to reveal what the card even is. */
@media (hover: none) {
  .archive-card__tint { opacity: 0.45; }
  .archive-card__title { opacity: 1; transform: none; }
}

@media (max-width: 1024px) {
  .archive-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 700px) {
  .archive-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .archive-page { padding-top: 110px; }
}

/* ── Project page ────────────────────────────────────────────────────── */
.proj-hero {
  position: relative; min-height: 72vh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 140px var(--pad) 56px;
  color: var(--bg); background: var(--ink);
  overflow: hidden;
}
.proj-hero__media { position: absolute; inset: 0; z-index: 0; display: flex; align-items: center; justify-content: center; }
.proj-hero__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.proj-hero__placeholder {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.16em;
  color: rgba(244, 241, 234, 0.6); text-transform: uppercase;
}
.proj-hero__meta { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 10px; }

/* proj-hero--photo: a real hero image is present. The image is shown at its full,
   native aspect ratio (never cropped) — the hero box sizes itself to the image,
   rather than the image being force-fit (object-fit: cover) into a fixed-height box.
   Height is capped (not cropped): width/height both resolve from the image's own
   intrinsic ratio bounded by max-width/max-height, so a wide image runs edge to
   edge while a taller one simply renders narrower and centered rather than tall —
   the container adapts to the image, the image is never touched. */
.proj-hero--photo { display: block; padding: 0; min-height: 0; }
.proj-hero--photo .proj-hero__media {
  position: relative; inset: auto; display: block; line-height: 0; text-align: center;
}
.proj-hero--photo .proj-hero__media img {
  display: block; width: auto; height: auto;
  max-width: 100%; max-height: 64vh;
  object-fit: initial; margin: 0 auto;
}
.proj-hero--photo .proj-hero__media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 55%, rgba(0, 0, 0, 0.65) 100%);
}
.proj-hero--photo .proj-hero__meta {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 0 var(--pad) 40px;
}
@media (max-width: 700px) {
  .proj-hero--photo .proj-hero__meta { padding-bottom: 22px; }
}
.proj-hero__num {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.16em; opacity: 0.75;
}
.proj-hero__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(48px, 8vw, 120px); line-height: 0.92;
  letter-spacing: -0.01em; text-transform: uppercase; margin: 0;
}

.proj-meta {
  max-width: var(--maxw); margin: 0 auto; padding: 40px var(--pad);
  display: flex; flex-wrap: wrap; justify-content: center; gap: 28px 56px;
  border-bottom: 1px solid var(--line);
}
.proj-meta__row { display: flex; flex-direction: column; gap: 6px; margin: 0; }
.proj-meta__row dt {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted); margin: 0;
}
.proj-meta__row dd { margin: 0; font-family: var(--font-body); font-size: 15px; color: var(--fg); }

.proj-opening { max-width: 900px; margin: 0 auto; padding: 88px var(--pad) 24px; }
.proj-opening__text {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(28px, 4vw, 44px); line-height: 1.15;
  letter-spacing: -0.005em; margin: 0;
}

.proj-section { padding: var(--section-rhythm) 0; border-top: 1px solid var(--line); }
.proj-section__inner { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--pad); }
.proj-section__head { display: flex; align-items: baseline; gap: 16px; margin-bottom: 40px; flex-wrap: wrap; }
.proj-section__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  color: var(--muted); font-weight: 600;
}
.proj-section__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 4vw, 56px); text-transform: uppercase; margin: 0; line-height: 1;
}
.proj-section__blocks { display: flex; flex-direction: column; gap: var(--block-gap); }

/* proj-section--compact: for short, closing-passage sections (e.g. Impact,
   Reflection) whose content is a handful of one-line statements — height should
   be driven by that content, not stretched to match a full narrative section. */
.proj-section--compact { padding: clamp(28px, 4vw, 52px) 0; }
.proj-section--compact .proj-section__head { margin-bottom: 20px; }
.proj-section--compact .proj-section__blocks { gap: 12px; }

.proj-block--text {
  font-family: var(--font-body); font-size: 18px; line-height: 1.75;
  color: var(--fg); margin: 0; max-width: 720px;
}
.proj-block--quote {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(24px, 3vw, 36px); line-height: 1.25;
  margin: 0; padding-left: 24px; border-left: 3px solid var(--accent);
  max-width: 720px; text-transform: none;
}
/* Offset/centered single images: full width on mobile (no artificial shrinking);
   scaled to a deliberately large, still-clearly-offset proportion on desktop —
   "offset" is a position, not a size. */
.proj-block--half, .proj-block--right, .proj-block--center { max-width: none; }
@media (min-width: 700px) {
  .proj-block--half { max-width: 64%; }
  .proj-block--right { max-width: 64%; margin-left: auto; }
  .proj-block--center { max-width: 70%; margin-left: auto; margin-right: auto; }
}

/* proj-block--bleed: a single image given maximum, edge-to-edge presence within
   the content column (cancels the section's own side padding) — reserved for
   the one or two images per project that should feel immersive, not routine. */
.proj-block--bleed { max-width: none; margin-left: calc(var(--pad) * -1); margin-right: calc(var(--pad) * -1); }

.proj-block--pair { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.proj-block--triple { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; }
@media (max-width: 700px) {
  .proj-block--pair, .proj-block--triple { grid-template-columns: 1fr; }
}
@media (min-width: 701px) and (max-width: 1000px) {
  .proj-block--triple { grid-template-columns: 1fr 1fr; }
}

/* proj-block--split: one image + one paragraph, side by side (editorial pairing,
   not a card) — reusable across any project. side is which column the image
   visually sits in; the image is always given more weight (~55/45) than the
   text regardless of side, via flex-grow rather than a fixed grid track, so
   swapping sides never shrinks the image. Collapses to an image-then-text
   stack under 860px. */
.proj-block--split { display: flex; align-items: center; gap: 40px; }
.proj-block--split-right .proj-block__figure { order: 2; }
.proj-block--split-right .proj-block__text { order: 1; }
.proj-block__figure { flex: 1.2 1 0; min-width: 0; }
.proj-block--split .proj-block__text {
  flex: 1 1 0; min-width: 0;
  font-family: var(--font-body); font-size: 18px; line-height: 1.75;
  color: var(--fg); margin: 0;
}
@media (max-width: 860px) {
  .proj-block--split { flex-direction: column; align-items: stretch; gap: 20px; }
  .proj-block--split-right .proj-block__figure,
  .proj-block--split-right .proj-block__text { order: initial; }
}

.asset-slot { margin: 0; }
.asset-slot__frame {
  position: relative; aspect-ratio: 4 / 3; border-radius: var(--radius);
  overflow: hidden; display: flex; align-items: center; justify-content: center;
}
.asset-slot__frame img,
.asset-slot__frame video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* asset-slot__frame--photo: a real image (or video) is present. Shown at its full,
   native aspect ratio (never cropped) — the frame sizes itself to the media instead
   of the media being force-fit (object-fit: cover) into a fixed 4:3 box. Placeholder
   slots (no media yet) are unaffected. */
.asset-slot__frame--photo { aspect-ratio: auto; display: block; }
.asset-slot__frame--photo img,
.asset-slot__frame--photo video { height: auto; object-fit: initial; }
.asset-slot__label {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  color: rgba(244, 241, 234, 0.8); text-transform: uppercase;
}
.asset-slot__caption { margin: 10px 0 0; font-family: var(--font-mono); font-size: 12px; color: var(--muted); }

.proj-section--artifacts .proj-section__blocks { gap: 24px; }
/* Artifacts is the visual payoff — final photography gets a slightly wider stage
   than the running-text content column. */
.proj-section--artifacts .proj-section__inner { max-width: 1600px; }

/* ── Film — resting filmstrip ────────────────────────────────────────── */
.film-strip {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}
.film-strip__item {
  display: flex; flex-direction: column; align-items: stretch; gap: 10px;
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  text-align: left; font: inherit; color: inherit;
}
.film-strip__cover {
  position: relative; aspect-ratio: 4 / 5;
  border-radius: var(--radius); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.film-strip__cover img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform 0.4s ease;
}
.film-strip__item:hover .film-strip__cover img,
.film-strip__item:focus-visible .film-strip__cover img { transform: scale(1.04); }
.film-strip__placeholder {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em;
  color: rgba(244, 241, 234, 0.85); text-transform: uppercase; text-align: center; padding: 0 12px;
}
.film-strip__play {
  position: absolute; right: 10px; bottom: 10px; z-index: 2;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 999px;
  background: rgba(14, 14, 12, 0.55); color: #f4f1ea;
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
}
.film-strip__tag {
  display: flex; justify-content: space-between; gap: 8px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted);
}
.film-strip__tag em { font-style: normal; color: var(--muted); }
@media (max-width: 700px) {
  .film-strip { grid-template-columns: repeat(2, 1fr); gap: 10px; }
}

/* ── Film — lightbox ─────────────────────────────────────────────────── */
.film-lightbox {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
}
.film-lightbox__backdrop {
  position: absolute; inset: 0;
  background: rgba(8, 8, 7, 0.94);
}
.film-lightbox__panel {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 1fr) 280px;
  align-items: center; gap: 32px;
  max-width: 1100px; width: min(94vw, 1100px); max-height: 88vh;
  margin: 0 auto; padding: 0 64px;
}
.film-lightbox__stage {
  display: flex; align-items: center; justify-content: center;
  max-height: 88vh; overflow: auto;
}
.film-lightbox__stage iframe { max-width: 100%; }
/* Rendered instead of the Instagram embed when real Mintt-synced media is
   available for the active post (see useMinttFeed in project-components.jsx) —
   a real <video>/<img>, not Instagram's card chrome. */
.film-lightbox__video, .film-lightbox__stage-image {
  max-width: 100%; max-height: 86vh; display: block;
  border-radius: var(--radius); background: #000;
}
.film-lightbox__stage-image { object-fit: contain; }
.film-lightbox__info {
  display: flex; flex-direction: column; align-items: flex-start; gap: 14px;
  color: #f4f1ea;
}
.film-lightbox__count {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em;
  color: rgba(244, 241, 234, 0.6); text-transform: uppercase;
}
.film-lightbox__label {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: 22px; line-height: 1.1;
}
.film-lightbox__link {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase;
  color: #f4f1ea; padding-bottom: 4px; border-bottom: 1px solid rgba(244, 241, 234, 0.4);
}
.film-lightbox__link:hover { border-color: #f4f1ea; }
.film-lightbox__close {
  position: absolute; top: -44px; right: 0; z-index: 2;
  width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid rgba(244, 241, 234, 0.3); border-radius: 999px;
  color: #f4f1ea; font-size: 15px; cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.film-lightbox__close:hover { background: rgba(244, 241, 234, 0.12); border-color: #f4f1ea; }
.film-lightbox__arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid rgba(244, 241, 234, 0.3); border-radius: 999px;
  color: #f4f1ea; cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.film-lightbox__arrow:hover { background: rgba(244, 241, 234, 0.12); border-color: #f4f1ea; }
.film-lightbox__arrow--prev { left: 8px; }
.film-lightbox__arrow--prev svg { transform: scaleX(-1); }
.film-lightbox__arrow--next { right: 8px; }
@media (max-width: 860px) {
  .film-lightbox__panel {
    grid-template-columns: 1fr; padding: 0 16px; width: 94vw; max-height: 92vh;
    overflow-y: auto;
  }
  .film-lightbox__close { top: 8px; right: 8px; background: rgba(8, 8, 7, 0.6); }
  .film-lightbox__arrow--prev { left: -2px; }
  .film-lightbox__arrow--next { right: -2px; }
  .film-lightbox__info { padding-bottom: 16px; }
}

.proj-conversation { background: var(--ink); color: var(--bg); padding: var(--section-rhythm) var(--pad); }
.proj-conversation__inner { max-width: 760px; margin: 0 auto; }
.proj-conversation__lead {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 4vw, 56px); text-transform: uppercase;
  margin: 0 0 20px; line-height: 1.05;
}
.proj-conversation__body {
  font-family: var(--font-body); font-size: 17px; line-height: 1.7;
  color: rgba(244, 241, 234, 0.82); margin: 0 0 32px; max-width: 620px;
}
.proj-conversation__link {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em;
  text-transform: uppercase; padding-bottom: 6px; border-bottom: 1px solid currentColor;
}

/* ProjectNav is rendered once, immediately after the hero, and stays pinned there
   for the rest of the scroll (position: sticky, offset below the fixed site Nav) —
   this is the same Prev/Archive/Next component as before, just persistent. No
   toolbar chrome: no bar background, blur, or border — three quiet elements
   floating over the page rather than an app-style strip. The bar itself ignores
   pointer events so it never blocks the content it floats over; only the actual
   links/pill are clickable. */
.proj-nav-bar {
  position: sticky; top: 72px; z-index: 40;
  pointer-events: none;
}
/* Previous/Next are positioned against the viewport-width bar itself (left/right,
   not a content-column grid track), so their distance from the edge is a function
   of the viewport, never of the project-content max-width. Archive_ stays in normal
   flow, centered, which is what gives .proj-nav its height for the side items'
   top: 50% to resolve against. */
.proj-nav {
  position: relative;
  display: flex; justify-content: center; align-items: center;
  padding: 18px var(--pad);
  min-height: 24px;
}
.proj-nav__side {
  position: absolute; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; gap: 10px; max-width: min(32vw, 280px);
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--fg);
  pointer-events: auto;
  text-shadow: 0 1px 2px color-mix(in oklab, var(--bg) 60%, transparent);
}
.proj-nav__side span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.proj-nav__side--prev { left: clamp(16px, 4vw, 56px); }
.proj-nav__side--next { right: clamp(16px, 4vw, 56px); justify-content: flex-end; }
.proj-nav__side--prev svg { transform: scaleX(-1); }

/* Archive_ pill — "liquid" refinement: softer organic radius, translucent cream
   surface, light blur, a hairline border instead of a hard outline, and a gentle
   press on hover/active rather than a hard color swap. Still minimal/architectural,
   no glossy highlight or shine. */
.proj-nav__mid {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 10px 22px; border-radius: 22px; color: var(--fg);
  border: 1px solid color-mix(in oklab, var(--fg) 12%, transparent);
  background: color-mix(in oklab, var(--bg) 72%, transparent);
  backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  white-space: nowrap;
  pointer-events: auto;
  transition: background 0.25s ease, border-color 0.25s ease, transform 0.2s ease, box-shadow 0.25s ease;
}
.proj-nav__mid:hover {
  background: color-mix(in oklab, var(--bg) 90%, transparent);
  border-color: color-mix(in oklab, var(--fg) 20%, transparent);
  transform: scale(0.98);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.proj-nav__mid:active { transform: scale(0.96); }

.proj-notfound {
  min-height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 140px var(--pad) 80px; text-align: center;
  font-family: var(--font-mono); font-size: 14px; color: var(--muted);
}
.proj-notfound a { color: var(--fg); text-decoration: underline; }

@media (max-width: 700px) {
  .proj-meta { gap: 20px 32px; }
  /* The sticky nav stays a single thin row on mobile — side labels collapse to
     arrow-only so the persistent floating row never grows tall enough to eat
     the viewport. */
  .proj-nav-bar .proj-nav { padding: 12px var(--pad); gap: 8px; }
  .proj-nav-bar .proj-nav__side span { display: none; }
  .proj-nav-bar .proj-nav__mid { padding: 6px 12px; font-size: 11px; }
}

/* ── COLLECTION_ / Shopify ────────────────────────────────────────────── */
.shop-message {
  max-width: var(--maxw); margin: 0 auto; padding: 40px var(--pad) 120px;
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.04em;
  color: var(--muted);
}

/* Product grid reuses the Archive grid's layout rhythm (same max-width, gap,
   responsive column counts) for visual continuity, with its own card design. */
.prod-grid {
  max-width: var(--maxw); margin: 0 auto;
  padding: 0 var(--pad) 120px;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}
.prod-card { display: block; }
.prod-card__frame {
  position: relative; aspect-ratio: 4 / 5; border-radius: var(--radius);
  overflow: hidden; background: var(--bg-2);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 14px;
}
.prod-card__frame img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform 0.4s ease;
}
.prod-card:hover .prod-card__frame img { transform: scale(1.03); }
.prod-card__noimg {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em;
  color: var(--muted); text-transform: uppercase;
}
.prod-card__badge {
  position: absolute; top: 12px; left: 12px; z-index: 1;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--bg);
  background: rgba(14, 14, 12, 0.72); padding: 5px 10px; border-radius: 999px;
}
.prod-card__meta { display: flex; flex-direction: column; gap: 4px; }
.prod-card__title {
  font-family: var(--font-display); font-weight: 400; font-size: 18px;
  letter-spacing: -0.005em; text-transform: uppercase; color: var(--fg);
}
.prod-card__price {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.04em; color: var(--muted);
}

@media (max-width: 1024px) {
  .prod-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 700px) {
  .prod-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; }
}

/* ── Product detail page ─────────────────────────────────────────────── */
.prod-page-wrap { max-width: var(--maxw); margin: 0 auto; padding: 140px var(--pad) 0; }
/* Quiet return-to-browsing link, floating above the gallery/info grid --
   deliberately not part of that grid, so it never competes visually with
   the purchase interface below it. Same left-arrow language as
   .proj-nav__side--prev. */
.prod-back {
  display: inline-flex; align-items: center; gap: 8px; margin-bottom: 32px;
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
}
.prod-back svg { transform: scaleX(-1); }
.prod-back:hover { color: var(--fg); }
.prod-page {
  display: grid; grid-template-columns: 1.3fr 1fr; gap: 64px;
  align-items: start; padding-bottom: 120px;
}
.prod-page__gallery { display: flex; flex-direction: column; gap: 16px; }
/* No crop on the product's own detail imagery, same rule as project stills:
   the frame sizes itself to the image, never the reverse. */
.prod-page__figure { margin: 0; line-height: 0; }
.prod-page__figure img { display: block; width: 100%; height: auto; border-radius: var(--radius); }
.prod-page__figure--empty {
  aspect-ratio: 4 / 5; display: flex; align-items: center; justify-content: center;
  background: var(--bg-2); border-radius: var(--radius);
  font-family: var(--font-mono); font-size: 12px; color: var(--muted); text-transform: uppercase;
}

.prod-page__info { position: sticky; top: 120px; display: flex; flex-direction: column; gap: 20px; }
.prod-page__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  color: var(--muted); font-weight: 600;
}
.prod-page__title {
  font-family: var(--font-display); font-weight: 400; margin: 0;
  font-size: clamp(32px, 3.4vw, 48px); line-height: 1.02; letter-spacing: -0.01em;
  text-transform: uppercase;
}
.prod-page__price {
  display: flex; align-items: baseline; gap: 12px;
  font-family: var(--font-mono); font-size: 18px; color: var(--fg);
}
.prod-page__compare-at { color: var(--muted); text-decoration: line-through; font-size: 14px; }
.prod-page__soldout {
  display: inline-block; align-self: flex-start;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--fg);
  border: 1px solid var(--line); border-radius: 999px; padding: 6px 14px;
}

.prod-options { display: flex; flex-direction: column; gap: 16px; }
.prod-options__group { display: flex; flex-direction: column; gap: 8px; }
.prod-options__label {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted);
}
.prod-options__values { display: flex; flex-wrap: wrap; gap: 8px; }
.prod-options__value {
  height: 40px; padding: 0 16px; border-radius: var(--radius);
  border: 1px solid var(--line); background: transparent; color: var(--fg);
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.04em;
  cursor: pointer; transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.prod-options__value:hover { border-color: var(--fg); }
.prod-options__value.is-active { background: var(--ink); color: var(--bg); border-color: var(--ink); }
[data-theme="dark"] .prod-options__value.is-active { background: var(--bg); color: var(--ink); }
.prod-options__value.is-unavailable { color: var(--muted); text-decoration: line-through; opacity: 0.5; }

.qty-stepper {
  display: inline-flex; align-items: center; gap: 0;
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  height: 46px;
}
.qty-stepper button {
  width: 40px; height: 100%; border: none; background: transparent; color: var(--fg);
  font-size: 16px; cursor: pointer; transition: background 0.2s ease;
}
.qty-stepper button:hover:not(:disabled) { background: var(--bg-2); }
.qty-stepper button:disabled { opacity: 0.3; cursor: default; }
.qty-stepper span {
  min-width: 32px; text-align: center;
  font-family: var(--font-mono); font-size: 13px;
}

.prod-page__add { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.prod-page__added {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em;
  color: var(--fg); opacity: 0.75;
}
.prod-page__desc {
  font-family: var(--font-body); font-size: 15px; line-height: 1.7; color: var(--fg);
  padding-top: 12px; border-top: 1px solid var(--line);
}

.prod-notfound {
  min-height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 140px var(--pad) 80px; text-align: center;
  font-family: var(--font-mono); font-size: 14px; color: var(--muted);
}
.prod-notfound a { color: var(--fg); text-decoration: underline; }

@media (max-width: 900px) {
  .prod-page-wrap { padding-top: 110px; }
  .prod-page { grid-template-columns: 1fr; gap: 32px; }
  .prod-page__info { position: static; }
}

/* ── Product recommendations ─────────────────────────────────────────── */
/* Sits below the primary purchase interface, visually separated by its own
   top border/rhythm (same treatment as .proj-section) so it reads as a
   distinct "keep exploring" beat, not a continuation of the Add to Cart
   decision above it. */
.prod-recs { border-top: 1px solid var(--line); padding: var(--section-rhythm) 0; }
.prod-recs__inner { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--pad); }
.prod-recs__heading {
  font-family: var(--font-display); font-weight: 400; margin: 0 0 32px;
  font-size: clamp(24px, 2.6vw, 34px); letter-spacing: -0.005em; text-transform: uppercase;
}
.prod-recs__grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px;
}
@media (max-width: 700px) {
  /* CSS-only swipeable row -- no carousel library. Cards keep their normal
     size (no squeezing to fit), so this reads as "swipe to see more,"
     which is genuinely better for discovery on a narrow screen than three
     stacked full-width cards competing with the purchase decision above. */
  .prod-recs__grid {
    display: flex; gap: 16px; overflow-x: auto; padding-bottom: 8px;
    scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  }
  .prod-recs__grid .prod-card { flex: 0 0 72%; scroll-snap-align: start; }
}

/* ── Cart drawer ──────────────────────────────────────────────────────── */
/* Same interaction language as .menu (SideMenu): backdrop + right-hand panel,
   separate class names so the two overlays never collide if both exist. */
.cart-drawer { position: fixed; inset: 0; z-index: 110; pointer-events: none; }
.cart-drawer--open { pointer-events: auto; }
.cart-drawer__backdrop {
  position: absolute; inset: 0;
  background: rgba(14, 14, 12, 0.55);
  backdrop-filter: blur(8px);
  opacity: 0; transition: opacity 0.3s ease;
}
.cart-drawer--open .cart-drawer__backdrop { opacity: 1; }
.cart-drawer__panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(420px, 100%);
  background: var(--bg); color: var(--fg);
  display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
  box-shadow: -8px 0 32px rgba(0, 0, 0, 0.12);
}
.cart-drawer--open .cart-drawer__panel { transform: translateX(0); }
.cart-drawer__head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 24px var(--pad); border-bottom: 1px solid var(--line);
}
.cart-drawer__title {
  font-family: var(--font-display); font-weight: 400; font-size: 22px;
  text-transform: uppercase; letter-spacing: -0.005em;
}
.cart-drawer__close {
  width: 40px; height: 40px; border-radius: 999px; border: 1px solid var(--line);
  background: transparent; color: inherit; cursor: pointer;
}
.cart-drawer__close:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.cart-drawer__lines {
  flex: 1; overflow-y: auto; padding: 8px var(--pad);
  display: flex; flex-direction: column;
}
.cart-line {
  display: flex; gap: 14px; padding: 20px 0; border-bottom: 1px solid var(--line);
}
.cart-line__img { width: 76px; height: 95px; object-fit: cover; border-radius: var(--radius); flex-shrink: 0; }
.cart-line__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.cart-line__title {
  font-family: var(--font-display); font-weight: 400; font-size: 15px;
  text-transform: uppercase; letter-spacing: -0.005em;
}
.cart-line__variant {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; color: var(--muted);
}
.cart-line__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; }
.cart-line__row .qty-stepper { height: 36px; }
.cart-line__row .qty-stepper button { width: 32px; }
.cart-line__price { font-family: var(--font-mono); font-size: 12px; }
/* Only appears when a quantity increase came back lower than requested --
   Shopify's own inventory cap, applied silently with no error otherwise.
   Muted, not alarmist -- same restrained tone as .cta__error, not a red/
   destructive color the rest of the cart never uses. */
.cart-line__warning {
  margin-top: 6px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--fg); opacity: 0.75;
}
.cart-line__remove {
  align-self: flex-start; margin-top: 4px; background: none; border: none; padding: 0;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); text-decoration: underline; cursor: pointer;
}
.cart-line__remove:hover { color: var(--fg); }
.cart-drawer__foot { padding: 20px var(--pad) 28px; border-top: 1px solid var(--line); }
.cart-drawer__subtotal {
  display: flex; justify-content: space-between; margin-bottom: 16px;
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.04em;
}
.cart-drawer__checkout { width: 100%; justify-content: center; }
.cart-drawer__checkout.is-disabled { opacity: 0.5; pointer-events: none; }

/* ── Architecture page (coming soon) ─────────────────────────────────────
   No published work exists yet, so there is nothing to grid or gallery here
   -- one singular, centered composition instead: title, three lines read in
   sequence, a short rule, then the "coming soon" mark. Headline matches
   .archive-head__title's scale (the same one PRODUCTS_ and ARCHIVE_ use) so
   this page doesn't read as an oversized one-off treatment.

   .arch-shell is a sticky-footer flex column: min-height:100vh with
   .arch-page (flex:1) absorbing any leftover viewport height. Because the
   centered content inside .arch-page stacks from the top, that leftover
   space lands after it -- between COMING SOON_ and the Footer, which is
   exactly the "comfortable breathing space before the footer" this page
   wants -- and it guarantees the Footer's bottom edge always reaches the
   bottom of the viewport, so no cream shows beneath it on a tall window. */
.arch-shell { min-height: 100vh; display: flex; flex-direction: column; }
.arch-shell .arch-page { flex: 1 0 auto; }
.arch-page { padding-top: 140px; }
.arch-center {
  max-width: 640px; margin: 0 auto;
  padding: 6vh var(--pad) 10vh;
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 26px;
}
.arch-center__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(56px, 7vw, 112px); margin: 0;
  line-height: 0.9; letter-spacing: -0.01em;
  text-transform: uppercase;
  animation: fadeUp 0.7s ease both;
}
.arch-center__line {
  font-family: var(--font-mono); font-size: 14px; line-height: 1.75;
  color: var(--muted); margin: 0; max-width: 44ch;
  animation: fadeUp 0.7s ease both;
}
/* Extra margin on top of the flex gap so the headline reads as its own
   beat before the message begins, without touching the (still-uniform)
   spacing between the three message lines themselves. */
.arch-center__line--1 { margin-top: 34px; }
.arch-center__line--2 { animation-delay: 0.08s; }
.arch-center__line--3 { animation-delay: 0.16s; }
.arch-center__rule {
  width: 40px; height: 1px; background: var(--line);
  transform: scaleX(0); margin-top: 4px;
  animation: archRuleGrowX 0.8s ease 0.32s both;
}
.arch-center__soon {
  font-family: var(--font-mono); font-weight: 700; font-size: 13px;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--fg);
  animation: fadeUp 0.7s ease 0.4s both;
}

@keyframes archRuleGrowX { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@media (max-width: 700px) {
  .arch-page { padding-top: 110px; }
  .arch-center { padding: 4vh var(--pad) 8vh; gap: 20px; }
  .arch-center__line--1 { margin-top: 22px; }
}

/* ── Contact page ─────────────────────────────────────────────────────────
   Reuses the homepage's .cta accent-panel language (same bold yellow block,
   same form) rather than the quiet Archive/Products editorial treatment --
   the whole page IS the conversion action, so it gets the studio's most
   confident, high-contrast surface instead of a muted one. .contact-page
   itself stays sized to its own content -- the yellow panel should end
   exactly where its content ends, flush against the Footer, not padded out
   to fill the viewport.

   .contact-shell is the same sticky-footer flex column as .arch-shell, but
   here the leftover viewport height is given to the Footer itself (flex:1)
   instead of to .contact-page, so the yellow section stays flush against
   the Footer's top edge either way. The Footer's own content still sits at
   its normal top-of-box position; any extra height just extends the
   Footer's existing black background further down, so a tall window never
   exposes cream beneath it, and short content is unaffected. */
.contact-shell { min-height: 100vh; display: flex; flex-direction: column; }
.contact-shell .contact-page { flex: 0 0 auto; }
.contact-shell .footer { flex: 1 0 auto; }
.contact-cta { padding-top: 140px; padding-bottom: 96px; }
.contact-cta__email { margin-top: -4px; }
.contact-cta__email a { color: inherit; text-decoration: underline; }
.contact-cta__email a:hover { color: var(--ink); }

@media (max-width: 700px) {
  .contact-cta { padding-top: 110px; padding-bottom: 64px; }
}

/* ── Story page (OUR STORY_) ──────────────────────────────────────────────
   COMPOSITION PROTOTYPE: placeholder imagery only. One continuous editorial
   sequence, not six stacked sections -- density and light shift through the
   scroll (light through 01-03, dark for 04, light again for 05). Sticky
   footer wrapper matches Architecture_/Contact_'s pattern. */
.story-shell { min-height: 100vh; display: flex; flex-direction: column; }
.story-shell .story-page { flex: 1 0 auto; padding-top: 140px; }

.story-tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  font-weight: 600; color: var(--muted); margin-bottom: 22px;
}
.story-tag--dark { color: rgba(244, 241, 234, 0.5); }

.story-section {
  max-width: 900px; margin: 0 auto;
  padding: calc(var(--section-rhythm) * 1.7) var(--pad);
}

/* ── Opening ─────────────────────────────────────────────────────────── */
/* .story-open is full-width and is the positioning context for
   .story-open__next -- its horizontal center is the section's own center,
   which is the viewport's center, independent of .story-open__inner's
   narrower 1100px measure below. */
.story-open {
  position: relative;
  min-height: 76vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
}
/* padding lives here (not on .story-open) so the title's available width is
   1100px minus this padding, exactly as it was before .story-open__inner
   existed -- putting padding on the outer element instead would hand the
   title ~2x var(--pad) of extra width it didn't have before, changing where
   it wraps (an unintended composition change, not just a refactor). */
.story-open__inner {
  max-width: 1100px; margin: 0 auto; padding: 0 var(--pad);
  display: flex; flex-direction: column; align-items: center; gap: 26px;
}
.story-open__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(52px, 10vw, 168px); line-height: 0.92;
  letter-spacing: -0.01em; text-transform: uppercase; margin: 0;
  animation: fadeUp 0.9s ease both;
}
.story-open__line {
  font-family: var(--font-mono); font-size: clamp(14px, 1.6vw, 18px);
  line-height: 1.6; color: var(--muted); margin: 0;
  animation: fadeUp 0.9s ease both;
}
.story-open__line:nth-of-type(2) { animation-delay: 0.14s; }
.story-open__line:nth-of-type(3) { animation-delay: 0.26s; }

/* Glimpse of the next section, not a generic "scroll down" prompt -- reuses
   .story-tag's own styling so it reads as a preview of the real thing, not
   a mismatched label. Plays on load rather than on scroll-intersection,
   since this sits above the fold before any scrolling happens. Centered
   against .story-open (full section width, see above), not .story-open__inner.
   Split across two elements: .story-open__next carries ONLY the centering
   transform, .story-open__next-inner carries ONLY the entrance animation --
   an animated transform replaces a plain transform on the same element
   rather than combining with it, which silently breaks translateX(-50%)
   centering if both live on one element. */
/* Both cues are now <a> tags (optional scroll shortcuts, not required entry
   points) -- reset link defaults only, no new visual treatment. */
.story-open__next, .story-next-cue {
  text-decoration: none; color: inherit; cursor: pointer;
}
.story-open__next {
  position: absolute; left: 50%; bottom: clamp(28px, 6vh, 56px);
  /* Mathematically centered (translateX(-50%)) reads as sitting slightly
     right of the title's own visual axis -- the label's specific glyphs
     ("01 / PROCESS_": numerals, slashes, a trailing underscore) don't carry
     the same optical weight as solid letterforms, so an exact geometric
     center is not the same as a perceived one here. Small manual leftward
     correction on top of the true center, not a replacement for it. */
  transform: translateX(calc(-50% - 8px));
}
.story-open__next-inner {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  animation: fadeUp 0.9s ease 0.5s both;
}
.story-open__next-label { margin-bottom: 0; text-align: center; }
/* line-height: 0 on the flex wrapper removes the couple of px of inline-SVG
   baseline whitespace that would otherwise nudge the chevron's true visual
   center a hair off from the label's center above it. */
.story-open__chevron { display: flex; justify-content: center; line-height: 0; color: var(--muted); }

/* ── Shared typographic vocabulary (01 / 02 / 03 / 05) ──────────────────── */
.story-headline {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 5vw, 64px); line-height: 1.05;
  text-transform: uppercase; letter-spacing: -0.005em;
  margin: 0 0 var(--block-gap);
}
.story-headline--dark { color: var(--bg); }

.story-body {
  font-family: var(--font-body); font-size: 17px; line-height: 1.75;
  max-width: 62ch; color: var(--fg); margin: 0 0 var(--block-gap);
}
.story-body--dark { color: rgba(244, 241, 234, 0.82); }

.story-lines { display: flex; flex-direction: column; gap: 6px; margin: 0 0 var(--block-gap); }
.story-lines p {
  font-family: var(--font-mono); font-size: 14px; line-height: 1.7;
  color: var(--muted); margin: 0;
}
.story-lines--wide p { max-width: 64ch; }

.story-emphasis {
  font-family: var(--font-mono); font-weight: 700; font-size: 15px;
  letter-spacing: 0.02em; margin: 0 0 var(--block-gap); color: var(--fg);
}
.story-accent-line {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(22px, 2.6vw, 34px); margin: var(--block-gap) 0; letter-spacing: -0.005em;
}

.story-stack { list-style: none; margin: 0 0 var(--block-gap); padding: 0; display: flex; flex-direction: column; gap: 10px; }
.story-stack li {
  font-family: var(--font-mono); font-size: 15px; line-height: 1.6; color: var(--fg);
  max-width: 56ch;
}
.story-stack--dark li { color: rgba(244, 241, 234, 0.85); }

.story-lines-single { font-family: var(--font-mono); font-size: 14px; color: var(--muted); margin: 4px 0; }

/* ── 01 / Process_ — a cadence moment ────────────────────────────────────
   Section padding/rhythm intentionally left at the shared .story-section
   default (the larger padding + --block-gap tried here previously was
   reverted at the user's request; only the cadence list treatment below
   stays). Each line is its own .reveal target (see story.entry.jsx) so they step in
   one at a time rather than as one block -- the cadence is the point.
   Sized between .story-emphasis (15px mono) and .story-headline (32-64px
   display): real presence without competing with the heading above it. */
.story-cadence {
  list-style: none; margin: 0 0 calc(var(--block-gap) * 1.15); padding: 0;
  display: flex; flex-direction: column; gap: 12px;
}
.story-cadence li {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(22px, 2.8vw, 38px); line-height: 1.08; letter-spacing: -0.005em;
  color: var(--fg);
}
.story-cadence li:nth-child(2) { transition-delay: 0.08s; }
.story-cadence li:nth-child(3) { transition-delay: 0.16s; }
.story-cadence li:nth-child(4) { transition-delay: 0.24s; }
.story-cadence li:nth-child(5) { transition-delay: 0.32s; }
.story-cadence li:nth-child(6) { transition-delay: 0.4s; }

.story-close {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(28px, 3.6vw, 48px); line-height: 1.1; letter-spacing: -0.005em;
  margin: calc(var(--block-gap) * 1.4) 0 0;
}
.story-close--dark { color: var(--bg); }

/* ── Placeholder media unit (IMAGE 01_-10_) ─────────────────────────────
   Reuses the Archive's existing flat-tint palette plus a subtle hatch
   texture (same repeating-linear-gradient already used on .cta/.wwd__panel)
   so ten placeholders read as one considered system, not gray boxes. */
.story-img {
  position: relative; border-radius: var(--radius); overflow: hidden;
  display: flex; align-items: flex-end; padding: 16px; width: 100%;
  background-image: repeating-linear-gradient(135deg, rgba(0,0,0,0.05) 0 1px, transparent 1px 20px);
}
.story-img.archive-tint--1 { background-color: var(--archive-tint-1); }
.story-img.archive-tint--2 { background-color: var(--archive-tint-2); }
.story-img.archive-tint--3 { background-color: var(--archive-tint-3); }
.story-img.archive-tint--4 { background-color: var(--archive-tint-4); }
.story-img__label {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; font-weight: 600;
  color: #f4f1ea; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* ── 02 / The Inbetween_ — moments (text + image paired, staggered) ─────
   Alternating alignment/offset per row reads as an editorial sequence, not
   a card grid -- text and image are one physical unit in the DOM, which
   also gives mobile a naturally composed single-column fallback. */
.story-moments { display: flex; flex-direction: column; gap: 64px; margin: var(--section-rhythm) 0; }
.story-moment { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; align-items: center; }
.story-moment p {
  font-family: var(--font-mono); font-size: 16px; line-height: 1.6; color: var(--fg); margin: 0;
}
.story-moment .story-img { max-width: 420px; }
.story-moment--1 .story-img { max-width: 320px; }
.story-moment--2 { grid-template-columns: 0.8fr 1.2fr; }
.story-moment--2 p { order: 2; text-align: right; }
.story-moment--2 .story-img { order: 1; max-width: 100%; }
.story-moment--3 .story-img { max-width: 300px; justify-self: center; }
.story-moment--4 { grid-template-columns: 1.2fr 0.8fr; }
.story-moment--4 .story-img { max-width: 260px; justify-self: end; }
.story-moment--5 { grid-template-columns: 0.9fr 1.1fr; }
.story-moment--5 p { order: 2; text-align: right; }
.story-moment--5 .story-img { order: 1; max-width: 100%; }

/* .story-moments-text: text-only fallback for the block above when
   STORY_MEDIA_ENABLED is false (see story.entry.jsx) -- same five lines,
   no image to pair against, so shown at cadence-list scale instead of
   .story-moment's small 16px caption size. Delete this whole rule (and the
   fallback branch that uses it) if the paired layout above ever becomes
   the only version again. */
.story-moments-text {
  list-style: none; margin: var(--section-rhythm) 0; padding: 0;
  display: flex; flex-direction: column; gap: 18px;
}
.story-moments-text li {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(24px, 3.4vw, 44px); line-height: 1.15; letter-spacing: -0.005em;
  color: var(--fg);
}
.story-moments-text li:nth-child(2) { transition-delay: 0.08s; }
.story-moments-text li:nth-child(3) { transition-delay: 0.16s; }
.story-moments-text li:nth-child(4) { transition-delay: 0.24s; }
.story-moments-text li:nth-child(5) { transition-delay: 0.32s; }

/* ── 03 / Aworkinprocess_ — medium collage (words + images) ──────────────
   Flexbox-wrap rather than CSS Grid on purpose: this mixes text fragments
   and images with very different heights, and grid's implicit row-track
   auto-sizing does not guarantee non-overlapping rows once spans/heights
   vary this much -- flex-wrap lines never overlap by construction, so
   varied flex-basis widths give the same asymmetric, mosaic-like wrapping
   rhythm without the layout risk. */
.story-medium__grid {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  gap: 28px 26px; margin: var(--section-rhythm) 0;
}
.story-word {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  line-height: 0.95; letter-spacing: -0.005em; color: var(--fg);
}
.story-word--xl { font-size: clamp(36px, 5.2vw, 76px); flex: 1 1 320px; }
.story-word--lg { font-size: clamp(28px, 3.8vw, 54px); flex: 1 1 240px; }
.story-word--md { font-size: clamp(20px, 2.4vw, 34px); flex: 1 1 190px; color: var(--muted); }
.story-word--sm {
  font-family: var(--font-mono); font-size: clamp(13px, 1.4vw, 16px);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  flex: 0 1 130px; font-weight: 600;
}
.story-img--w1 { flex: 0 1 230px; }
.story-img--w2 { flex: 0 1 170px; }
.story-img--w3 { flex: 1 1 360px; }
.story-img--w4 { flex: 0 1 230px; }
.story-img--w5 { flex: 1 1 460px; }
.story-medium__grid .story-img { max-width: none; }

/* Quiet continuation cue into 04, reusing the opening cue's chevron. Sits
   inside .story-medium's own existing bottom padding via absolute
   positioning -- adds no height, no extra margin/gap, no section resize. */
.story-medium { position: relative; }
.story-next-cue {
  position: absolute; left: 50%; bottom: 28px; transform: translateX(-50%);
}
.story-next-cue__inner {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.story-next-cue__label { margin-bottom: 0; text-align: center; }

/* ── 04 / State Of Mind_ (dark, full-bleed) ──────────────────────────────
   .story-dark carries the full-bleed background; .story-dark__inner is the
   normal measured .story-section content sitting on top of it. */
.story-dark { background: var(--ink); }
.story-dark__inner { max-width: 900px; margin: 0 auto; padding: calc(var(--section-rhythm) * 1.9) var(--pad); }

/* All three groups share one left edge -- a right-floated group and later an
   indented one both previously read as leaving the sequence rather than
   being part of it. */
.story-visible { display: flex; flex-direction: column; align-items: flex-start; text-align: left; gap: 4px; margin: 30px 0; }
.story-visible__yes {
  display: block; font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(20px, 2.8vw, 34px); color: var(--bg); line-height: 1.1;
}
.story-visible__no {
  display: block; font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.05em;
  color: rgba(244, 241, 234, 0.5); text-transform: uppercase; margin-top: 4px;
}

/* ── Ending ──────────────────────────────────────────────────────────── */
.story-ending {
  max-width: 900px; margin: 0 auto;
  padding: calc(var(--section-rhythm) * 2) var(--pad) calc(var(--section-rhythm) * 2.2);
  text-align: center; display: flex; flex-direction: column; align-items: center; gap: 48px;
}
.story-ending__lead {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(36px, 5.6vw, 84px); line-height: 1; margin: 0; letter-spacing: -0.01em;
}
.story-ending__links { display: flex; flex-direction: column; gap: 20px; align-items: center; }
.story-ending__link {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-mono); font-size: 14px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--fg); padding-bottom: 6px; border-bottom: 1px solid var(--line);
  transition: border-color 0.2s ease, color 0.2s ease;
}
.story-ending__link:hover { border-color: var(--fg); color: var(--fg); }

@media (max-width: 900px) {
  .story-img--w1, .story-img--w2, .story-img--w4 { flex-basis: 45%; }
  .story-img--w3, .story-img--w5 { flex-basis: 100%; }
}

@media (max-width: 700px) {
  .story-shell .story-page { padding-top: 110px; }
  .story-section { padding: calc(var(--section-rhythm) * 1.1) var(--pad); }
  .story-open { min-height: 62vh; }
  .story-open__inner { gap: 20px; }
  .story-dark__inner { padding: calc(var(--section-rhythm) * 1.1) var(--pad); }

  /* Mobile is composed deliberately, not the desktop grid squeezed down:
     each moment becomes a single column with the image following its own
     line directly, and the medium collage becomes one steady column in
     document order rather than a dense multi-span mosaic. */
  .story-moment, .story-moment--2, .story-moment--4, .story-moment--5 {
    grid-template-columns: 1fr; gap: 16px; text-align: left;
  }
  .story-moment p, .story-moment--2 p, .story-moment--5 p { order: 0; text-align: left; }
  .story-moment .story-img, .story-moment--1 .story-img, .story-moment--2 .story-img,
  .story-moment--3 .story-img, .story-moment--4 .story-img, .story-moment--5 .story-img {
    order: 1; max-width: 100%; justify-self: stretch;
  }
  .story-moments { gap: 44px; }

  .story-medium__grid { gap: 20px; }
  .story-word--xl, .story-word--lg, .story-word--md, .story-word--sm,
  .story-img--w1, .story-img--w2, .story-img--w3, .story-img--w4, .story-img--w5 {
    flex-basis: 100%;
  }

  .story-ending__links { width: 100%; }
}

/* ── Legal pages (Privacy/Terms/Shipping/Returns/Cookies) ────────────────
   Quiet, readable, functional -- not a design showcase. Reuses existing
   type tokens only (--font-body/--font-mono/--font-display, --pad, --maxw,
   --line, --muted). */
.legal-page { padding: 140px var(--pad) var(--section-rhythm); }
.legal-page__inner { max-width: 720px; margin: 0 auto; }
.legal-page__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 16px;
}
.legal-page__inner h1 {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: clamp(32px, 5vw, 56px); line-height: 1; letter-spacing: -0.01em;
  margin: 0 0 12px;
}
.legal-page__updated {
  font-family: var(--font-mono); font-size: 12px; color: var(--muted);
  margin: 0 0 48px;
}
.legal-section { margin-bottom: 36px; }
.legal-section h2 {
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em;
  text-transform: uppercase; font-weight: 600; color: var(--fg);
  margin: 0 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.legal-section p, .legal-section li {
  font-family: var(--font-body); font-size: 16px; line-height: 1.7;
  color: var(--fg); margin: 0 0 14px;
}
.legal-section ul { margin: 0 0 14px; padding-left: 22px; }
.legal-section li { margin-bottom: 6px; }
.legal-section a { text-decoration: underline; text-underline-offset: 2px; }
.legal-section a:hover { color: var(--accent); }
@media (max-width: 700px) {
  .legal-page { padding-top: 110px; }
}

/* ── Consent banner ──────────────────────────────────────────────────────
   Two modes from one component (consent.jsx): a compact corner card (first
   visit) and a centered preferences panel (Manage Preferences_, from the
   bar, the footer, or the Cookie Policy page). Equal visual weight for
   Accept All / Reject Non-Essential / Manage Preferences -- no dark
   patterns. */
.consent-banner { position: fixed; z-index: 500; }
/* Initial banner: deliberately a small card, not a full-width bar -- a
   full-width strip across the whole viewport reads as exactly the "large
   SaaS cookie banner" this was asked to move away from. Bottom-center on
   mobile (feels deliberately anchored rather than floating in a corner on a
   narrow screen), bottom-right on desktop. bottom uses max() against
   env(safe-area-inset-bottom) rather than stacking the two, so notched
   iPhones get their real safe-area inset as the margin instead of that
   inset plus an extra 16px on top of it; browsers without the env()
   function simply see the 16px fallback. max-width keeps 16px of side
   breathing room on any viewport without a fragile fixed width. */
.consent-banner--bar {
  left: 50%; right: auto; transform: translateX(-50%);
  bottom: max(16px, env(safe-area-inset-bottom, 0px));
  max-width: min(320px, calc(100vw - 32px));
}
@media (min-width: 701px) {
  .consent-banner--bar { left: auto; right: 16px; transform: none; }
}
.consent-banner__bar {
  margin: 0; background: var(--ink); color: var(--bg);
  padding: 14px 16px; border-radius: var(--radius);
  display: flex; flex-direction: column; gap: 10px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.24);
  position: relative;
}
.consent-banner__bar .consent-banner__copy { font-size: 12px; line-height: 1.5; padding-right: 18px; }
.consent-banner--bar .consent-banner__actions { flex-direction: column; gap: 6px; }
.consent-banner--bar .btn { height: 32px; padding: 0 12px; font-size: 10px; gap: 6px; width: 100%; justify-content: center; }
.consent-banner:not(.consent-banner--bar) {
  inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(14,14,12,0.6); padding: 24px;
}
.consent-banner__panel {
  background: var(--bg); color: var(--fg); max-width: 460px; width: 100%;
  padding: 32px; border-radius: var(--radius);
  max-height: 85vh; overflow-y: auto;
  position: relative;
}
.consent-banner__panel .consent-banner__title { padding-right: 24px; }
/* Shared small "x" close control for both surfaces -- native text glyph,
   no icon dependency. color:inherit so it automatically matches each
   surface's own text color (light-on-dark bar, dark-on-light panel). A
   32x32 hit area keeps the tap target comfortable while the glyph itself
   stays visually small and quiet. */
.consent-banner__close {
  position: absolute; top: 8px; right: 8px;
  width: 32px; height: 32px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: var(--radius);
  cursor: pointer; color: inherit; opacity: 0.6;
  font-family: var(--font-mono); font-size: 18px; line-height: 1;
  transition: opacity 0.2s, background 0.2s;
}
.consent-banner__close:hover { opacity: 1; background: rgba(128,128,128,0.12); }
.consent-banner__close:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
.consent-banner__title {
  font-family: var(--font-display); font-weight: 400; text-transform: uppercase;
  font-size: 24px; margin: 0 0 12px;
}
.consent-banner__copy {
  font-family: var(--font-body); font-size: 14px; line-height: 1.6;
  margin: 0; max-width: 56ch;
}
.consent-banner__panel .consent-banner__copy { margin-bottom: 20px; }
.consent-banner__row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 14px 0; border-top: 1px solid var(--line);
}
.consent-banner__row strong { font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.04em; }
.consent-banner__row p { font-family: var(--font-body); font-size: 13px; color: var(--muted); margin: 4px 0 0; }
.consent-banner__row input { flex-shrink: 0; margin-top: 4px; width: 18px; height: 18px; }
.consent-banner__actions { display: flex; flex-wrap: wrap; gap: 10px; }
.consent-banner__panel .consent-banner__actions { margin-top: 20px; }
@media (max-width: 700px) {
  /* Applies to the Manage Preferences panel's action row -- the initial
     bar's actions are already always-stacked/full-width (see
     .consent-banner--bar rules above) independent of viewport width. */
  .consent-banner__actions { justify-content: stretch; }
  .consent-banner__actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* ── 404 ──────────────────────────────────────────────────────────────────
   A genuine editorial surface, not a generic error utility -- shares the
   homepage Hero's dark/beams/grain treatment (its exact class names are
   reused directly in notfound.entry.jsx) so this reads as an intentional
   extension of the site rather than a bolted-on utility page. The hollow
   "0" is the whole concept in one glyph: a literal gap in the sequence,
   standing in for the missing page -- copy stays to one dry line rather
   than explaining the idea. */
.notfound {
  position: relative; min-height: 100vh; overflow: hidden;
  display: flex; flex-direction: column; justify-content: center;
  padding: 120px var(--pad) 80px;
  background: var(--ink); color: var(--bg);
}
.notfound__bg { position: absolute; inset: -10% 0; z-index: 0; background: var(--ink); }
.notfound__inner { position: relative; z-index: 1; max-width: var(--maxw); margin: 0 auto; width: 100%; }
.notfound__tag {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em;
  color: rgba(244, 241, 234, 0.55); margin: 0 0 20px;
}
.notfound__eyebrow {
  display: inline-flex; align-items: center; gap: 12px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  text-transform: uppercase; color: rgba(244, 241, 234, 0.7);
  margin-bottom: 24px;
}
.notfound__digits {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(96px, 22vw, 320px);
  line-height: 0.86; letter-spacing: -0.01em;
  margin: 0 0 32px; display: flex; gap: 0.02em;
}
/* Base case (no text-stroke support): the "0" just renders as a normal
   solid digit, identical to the two "4"s -- a safe, fully legible "404"
   with no broken/invisible glyph, just without the hollow flourish. */
.notfound__hollow { color: var(--bg); }
@supports (-webkit-text-stroke: 1px black) or (text-stroke: 1px black) {
  .notfound__hollow {
    color: transparent;
    -webkit-text-stroke: 3px var(--bg);
    text-stroke: 3px var(--bg);
    animation: notfoundBreathe 3.2s ease-in-out infinite;
  }
}
@keyframes notfoundBreathe {
  0%, 100% { -webkit-text-stroke-width: 3px; text-stroke-width: 3px; opacity: 1; }
  50% { -webkit-text-stroke-width: 1.5px; text-stroke-width: 1.5px; opacity: 0.7; }
}
.notfound__line {
  font-family: var(--font-mono); font-size: clamp(14px, 1.8vw, 18px);
  letter-spacing: 0.02em; color: rgba(244, 241, 234, 0.85);
  margin: 0 0 48px; max-width: 40ch;
}
.notfound__paths { display: flex; flex-wrap: wrap; gap: 20px 40px; }
.notfound__path {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.1em;
  color: var(--bg); padding-bottom: 4px; border-bottom: 1px solid rgba(244,241,234,0.25);
  transition: border-color 0.2s, gap 0.2s, color 0.2s;
}
.notfound__path:hover, .notfound__path:focus-visible { border-color: var(--accent); color: var(--accent); gap: 14px; }
.notfound__path:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.notfound__path svg { transition: transform 0.2s; }
.notfound__path:hover svg, .notfound__path:focus-visible svg { transform: translateX(2px); }

@media (max-width: 700px) {
  /* Not a linear shrink of the desktop composition: the digits get their
     own, steeper clamp() curve (rather than just scaling the same one down)
     so they stay dominant at phone width instead of shrinking proportionally
     with everything else, and the two return paths stack full-width (same
     reflow pattern used for the compact consent card's own actions) instead
     of staying inline. */
  .notfound { padding: 100px var(--pad) 56px; }
  .notfound__digits { font-size: clamp(72px, 32vw, 140px); margin-bottom: 24px; }
  .notfound__line { margin-bottom: 36px; }
  .notfound__paths { flex-direction: column; gap: 16px; }
}

/* ── Site gate (temporary launch curtain) ────────────────────────────────
   Purely visual -- see public/site-gate.js for the SITE_GATE_ENABLED switch
   and all behavior. Built from the same ground as .hero/.notfound (dark
   --ink background, .hero__beams/.hero__grain texture, Anton display type,
   JetBrains Mono labels, the one --accent yellow) so it reads as an
   intentional extension of the site rather than a bolted-on login screen. */
.site-gate {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 32px var(--pad);
  background: var(--ink); color: var(--bg);
  overflow: hidden;
}
.site-gate__bg { position: absolute; inset: -10% 0; z-index: 0; background: var(--ink); }
.site-gate__inner {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 20px; max-width: 480px; width: 100%;
}
.site-gate__mark {
  /* Matches the Logo component's own treatment (mono, weight 700, -0.01em
     tracking -- see components.jsx) at this site's largest established
     wordmark size (Footer's brand line uses size=26; Nav's is 15) rather
     than a small eyebrow-style label, since this line carries the gate's
     opening weight the way the footer's does for the page it sits in. */
  font-family: var(--font-mono); font-size: clamp(15px, 3vw, 26px); font-weight: 700;
  letter-spacing: -0.01em; color: var(--bg);
}
.site-gate__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(40px, 8vw, 72px); line-height: 0.9;
  letter-spacing: -0.01em; text-transform: uppercase; margin: 0;
}
.site-gate__form { display: flex; gap: 10px; width: 100%; margin-top: 8px; }
@media (min-width: 481px) {
  /* .site-gate__inner's 480px column comfortably fits NOT QUITE YET_
     (~384px rendered), but the form filling that full width read as
     stretched under it -- capping it well under the headline's own width
     (with .site-gate__inner's align-items: center still doing the
     centering) reads as a compact, intentional control instead. 360px
     keeps each half a comfortable 175px, well above the ~172px [ ENTER
     PASSWORD ] needs to render without clipping. Mobile (<=480px) is
     untouched: its stacked form already renders narrower than 360px on
     real phone widths, so this never applies there. */
  .site-gate__form { max-width: 360px; }
}
/* Both sides get the identical explicit flex-basis (half the row, minus
   half the gap) rather than flex: 1 1 0% -- Chromium gives a nowrap-text
   <button> more than its fair share under the plain flex-grow algorithm
   even with min-width: 0, so an equal basis is what actually makes their
   visible widths match instead of just asking the browser to split evenly. */
.site-gate__input {
  flex: 0 0 calc(50% - 5px); min-width: 0; height: 46px;
  font: 13px/1.5 var(--font-mono); padding: 0 16px; text-align: center;
  border: 1px solid rgba(244, 241, 234, 0.25); border-radius: var(--radius);
  background: rgba(244, 241, 234, 0.08); color: var(--bg); outline: none;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.site-gate__input::placeholder { color: rgba(244, 241, 234, 0.45); }
.site-gate__input:focus { border-color: var(--accent); background: rgba(244, 241, 234, 0.14); }
.site-gate__submit { flex: 0 0 calc(50% - 5px); min-width: 0; justify-content: center; }
.site-gate__error {
  min-height: 14px; margin: 0;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em;
  color: var(--accent);
}
.site-gate__hint {
  margin: 0; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.16em;
  color: rgba(244, 241, 234, 0.5);
}
@media (max-width: 480px) {
  .site-gate__form { flex-direction: column; }
  /* flex-basis follows the main axis, which is now vertical -- the 50%
     split above was sizing HEIGHT here, not width. Reset to auto so each
     takes its natural height again; width still matches via align-items:
     stretch (default), same as before this change. */
  .site-gate__input, .site-gate__submit { flex: 0 0 auto; }
}
