/* ============================================================
   synqa.ca - site styles
   Foundations (palette, type) imported from the design system.
   This file holds shell + chapter styles for the long-scroll homepage.
   ============================================================ */

@import url("./foundations.css?v=v10.3");

:root {
  --content-w: 1240px;  /* single source of truth for the content-card width; dial here to resize every card at once */
  --reveal-dur: 340ms;  /* v10: ONE standardized, fast scroll-in time for sub-page cards/heroes/rows (subpages.js reads this; work.html .case-entry uses it) so quick scrollers never miss a card */
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

html {
  /* Honest motion: no smooth-scroll layer. Native scroll only (no Lenis
     in either the prototype or the production target) so the dock + erase
     scrub feel rigid against the wheel. */
  scroll-behavior: auto;
  background: var(--bone);
  /* Baseline-first: WebKit <16 drops `clip` whole, so `hidden` holds the
     no-sideways-pan law there (on BOTH html and body, the v8.4 lesson);
     capable engines take the `clip` override on the next line, which keeps
     sticky pins intact. Degraded-engine cost: sticky may demote toward its
     composed end state, which the hardening pass accepts over a panning page. */
  overflow-x: hidden;
  overflow-x: clip;
}

body {
  background: var(--bone);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  overflow-x: hidden;
  overflow-x: clip;
}

/* S2 (webview pass 4): the root canvas follows the active chapter surface,
   written by site.js updateChapterStamp() as html[data-surface]. In the
   frame-resize webview world (the Instagram class) a mid-gesture resize can
   expose unpainted strips that paint the ROOT background; over an Ink chapter
   a Bone root reads as a light flashing bar (field-recorded on an iPhone 16
   Pro; machine-red in frame-flip-check --bands). Painting the root Ink while
   an Ink chapter is active makes those strips invisible. Visible pixels never
   change: chapters are opaque and cover the viewport, and the top and bottom
   overscroll zones belong to Bone chapters, so the root stays Bone there.
   Only the homepage driver ever writes this attribute; every page body ships
   data-surface="bone" and subpages never flip it. */
html[data-surface="ink"],
html[data-surface="ink"] body {
  background: var(--ink);
}

/* ----------------------------------------------------------------
   Persistent shell - present on every chapter
   ----------------------------------------------------------------*/

/* M-shell · sticky nav. Bone-92α + 12px backdrop blur, the one
   permitted use of blur in the system. */
.shell-nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 40;
  display: flex; align-items: center; justify-content: space-between;
  /* Horizontal padding scales with viewport so the 7-link nav + CTA
     still breathe at the smallest desktop width (1024px) before the
     hamburger takes over at 1023px. */
  padding: 18px clamp(20px, 3.2vw, 40px);
  gap: clamp(16px, 2vw, 32px);
  background: rgba(240, 234, 216, 0); /* fades in as user scrolls past dock */
  -webkit-backdrop-filter: blur(0px);
          backdrop-filter: blur(0px);
  border-bottom: 1px solid transparent;
  transition: background var(--dur-base) var(--ease-standard),
              backdrop-filter var(--dur-base) var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard);
}
.shell-nav[data-docked="true"] {
  background: rgba(240, 234, 216, 0.92);
  -webkit-backdrop-filter: blur(12px);
          backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.shell-nav-brand {
  /* The wordmark "slot" in the nav. Empty placeholder; the live
     animating wordmark docks here. Sized so the dock target is
     unambiguous. */
  display: inline-flex; align-items: center;
  width: 92px;   /* dock target width - see site.js M01 */
  height: 26px;
  position: relative;
  border-bottom: none; /* override foundations.css default link rule */
}
.shell-nav-brand img {
  height: 26px; width: auto; display: block;
  opacity: 0; /* the moving wordmark stands in until dock completes */
  transition: opacity var(--dur-base) var(--ease-standard);
}
.shell-nav[data-docked="true"] .shell-nav-brand img { opacity: 1; }

.shell-nav-links {
  /* Responsive gap so 7 links + CTA still fit at 1024px (the smallest
     desktop width before the 1023px hamburger breakpoint).
     - gap shrinks from 32px down to 16px between 1280px and 1024px
     - font-size shrinks from 14px to 13px in the same range
     - letter-spacing tightens by 0.005em for a touch more headroom
     At 1024px: 7 links (~268px text) + 6 x 16px gaps (96px) = ~364px,
     well inside the ~545px available between brand + CTA. */
  display: flex;
  gap: clamp(16px, 1.6vw, 32px);
  font-family: var(--font-display);
  font-size: clamp(13px, 0.95vw, 14px);
  font-weight: 500;
  letter-spacing: -0.005em;
  white-space: nowrap;
}
.shell-nav-links a {
  color: var(--ink);
  border-bottom: none;
  position: relative;
  padding-bottom: 2px;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-standard);
}
.shell-nav-links a:hover { color: var(--clay); }
.shell-nav-links .period { color: var(--clay); }

.shell-nav-cta {
  display: inline-flex; align-items: center; gap: 1px;
  background: var(--clay); color: var(--bone);
  font-family: var(--font-display); font-weight: 500;
  /* CTA scales in step with the nav links so the chip stays
     proportional at the smallest desktop width. */
  font-size: clamp(13px, 0.95vw, 14px);
  padding: 10px clamp(12px, 1.2vw, 16px);
  border-radius: var(--radius-md);
  border: none; cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  border-bottom: none; /* override foundations.css default link rule */
  transition: background 40ms var(--ease-standard),
              transform var(--dur-base) var(--ease-standard);
  will-change: transform;
}
.shell-nav-cta:hover { background: var(--clay-hover); color: var(--bone); }
.shell-nav-cta:active { background: var(--clay-active); }
.shell-nav-cta .period { color: var(--bone); }

/* M-shell · scroll-progress hairline.  1px Bone-400 base, Clay fill
   tied 1:1 to document scroll. */
.shell-progress {
  position: fixed; top: 0; left: 0; right: 0;
  height: 1px; background: var(--bone-400);
  z-index: 50;
}
.shell-progress > i {
  display: block; height: 100%; width: 0%;
  background: var(--clay);
  transform-origin: left center;
}

/* Place stamps - upper-right Toronto (Clay), lower-left Cape Town (Harbor).
   Invert via [data-shell-surface="ink"] when over an Ink chapter. */
.shell-stamp {
  position: fixed; z-index: 35;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  pointer-events: none;
  transition: color var(--dur-base) var(--ease-standard);
}
/* Both place stamps clear the 60px nav band - top stamp sits below it. */
.shell-stamp-tr { top: 84px; right: 40px; color: var(--clay); }
.shell-stamp-bl { bottom: 24px; left: 40px;  color: var(--harbor); }
.shell-stamp-bl[data-surface="ink"] { color: var(--fg-cool-on-ink); }

/* M11 · mono ticker - right-edge vertical column.
   Stays present on every chapter except 01 + 09. The column is
   vertically centered on the viewport and the visible band fades
   out at the top + bottom edges via mask-image, so the marquee
   appears to surface from and dissolve into the page.

   Roll-in entrance: on activation the column slides in from the
   right edge (translateX 40 to 0) while fading up. The centering
   translateY(-50%) is preserved by composing both translates on
   the same transform line. */
.shell-ticker {
  position: fixed;
  top: 50%;
  right: 16px;
  width: 32px;
  height: 60vh;
  /* Two transforms combined: vertical centering + horizontal
     roll-in offset driven by the local --ticker-x variable. */
  --ticker-x: 40px;
  transform: translateY(-50%) translateX(var(--ticker-x));
  z-index: 30;
  overflow: hidden;
  pointer-events: none;
  font-family: var(--font-mono); font-size: 10px;
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
  opacity: 0;
  transition: opacity var(--dur-cinematic) var(--ease-out),
              transform var(--dur-cinematic) var(--ease-out);
  /* Fade-out at the top + bottom edges of the column. The marquee
     engine keeps scrolling underneath; the mask only controls what
     the eye sees. Black band runs ~70% of the height; the outer
     15% on each side dissolves into the canvas. */
  -webkit-mask-image: linear-gradient(to bottom,
                        transparent 0%,
                        black 15%,
                        black 85%,
                        transparent 100%);
          mask-image: linear-gradient(to bottom,
                        transparent 0%,
                        black 15%,
                        black 85%,
                        transparent 100%);
}
.shell-ticker[data-active="true"] {
  opacity: 1;
  --ticker-x: 0px;
}

/* The track stacks rows vertically (normal flex column in horizontal
   writing mode) and only carries the marquee's translateY transform.
   Each row span is the one that gets vertical-rl writing mode, so
   the TEXT inside reads top-to-bottom while the rows themselves
   stack down the column the way the marquee engine expects.

   Track must NOT be absolutely positioned to inset:0 - that would
   force its height equal to the container's, which breaks the
   marquee wrap math (it relies on scrollHeight/2 reflecting the
   real total content height). */
.shell-ticker-track {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  transform: translateY(0);
  will-change: transform;
}
/* Direct-child row spans render vertical-rl so their text reads
   top-to-bottom down the column. Nested spans (.period, time
   wrapper) inherit the writing mode and must NOT redeclare it. */
.shell-ticker-track > span {
  display: inline-block;
  white-space: nowrap;
  writing-mode: vertical-rl;
}
.shell-ticker-track .period { color: var(--clay); }
/* M11 time node: no transition - minute updates are silent in-place swaps.
   A single-digit change is imperceptible; an opacity fade caused a
   visible once-per-minute flicker on the visible rows. */

/* M13 · cursor period trail container */
.shell-trail {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 25;
}
.shell-trail-dot {
  position: absolute;
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--clay);
  opacity: 0.4;
  transform: translate(-50%, -50%);
  transition: opacity 600ms linear;
  will-change: opacity, transform;
}

/* M20 · corner stamp (chapter label, upper-left) - clears the nav band. */
.shell-chapter-stamp {
  position: fixed;
  top: 84px; left: 40px;
  z-index: 35;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stamp-fg);
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-standard);
}
.shell-chapter-stamp[data-surface="ink"] { color: var(--fg-quiet-on-ink); }

/* ----------------------------------------------------------------
   M01 · The dock - Chapter 1's animating wordmark
   ----------------------------------------------------------------*/

.dock-wordmark {
  /* Lives fixed in viewport; transform is driven by scroll progress
     (M01). At scroll=0 it sits centered. At dock-complete it lands
     at the nav slot's left edge. */
  position: fixed;
  z-index: 38;
  top: 50%;
  left: 50%;
  width: 280px;
  height: auto;
  transform: translate(-50%, -50%) scale(1);
  transform-origin: left center;
  will-change: transform, opacity;
  pointer-events: none;
  /* Container-query context so the M29 roll can size + position itself as a
     fraction of THIS box (cqw). The box width is responsive (280/200/168px
     across breakpoints), so a fixed-px roll only matched the SVG at 280px;
     anchoring to cqw keeps the roll glued to the wordmark at every width. */
  container-type: inline-size;
}
.dock-wordmark img { width: 100%; height: auto; display: block; }

/* ----------------------------------------------------------------
   M29 · The glyph roll
   Text overlay that slot-machines in on load, then swaps back to
   the SVG img once the roll completes. Positioned to exactly overlay
   the img: absolute, same 280px width, same left-center anchor.
   Default (no-JS): hidden. Under html.js the roll shows and img
   is hidden until the animation resolves.
   ----------------------------------------------------------------*/

.dock-roll {
  display: none; /* hidden by default, no-JS fallback: img shows instead */
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  /* Height matches img: auto means we let the text set it. We use
     line-height 1 so vertical extent tracks the cap height only. */
  white-space: nowrap;
  font-family: var(--font-body);
  font-weight: 700;
  /* Size + place the text to overlay the SVG wordmark EXACTLY, derived from
     the SVG geometry (viewBox 8856.2 x 3809.7, glyph group at x=1024,
     baseline at y=2514, font UPM 2048). All in cqw (1cqw = 1% of the
     .dock-wordmark width) so it holds at every responsive width:
       glyph-origin X = 1024 / 8856.2 = 11.56% of width
       baseline Y = 2514 / 8856.2 = 28.39% of width from the box top
     Font-size = the SVG's true em: 1 em = 2048 font units = 2048/8856.2 of the
     box = 23.12cqw. Text and SVG are the SAME font (Inter Bold), so matching the
     em matches the cap height exactly (the glyphs were reading slightly smaller
     than the SVG below the true em). Per-glyph horizontal registration is handled
     by the fixed cqw cells below (not by font-size) -- the SVG was authored with
     marginally tighter tracking than the live font, so the cells hold the grid
     while font-size is free to match height.
     The text's own baseline sits ~0.865em (= 20.00cqw at 23.12cqw font) below
     the box top with line-height 1, so the translate nudges it down by the
     remainder (28.39 - 20.00 = 8.39cqw) and right to the origin (11.56cqw). */
  font-size: 23.12cqw;
  line-height: 1;
  color: var(--ink);
  pointer-events: none;
  /* Align baseline to match img; flex baseline aligns the glyphs */
  align-items: baseline;
  gap: 0;
  letter-spacing: 0;
  transform: translate(11.56cqw, 8.39cqw);
}

/* The roll is shown ONLY where container-query units exist (WebKit 16+,
   Chromium 105+): every cqw size/advance above is invalid below that floor,
   which would render a giant misplaced Inter overlay on every page. Old
   engines never enter this block, so .dock-roll stays display:none and the
   SVG wordmark shows directly (M29's composed end state). site.js gates the
   roll with the same CSS.supports probe so it never hides the img inline. */
@supports (container-type: inline-size) {
  html.js .dock-roll {
    display: inline-flex; /* shown only when JS is active */
  }

  html.js .dock-roll ~ img {
    /* Hide the SVG img while the roll is running; site.js restores it */
    visibility: hidden;
  }
}

/* Per-glyph cells: each occupies a FIXED slot whose width is the SVG's own
   advance for that letter (advance / 8856.2 of the box), so every glyph and the
   period land exactly on the SVG's advance grid. Relying on the live font's
   natural advances left a sub-glyph drift (the totals matched but individual
   letters did not), so the period sat a few px right of the SVG period and the
   roll->SVG crossfade flickered. Pinning the cells removes that drift. */
.dock-roll .gl {
  display: inline-block;
  flex: 0 0 auto;       /* never let flex shrink the fixed cells */
  text-align: left;     /* the locked glyph aligns to the cell (SVG origin) */
  overflow: visible;    /* a wide spinning char may bleed; that is fine */
}
.dock-roll .gl:nth-child(1) { width: 14.679cqw; } /* S advance 1300 */
.dock-roll .gl:nth-child(2) { width: 13.460cqw; } /* y advance 1192.1 */
.dock-roll .gl:nth-child(3) { width: 13.934cqw; } /* n advance 1234 */
.dock-roll .gl:nth-child(4) { width: 14.115cqw; } /* q advance 1250.1 */
.dock-roll .gl:nth-child(5) { width: 12.962cqw; } /* a advance 1148 */

/* Clay period, matches the one-Clay-moment-per-composition rule.
   Starts invisible; site.js reveals it after the last letter locks. */
.dock-roll .period {
  color: var(--clay);
  opacity: 0;
}

/* ----------------------------------------------------------------
   M05 · Painted erase canvas
   Fixed full-viewport, only visible during the bridge scroll zone.
   Draws a giant "Synqa." typographic mark and erases it via
   destination-out brush stroke as scroll progresses.
   ----------------------------------------------------------------*/

.erase-canvas {
  position: fixed;
  /* THE OVERDRAW EXTENSION (pass 7) - REVERSES the pass-6 "vacuous" call. This
     fixed full-viewport cover IS the Ch2/M05 bone scene cover, and the pass-7
     field bug proved it can go SHORT: while the erase canvas is visible the
     adoption postponement stranded the px tokens in the short world, so a frame
     GROW left the canvas ~124px short at 874 rest and the next section's content
     (not root Bone) showed through the bottom edge. The bleed is applied in JS,
     not here: sizeCanvas() sets the backing height to cvisH + CANVAS_BLEED(150)px
     (bottom-only - a fixed top:0 cover only out-runs at the bottom), pure Bone
     that the brush never sweeps, so residual staleness exposes only the cover's
     own surface. The pass-6 cost concern (a taller backing store regressing the
     bridge during-resize input latency) was RE-MEASURED on the pass-7 tree:
     frame-flip-check --responsiveness = 0 input failures with the taller store
     (the heavy sizeCanvas realloc still runs at idle, not on the input path).
     `inset:0` below only positions the element; the JS height carries the bleed. */
  inset: 0;
  z-index: 28;
  pointer-events: none;
  opacity: 0; /* gated to the bridge range by site.js */
  background: transparent;
  will-change: opacity;
}

/* ----------------------------------------------------------------
   Chapters - shared
   ----------------------------------------------------------------*/

main { display: block; }

.chapter {
  position: relative;
  width: 100%;
  background: var(--bone);
  color: var(--ink);
}
.chapter[data-surface="ink"] {
  background: var(--ink);
  color: var(--bone);
}

.chapter-bridge {
  /* The scroll runway used by M05. Has no visible content of its
     own; the canvas paints over the viewport during this range. */
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bone);
}

/* ----------------------------------------------------------------
   Chapter 01 - Cold open
   ----------------------------------------------------------------*/

.ch-01 {
  min-height: 110vh;   /* dock runs over the first ~80vh of scroll */
  min-height: 110svh;
  position: relative;
}
.ch-01-frame {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ch-01-mono-top {
  position: absolute;
  top: 22vh;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp);
  text-transform: uppercase;
  color: var(--stone);
  white-space: nowrap;
}
.ch-01-stamp {
  position: absolute;
  bottom: 22vh;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp);
  text-transform: uppercase;
  color: var(--stone);
  display: inline-flex; align-items: center; gap: 14px;
  white-space: nowrap;
}
.ch-01-stamp .t { color: var(--clay); }
.ch-01-stamp .c { color: var(--harbor); }
.ch-01-stamp .dot { color: var(--stone); }

/* ----------------------------------------------------------------
   Chapter 02 - The line
   ----------------------------------------------------------------*/

.ch-02 {
  min-height: 100vh;
  padding: 22vh 0 14vh;
  position: relative;
}
.ch-02-inner {
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 80px;
  position: relative;
}

.line-head {
  /* The thesis statement. Fraunces display, sentence case,
     period in Clay, "hours back" in Harbor italic. */
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(56px, 8.5vw, 132px);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin: 0;
  color: var(--ink);
  text-wrap: balance;
  position: relative;
}
.line-head em {
  font-style: italic;
  font-weight: 400;
  color: var(--harbor);
}
.line-head .period { color: var(--clay); }
.line-head .line { display: block; }

/* M02 · the drop - each word starts down + invisible.
   Gated to .js so headline stays visible if JS fails to boot.
   Animation is driven from site.js via inline rAF tweens (the
   prototype's "gotcha-1": WAAPI / CSS transitions get stuck in
   some iframe contexts). CSS only owns the initial-state and the
   reduced-motion fallback - the timing lives in the JS so we can
   stagger imperatively. Production rebuilds this on a GSAP
   timeline with the same shape. */
.line-head .word {
  display: inline-block;
}
.line-head > .line > .word,
.line-head > .line > .period {
  display: inline-block;
  will-change: transform, opacity;
}
/* Inter-word spacing comes from the natural whitespace text nodes
   between sibling .word spans in the markup. No CSS word-spacing
   override - it stacks on top of the natural gap and forces the
   headline to wrap onto more lines than intended. */

html.js .line-head > .line > .word {
  opacity: 0;
  transform: translateY(28px);
}
html.js .line-head > .line > .period {
  opacity: 0;
  transform: translateY(16px);
}

html.js .ch-02-foot {
  opacity: 0;
  transform: translateY(8px);
}

/* M03 · carbon copy - duplicate behind the headline at Stone-α.
   site.js drives the offset off scroll velocity. */
.line-head .carbon {
  position: absolute;
  inset: 0;
  color: rgba(168, 156, 138, 0.42); /* stone @ ~42% */
  pointer-events: none;
  z-index: -1;
  transform: translate(2px, 2px);
  will-change: transform;
}
.line-head .carbon em { color: rgba(31, 77, 74, 0.32); }
.line-head .carbon .period { color: rgba(181, 87, 58, 0.32); }

.ch-02-foot {
  margin-top: 64px;
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--font-mono);
  font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp);
  text-transform: uppercase;
  color: var(--stone);
}
.ch-02-foot .period { color: var(--clay); }


/* ----------------------------------------------------------------
   Chapter 06 - The proof
   Surface: Bone with one Ink wipe panel between cases.
   Pin runway: 400vh - 100vh entry/peel + 300vh pin scrub through
   3 horizontal panels (case 1, wipe, case 2).
   ----------------------------------------------------------------*/

.ch-06 {
  position: relative;
  /* Pin runway. 100svh peel + ~620svh of pinned horizontal scrub. Long on
     purpose: each beat (title arc-draw, hold, the case-to-case slide) needs
     real scroll pixels, or the motion reads as hyper-sensitive and jumpy. */
  height: 720svh;
  background: var(--bone);
}

.ch-06-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background: var(--bone);
}

/* M17 · reverse parallax - vertical hairlines under the pinned area.
   Drag faster than the foreground translation so the eye gets pulled
   through. Rendered as a single repeating linear-gradient strip. */
.ch-06-bg {
  position: absolute; inset: 0;
  background-image: repeating-linear-gradient(
    to right,
    transparent 0,
    transparent 119px,
    rgba(168, 156, 138, 0.18) 119px,
    rgba(168, 156, 138, 0.18) 120px
  );
  pointer-events: none;
}

/* M16 · the peel - section header that lives at the top of the
   pinned shell. Each character has its own clip-path reveal that
   site.js drives off scroll progress. */
.ch-06-header {
  position: absolute;
  /* Fluid top offset: scales down on short viewports so the peel header
     never reads into the case content below it. */
  top: clamp(72px, 12vh, 120px); left: 0; right: 0;
  display: flex; flex-direction: column; gap: 14px;
  overflow: hidden;
  align-items: center;
  z-index: 4;
  pointer-events: none;
  will-change: transform, opacity;
}
.ch-06-header .mono {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  /* Stone (mid-tone), not --stamp-fg: this header is absolutely positioned
     over the pin and the dark case panels slide UNDER it, so the darker
     stamp tone would vanish against them. Stone reads on both Bone and dark. */
  color: var(--stone);
}
.ch-06-header .mono .period { color: var(--clay); margin: 0 4px; }
/* The chip in the Ch6 header rides over dark panels: force the inverted
   (Bone bg / Ink text) treatment so it stays legible across the scrub. */
.ch-06-header .stamp-chip { background: var(--bone) !important; color: var(--ink) !important; }
.peel-headline {
  /* Tabled per William: the chapter headline is dropped - the pinned
     "SECTION VI · THE PROOF" eyebrow carries the chapter. Removing it also
     kills the stray Clay period-dot that floated over the case titles. */
  display: none;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(36px, 4.5vw, 64px);
  line-height: 1.05; letter-spacing: -0.015em;
  margin: 0; color: var(--ink);
  text-align: center;
  white-space: nowrap;
}
.peel-headline em { font-style: italic; color: var(--harbor); font-weight: 400; }
.peel-headline .period { color: var(--clay); }
.peel-char {
  display: inline-block;
  /* Initial state: clipped from the right (invisible). The reveal
     uses clip-path: inset(0 100% 0 0) to inset(0 0 0 0), driven via
     inline style by site.js. */
  clip-path: inset(0 100% 0 0);
  will-change: clip-path;
}
.peel-char.space { width: 0.32em; }

/* The horizontal track. Three panels each 100vw wide; total 300vw. */
.ch-06-track {
  position: absolute; inset: 0;
  display: flex;
  width: 300vw;
  /* translateX driven inline from scroll progress via translate3d on this
     element. No will-change on panels - promoting children that ride a
     transformed ancestor forces 3 oversized textures recomposited every
     frame (net-negative). The translate3d on the track is composited
     without panel promotion. */
}
.ch-06-panel {
  flex: 0 0 100vw;
  height: 100vh;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Case panels - Bone surface, three-column composition. */
.case-panel {
  background: var(--bone);
  padding: clamp(112px, 18vh, 152px) clamp(40px, 6vw, 96px) clamp(56px, 9vh, 96px);
}
/* Case 1 (the first panel the visitor sees, while the peel header is
   still on screen) gets a bigger top gutter so the header doesn't
   read into the case meta + headline.  Case 2 and the wipe panel are
   reached after the header has faded, so they keep the normal padding.
   This is the OUTER panel's padding only - applying to the inner
   element too would double up.  */
.ch-06-panel.case-panel[data-case="1"] {
  padding-top: clamp(120px, 24vh, 240px);
}
.case-panel-inner {
  width: 100%;
  max-width: 1240px;
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: 80px;
  align-items: start;
}
.case-meta {
  display: flex; flex-direction: column; gap: 22px;
}
/* The app-icon mark + project-label stamp now share ONE row (icon left, label
   right, vertically centred against the ~64px tile), so the headline/body rise
   up beneath them as a single block. */
.case-detail-head {
  display: flex; align-items: center;
  gap: clamp(14px, 1.4vw, 20px);
}
.case-meta-stamp {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stamp-fg);
  display: inline-flex; align-items: baseline; gap: 10px;
}
.case-meta-stamp .label { color: var(--ink); }
.case-meta-stamp .period { color: var(--clay); }
.case-meta-stamp .sector { color: var(--ink); font-weight: 500; }
/* Legibility guard: the revealed case-meta-stamp sits on the Bone case panel,
   where --stamp-fg (dark) reads correctly. Should a case panel ever be flipped
   to an Ink surface, the dark stamp + dark chip would go invisible - so on any
   ink-surfaced case panel, render the stamp light and invert its chip to the
   Bone treatment (matching the title-card / header chip overrides). Scoped to
   data-surface="ink" so it never lightens text on the current Bone panels. */
.case-panel[data-surface="ink"] .case-meta-stamp,
[data-surface="ink"] .case-panel .case-meta-stamp {
  color: var(--fg-quiet-on-ink);
}
.case-panel[data-surface="ink"] .case-meta-stamp .label,
.case-panel[data-surface="ink"] .case-meta-stamp .sector,
[data-surface="ink"] .case-panel .case-meta-stamp .label,
[data-surface="ink"] .case-panel .case-meta-stamp .sector {
  color: var(--fg-quiet-on-ink);
}
.case-panel[data-surface="ink"] .case-meta-stamp .stamp-chip,
[data-surface="ink"] .case-panel .case-meta-stamp .stamp-chip {
  background: var(--bone) !important;
  color: var(--ink) !important;
}

.case-headline {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(36px, 3.8vw, 56px);
  line-height: 1.05; letter-spacing: -0.015em;
  margin: 0; color: var(--ink);
  text-wrap: balance;
}
.case-headline em { font-style: italic; color: var(--harbor); font-weight: 400; }
.case-headline .period { color: var(--clay); }

.case-body {
  display: flex; flex-direction: column; gap: 14px;
  font-family: var(--font-body); font-size: 16px; line-height: 1.55;
  color: var(--ink);
  max-width: 52ch;
}
.case-body p { margin: 0; }

.case-footer-stamp {
  margin-top: 8px;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stamp-fg);
}
.case-footer-stamp .period { color: var(--clay); }

.case-cta {
  display: inline-flex; align-items: baseline; gap: 6px;
  font-family: var(--font-body); font-size: 16px;
  color: var(--ink);
  border-bottom: 1px solid var(--ink);
  padding-bottom: 2px;
  align-self: flex-start;
  transition: color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.case-cta:hover { color: var(--clay); border-color: var(--clay); }
.case-cta .period { color: var(--clay); }

/* Metrics column - three big numbers, vertical. */
.case-metrics {
  display: flex; flex-direction: column; gap: clamp(20px, 4vh, 36px);
  padding-left: clamp(32px, 4vw, 56px);
  border-left: 1px solid var(--border);
}
.case-metric {
  display: grid;
  grid-template-columns: auto auto;
  align-items: baseline;
  gap: 8px;
  cursor: default;
}
.case-metric .value {
  font-family: var(--font-display); font-weight: 400;
  /* Height-aware: shrinks only on short viewports (where min picks vh)
     so the three big numbers can't overflow the pin; normal/tall sizes
     keep the 9vw scale untouched. */
  font-size: clamp(56px, min(9vw, 20vh), 132px);
  line-height: 0.95; letter-spacing: -0.04em;
  color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums;
  display: inline-block;
  /* will-change for the rAF tween writing inline transforms */
  will-change: transform;
}
.case-metric .unit {
  font-family: var(--font-display); font-weight: 400;
  font-style: italic;
  font-size: clamp(28px, 3vw, 44px);
  line-height: 1; letter-spacing: -0.01em;
  color: var(--clay);
  opacity: 0;
  transform: translateY(8px);
  will-change: opacity, transform;
}
.case-metric .label {
  grid-column: 1 / -1;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
  margin-top: 2px;
}
.case-metric .label .period { color: var(--clay); }
.case-metric .prefix {
  /* For "$430K" - the $ is a separate inline so it lands flush
     and doesn't tick. */
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(40px, 5vw, 72px);
  line-height: 1; color: var(--ink);
  margin-right: 4px;
  align-self: flex-start;
  padding-top: 0.12em;
}

/* M28 · the arc-drawn case title.
   An Ink title card covers each case panel above its .case-panel-inner.
   The Clay great-circle arc draws on under the project name, holds, then
   the whole card lifts + fades to reveal the case beneath.  All motion is
   driven inline by applyCh06() from the panel's LOCAL scroll progress;
   the card carries real text so it survives as a fallback.  z-index keeps
   it above .case-panel-inner until cleared, at which point JS sets
   pointer-events none so the revealed CTA / metrics are interactive. */
.case-title-card {
  position: absolute;
  inset: 0;
  z-index: 10;
  background: var(--ink);
  color: var(--bone);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 3.4vh, 40px);
  padding: clamp(64px, 12vh, 140px) clamp(40px, 6vw, 96px);
  text-align: center;
  /* The "lifting edge" - a Clay hairline that sweeps up the case as the sheet
     pulls off in beat 2 (the signature dark->light moment). */
  border-bottom: 2px solid var(--clay);
  will-change: transform, box-shadow;
  /* M28 (v9.4) is a SHEET PULL: applyCh06() writes translateY (the sheet
     slides up + off) + a warm drop shadow inline per frame.  The sheet stays
     opaque and MOVES - it does not fade.  The light .case-panel-inner detail
     beneath is revealed as the sheet clears. */
}
/* The revealed case content rises a few px through the draining card as the
   wipe uncovers it (cross-rise), so the metrics/story settle in rather than
   pop.  applyCh06() writes the transform/opacity inline; this is the at-rest
   pre-reveal state for the desktop pin only (mobile resets below). */
.case-panel .case-panel-inner {
  will-change: transform, opacity;
}

/* M28 (v9.4) · the sheet TITLE - a glyph-roll project name. applyCh06()
   scrambles -> resolves the .case-sheet-title-t text node each frame as the
   case locks; the trailing period is a separate always-Clay span. */
.case-sheet-title {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(40px, 6.2vw, 88px);
  letter-spacing: -0.02em; line-height: 1;
  color: var(--bone);
  white-space: nowrap;
}
.case-sheet-title .period { color: var(--clay); }
/* M29-style fixed glyph cells: each title character is an inline-block cell
   whose width is pinned by JS (measureCh06TitleCells) to its own glyph advance,
   so the spinning scramble never reflows. The .period stays a normal span. */
.case-sheet-title .gl { display: inline-block; text-align: center; }

/* The "SECTION VI · THE PROOF" chapter eyebrow, relocated INTO the title sheet
   so it rides UP and off with the sheet pull (beat 2) instead of staying
   pinned. Absolute near the old chapter-eyebrow position, centred above the
   glyph-roll title. The .case-title-card .stamp-chip rule below keeps the chip
   on Bone/Ink so it reads on the Ink sheet. */
.case-sheet-eyebrow {
  position: absolute;
  top: clamp(74px, 12vh, 96px);
  left: 0; right: 0;
  text-align: center;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--fg-quiet-on-ink);
  pointer-events: none;
}
.case-sheet-eyebrow .period { color: var(--clay); margin: 0 4px; }

/* The line DEVICE under the title (geodesic arc for Paper Trail; fork for
   Branchline). applyCh06() draws the stroke(s) via stroke-dashoffset and
   reveals the nodes/labels. overflow:visible so endpoint rings + labels that
   sit outside the path's box aren't clipped. */
.case-sheet-device {
  display: block;
  width: min(620px, 64vw);
  height: auto;
  overflow: visible;
  margin-top: clamp(18px, 3vh, 30px);
}
.case-sheet-device text { font-family: var(--font-mono); }
/* JS scales these in from center; needs fill-box so the scale pivots on the
   node, not the SVG origin. */
.case-sheet-device .dev-nodeMid,
.case-sheet-device .dev-nodeEnd {
  transform-box: fill-box;
  transform-origin: center;
}

/* The app-icon mark, relocated OUT of the title sheet and onto the light
   project detail ("project page"). The Bone surface lets the mark's own
   shadow read, so no ring is needed. */
.case-detail-mark {
  width: clamp(52px, 5vw, 66px);
  height: clamp(52px, 5vw, 66px);
  /* In a flex row now (.case-detail-head) - no stacked bottom margin. */
  margin-bottom: 0;
  flex: 0 0 auto;
}
.case-detail-mark > svg { display: block; width: 100%; height: 100%; }
.case-title-stamp {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--fg-quiet-on-ink);
}
.case-title-stamp .period { color: var(--clay); }
/* The card is an Ink surface but carries no data-surface="ink" hook, so its
   CASE 01 / CASE 02 chip would keep the default Ink-bg / Bone-text treatment
   and the chip background would vanish into the Ink card. Force the inverted
   Bone-bg / Ink-text treatment here (same fix as the Ch6 header chip). */
.case-title-card .stamp-chip {
  background: var(--bone) !important;
  color: var(--ink) !important;
}
.case-title-name {
  position: relative;
  margin: 0;
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(40px, 6vw, 96px);
  line-height: 1.02; letter-spacing: -0.02em;
  color: var(--bone);
  text-wrap: balance;
}
/* Arc rides directly under the project NAME, sweeping its full width and
   sitting high in the card (mid-card, just beneath the title) where the eye
   already is - not down at the panel bottom. Anchored to the name's box so
   the great-circle reads as drawing on under the title. */
.case-title-arc-layer {
  position: absolute;
  left: 50%;
  /* Hug the name's baseline: a small fixed gap below the name box keeps the
     sweep tucked right under the title rather than floating low. */
  top: calc(100% + clamp(6px, 1vh, 14px));
  transform: translateX(-50%);
  width: min(72vw, 620px);
  height: clamp(28px, 4vh, 52px);
  pointer-events: none;
  overflow: visible;
}
.case-title-arc-line {
  fill: none;
  stroke: var(--clay);
  stroke-width: 1.6;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  /* stroke-dasharray + stroke-dashoffset set inline by site.js (= path
     length) so the arc draws head-first as the panel's local progress
     advances through the DRAW phase. */
}
/* The drawing head: a Clay dot that rides the painting tip along the arc.
   site.js positions it each frame via getPointAtLength(drawn) so the eye
   follows the Clay laying down the line, landing at the arc's end.  Hidden
   until the draw starts and after it completes (set inline). */
.case-title-arc-dot {
  fill: var(--clay);
  opacity: 0;
  will-change: transform, opacity;
}
.case-title-coord {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--fg-quiet-on-ink);
}
.case-title-coord .period { color: var(--clay); }

/* M23 · double-take hover state - applied via inline rAF tween in JS;
   no CSS keyframe (transitions inside the iframe were flaky). */

/* Between-case Ink wipe panel (M07 in panel form).
   Full-bleed Ink, with a Clay period in the bottom-left. */
.wipe-panel {
  background: var(--ink);
  color: var(--bone);
  position: relative;
  overflow: hidden;
}
.wipe-panel-stamp {
  position: absolute;
  bottom: 96px; left: 96px;
  display: flex; align-items: baseline; gap: 14px;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--fg-quiet-on-ink);
}
.wipe-panel-stamp .period {
  font-family: var(--font-display);
  font-size: 96px;
  line-height: 0; color: var(--clay);
  margin-right: 4px;
  display: inline-block;
  transform: translateY(0.08em);
}
.wipe-panel-quote {
  position: absolute;
  inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 0 12vw;
}
.wipe-panel-quote p {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(32px, 4vw, 56px);
  line-height: 1.1; letter-spacing: -0.015em;
  color: var(--bone);
  margin: 0;
  text-wrap: balance;
  text-align: center;
  max-width: 22ch;
}
.wipe-panel-quote em { font-style: italic; color: var(--fg-cool-on-ink); font-weight: 400; }
.wipe-panel-quote .period { color: var(--clay); }

/* Pagination dots - bottom-center, show which panel is centered. */
.ch-06-paging {
  position: absolute;
  bottom: 40px; left: 0; right: 0;
  z-index: 5;
  display: flex; justify-content: center; gap: 10px;
  pointer-events: none;
}
.ch-06-paging i {
  display: block;
  width: 24px; height: 1px;
  background: var(--bone-400);
  transition: background var(--dur-fast) var(--ease-standard);
}
.ch-06-paging i.is-active { background: var(--clay); }

/* Very short viewports (landscape phones, letterboxed windows): compress
   the case composition so the three-column content fits the pin without
   clipping the headline/CTA. */
@media (max-height: 720px) {
  .case-panel { padding-top: clamp(92px, 15vh, 130px); padding-bottom: clamp(36px, 7vh, 64px); }
  .ch-06-panel.case-panel[data-case="1"] { padding-top: clamp(104px, 17vh, 160px); }
  .case-headline { font-size: clamp(26px, 3.2vw, 40px); }
  .case-body { font-size: 15px; }
  .case-body, .case-meta { gap: clamp(8px, 1.6vh, 14px); }
  .case-panel-inner { gap: clamp(32px, 5vw, 80px); }
}



/* ============================================================
   Tweaks panel - toggle hooks
   data-tweak-* attributes on <body> are written by tweaks.jsx.
   Pure CSS toggles; no JS bookkeeping.
   ============================================================ */
body[data-tweak-trail="off"] .shell-trail { display: none !important; }
body[data-tweak-ticker="off"] .shell-ticker { display: none !important; }
body[data-tweak-place-stamps="off"] .shell-stamp { display: none !important; }
body[data-tweak-carbon="off"] .carbon { display: none !important; }
body[data-tweak-motion="off"] .carbon,
body[data-tweak-motion="off"] .shell-trail,
body[data-tweak-motion="off"] .shell-ticker { display: none !important; }

/* ============================================================
   Form submission status (Resend wiring)
   .form-hp is the visually-hidden honeypot.  .form-status is the
   success / failure line forms.js swaps in; it settles in on
   --ease-out (honest decelerate), instant under reduced motion.
   ============================================================ */
.form-hp {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status {
  font-family: var(--font-body);
  text-align: center;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}

/* Confirmation colour is set per form card so it always contrasts:
   the .ch-09-form card is Bone-100 (dark Ink text); the .theta-form
   card is Ink-800 (light Bone text). */
.ch-09-form .form-status { color: var(--ink); }
.theta-form .form-status { color: var(--bone); }

.form-status.is-in {
  opacity: 1;
  transform: translateY(0);
}

.form-status.is-failure {
  margin-top: 1rem;
}

.form-status .period {
  color: var(--clay);
}

.form-status a {
  color: inherit;
  border-bottom: 1px solid currentColor;
}

/* ----------------------------------------------------------------
   Shared two-continents map component (.synqa-map-svg)
   Used by Ch 8 on the homepage (inside .ch-08-arc-wrap) and by
   About section IV (inside .about-arc-wrap). Shared so both
   instances are visually identical. Wrapper layout and city-dot
   positioning stay in pass3.css / subpages.css respectively.
   ---------------------------------------------------------------- */

/* The SVG fills its container. */
.synqa-map-svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Great-circle arc stroke - gradient referenced by the per-page
   <linearGradient id> baked into the SVG markup. */
.synqa-map-svg .synqa-map-arc {
  fill: none;
  stroke: url(#synqaMapGrad);
  stroke-width: 1.4;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

/* Equator / latitude reference hairlines. */
.synqa-map-svg .synqa-map-bg-line {
  fill: none;
  stroke: rgba(199, 190, 169, 0.12);
  stroke-width: 0.5;
  stroke-dasharray: 2 5;
  vector-effect: non-scaling-stroke;
}

/* Coastline paths (Natural Earth 110m). */
.synqa-map-svg .synqa-map-coastline path {
  fill: none;
  stroke: var(--stone);
  stroke-opacity: 0.3;
  stroke-width: 0.8;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

/* SVG city circles - hidden in favour of the fixed-size HTML dots
   (.synqa-map-city-dot) that sit over the SVG. */
.synqa-map-svg .synqa-map-dot {
  stroke-width: 0;
  display: none;
}

/* Coordinate labels. */
.synqa-map-svg .synqa-map-coord {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.synqa-map-svg .synqa-map-coord.toronto  { fill: var(--clay); }
.synqa-map-svg .synqa-map-coord.capetown { fill: var(--fg-cool-on-ink); }

/* Brand period at the arc apex. */
.synqa-map-svg .synqa-map-period {
  font-family: var(--font-display);
  fill: var(--clay);
  font-size: 24px;
}

/* Fixed-size HTML city dots (shared between pages).
   Positioned by the wrapper (pass3.css / subpages.css). */
.synqa-map-city-dot {
  position: absolute;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  pointer-events: none;
}
.synqa-map-city-dot.toronto  { left: 27.969%; top: 25.870%; background: var(--clay); }
.synqa-map-city-dot.capetown { left: 55.156%; top: 68.913%; background: var(--fg-cool-on-ink); }

/* ----------------------------------------------------------------
   Desktop nav dropdown - Mobile Apps
   Hover + focus-within opens; JS toggles aria-expanded for click/kbd.
   ---------------------------------------------------------------- */
.shell-nav-dropdown {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.shell-nav-droptoggle {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(13px, 0.95vw, 14px);
  letter-spacing: -0.005em;
  color: var(--ink);
  background: none; border: none; cursor: pointer;
  padding: 0; white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-standard);
}
.shell-nav-droptoggle:hover,
.shell-nav-dropdown:focus-within .shell-nav-droptoggle { color: var(--clay); }
/* Caret - small Clay chevron that flips open */
.shell-nav-droptoggle::after {
  content: "";
  display: inline-block;
  width: 7px; height: 5px;
  background: currentColor;
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.shell-nav-dropdown[data-open="true"] .shell-nav-droptoggle::after,
.shell-nav-dropdown:focus-within .shell-nav-droptoggle::after {
  transform: rotate(180deg);
}
.shell-nav-dropmenu {
  position: absolute;
  top: calc(100% + 10px); left: 50%;
  transform: translateX(-50%);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-2) 0;
  min-width: 148px;
  display: flex; flex-direction: column;
  /* Hidden by default; shown via hover/focus-within or data-open */
  opacity: 0; pointer-events: none; visibility: hidden;
  transform: translateX(-50%) translateY(-4px);
  transition: opacity var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard),
              visibility 0s var(--dur-fast);
}
/* Transparent hover bridge across the gap so the menu stays open while the
   pointer travels from the toggle down to the panel (it was closing too soon). */
.shell-nav-dropmenu::before {
  content: "";
  position: absolute;
  top: -14px; left: 0; right: 0; height: 14px;
}
.shell-nav-dropdown:hover .shell-nav-dropmenu,
.shell-nav-dropdown:focus-within .shell-nav-dropmenu,
.shell-nav-dropdown[data-open="true"] .shell-nav-dropmenu {
  opacity: 1; pointer-events: auto; visibility: visible;
  transform: translateX(-50%) translateY(0);
  transition: opacity var(--dur-fast) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard),
              visibility 0s 0s;
}
.shell-nav-dropmenu a {
  display: block;
  padding: 9px var(--space-3);
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(13px, 0.95vw, 14px);
  color: var(--ink); border-bottom: none;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}
.shell-nav-dropmenu a:hover { color: var(--clay); background: var(--bone-400); }
.shell-nav-dropmenu .period { color: var(--clay); }
/* is-active highlight inside the dropdown */
.shell-nav-dropmenu a.is-active { color: var(--clay); }

/* ----------------------------------------------------------------
   Shared footer  (moved from subpages.css so index.html gets it;
   subpages.css no longer carries these rules)
   ---------------------------------------------------------------- */
.subpage-footer {
  background: var(--ink);
  color: var(--bone);
  padding: 72px 0 56px;
}
.subpage-footer-inner {
  max-width: var(--content-w); margin: 0 auto; padding: 0 80px;
  display: grid;
  /* Anchor the mark to the left edge and ELSEWHERE to the right edge (matching
     the full-width baseline row below), splitting the slack evenly between the
     three columns instead of pooling it into one void on either side. */
  grid-template-columns: auto auto auto;
  justify-content: space-between;
  gap: 48px;
}
.subpage-footer-mark {
  display: flex; flex-direction: column; gap: 18px;
}
.subpage-footer-mark img { height: 32px; width: auto; }
.subpage-footer-stamp {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
}
.subpage-footer-stamp .period { color: var(--clay); margin: 0 6px; }
.subpage-footer-stamp .toronto { color: var(--clay); }
.subpage-footer-stamp .capetown { color: var(--fg-cool-on-ink); }
/* Keep each city coordinate atomic on narrow viewports */
.subpage-footer-stamp .toronto,
.subpage-footer-stamp .capetown { white-space: nowrap; }
/* Business address in footer col-1 */
.subpage-footer-address {
  font-style: normal;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
  display: flex; flex-direction: column; gap: 4px;
}
.subpage-footer-sitemap {
  display: flex; flex-direction: column; gap: 10px;
}
.subpage-footer-sitemap-label {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone); margin-bottom: 4px;
}
.subpage-footer-sitemap-label .period { color: var(--clay); }
/* Two sub-columns within the Pages sitemap */
.subpage-footer-sitemap-cols {
  display: flex; flex-direction: row; gap: 32px;
}
.subpage-footer-sitemap-col {
  display: flex; flex-direction: column; gap: 10px;
}
.subpage-footer-sitemap a {
  color: var(--bone);
  border-bottom: 1px solid var(--border-on-ink);
  padding-bottom: 1px;
  font-size: 15px;
  align-self: flex-start;
}
.subpage-footer-sitemap a:hover { color: var(--clay); border-color: var(--clay); }
.subpage-footer-sitemap .period { color: var(--clay); }
.subpage-footer-bottom {
  margin-top: 64px;
  padding-top: 24px;
  border-top: 1px solid var(--border-on-ink);
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
  max-width: var(--content-w);
  padding-left: 80px; padding-right: 80px;
  margin-left: auto; margin-right: auto;
  display: flex; justify-content: space-between;
}
.subpage-footer-bottom .period { color: var(--clay); margin: 0 6px; }
.subpage-footer-bottom .footer-coord { white-space: nowrap; }

/* Footer responsive (was in subpages.css @media blocks) */
@media (max-width: 1279px) {
  .subpage-footer-inner { padding: 0 32px; }
}
@media (max-width: 1023px) {
  .subpage-footer-inner { grid-template-columns: 1fr; gap: 40px; padding: 0 32px; }
  .subpage-footer-bottom { flex-direction: column; gap: 14px; padding-left: 32px; padding-right: 32px; }
}

/* ============================================================
   M31 · The living marks - animated app-icon tiles.
   Product marks (Theta/Holm/Rewrite/PatchPal/Synqa) + the 8 /work
   case marks + the /work background-map pin ring. Keyframes and the
   reusable tile, transcribed from the design handoff
   (Ideas-Fixes References/design_handoff_animations_and_map/Synqa Icons.dc.html).
   Lives in site.css so every page can host a mark. Honest motion:
   slow always-on loops, small moves; no reduced-motion gate
   (see dec-no-reduced-motion).
   ============================================================ */

/* The handoff authors easing as var(--ease-in-out-quint); the site only
   ships --ease-out / --ease-standard (the "quint" curves are JS-only), so
   define the CSS token here (NOT in foundations.css, which mirrors the
   design system) to keep the spec markup verbatim. */
:root { --ease-in-out-quint: cubic-bezier(0.83, 0, 0.17, 1); }

/* Product-mark loops */
@keyframes synBar    { 0%,100%{ transform: scaleY(.32) } 50%{ transform: scaleY(1) } }
@keyframes synGlow   { 0%,100%{ stroke-opacity:.42 } 50%{ stroke-opacity:1 } }
@keyframes synScan   { 0%{ transform: translateY(-380px); opacity:0 } 14%{ opacity:.85 } 86%{ opacity:.85 } 100%{ transform: translateY(380px); opacity:0 } }
@keyframes synRot    { to { transform: rotate(360deg) } }
@keyframes synTwinkle{ 0%,100%{ transform: scale(1); opacity:.92 } 50%{ transform: scale(1.06); opacity:1 } }
@keyframes synBeat   { 0%,100%{ transform: scale(1) } 45%{ transform: scale(1.2) } }
@keyframes ppWordBreathe { 0%,100%{ transform: scale(1) } 50%{ transform: scale(1.04) } }
@keyframes ppLeafWater   { 0%,100%{ transform: rotate(-3.5deg) skewX(2.5deg) } 50%{ transform: rotate(3.5deg) skewX(-2.5deg) } }

/* Work-mark loops */
@keyframes synScanSm { 0%{ transform: translateY(-5px); opacity:0 } 16%{ opacity:.95 } 84%{ opacity:.95 } 100%{ transform: translateY(30px); opacity:0 } }
@keyframes synDraw   { 0%{ stroke-dashoffset:60 } 50%{ stroke-dashoffset:0 } 78%{ stroke-dashoffset:0 } 100%{ stroke-dashoffset:-60 } }
@keyframes synSeq    { 0%{ opacity:0 } 10%{ opacity:1 } 90%{ opacity:1 } 100%{ opacity:0 } }
@keyframes synWave   { 0%,100%{ opacity:.28 } 50%{ opacity:1 } }

/* /work background-map pin ring */
@keyframes bgRing    { 0%{ transform: scale(.5); opacity:.8 } 70%{ opacity:0 } 100%{ transform: scale(3.2); opacity:0 } }
/* The re-iterate POP on a placed /work pin's dot (base scale 1.45 from .bgpin-dot). */
@keyframes bgPop     { 0%{ transform: scale(1.45) } 28%{ transform: scale(2.3) } 100%{ transform: scale(1.45) } }

/* The reusable tile: a rounded app-icon square that clips its mark and
   lifts on hover. Size is set by the host context (work tiles are large,
   home/hero marks smaller). */
.app-mark {
  position: relative;
  display: block;
  border-radius: 22%;
  overflow: hidden;
  box-shadow: var(--shadow-md);
  transition: transform .35s var(--ease-out);
  flex: none;
}
.app-mark > svg,
.app-mark > img { display: block; width: 100%; height: 100%; }
.app-mark:hover { transform: translateY(-9px); }

/* Product-hero icon row: icon sits LEFT of the stamp label on all product pages
   and the home Theta showcase. Rules live in site.css because index.html does
   not load subpages.css. */
.ch-07-hero-row { display: flex; align-items: center; gap: clamp(14px, 1.6vw, 22px); }
.product-hero-mark { width: clamp(60px, 6.5vw, 88px); height: clamp(60px, 6.5vw, 88px); flex: 0 0 auto; margin: 0; }
.product-hero-mark > svg { display: block; width: 100%; height: 100%; }

/* PatchPal is a layer stack (lime bg + breathing wordmark + swaying leaf). */
.app-mark .pp-bg   { position: absolute; inset: 0; background: #A1C935; }
.app-mark .pp-words{ position: absolute; inset: 0; width: 100%; height: 100%; transform-box: border-box; transform-origin: 50% 50%; animation: ppWordBreathe 4.4s ease-in-out infinite; }
.app-mark .pp-leaf { position: absolute; inset: 0; width: 100%; height: 100%; transform-box: border-box; transform-origin: 78% 53%; animation: ppLeafWater 5s ease-in-out infinite; }

/* Home Ch6 (the proof) - a small living mark above each case-study title.
   The chapter is Ink, so a hairline ring defines the tile edge (the --shadow-md
   doesn't read on dark). Lives inside .case-title-card, so it rides/clears with
   the M28 title card. */
.case-title-mark {
  width: clamp(50px, 5vw, 62px);
  height: clamp(50px, 5vw, 62px);
  margin: 0 auto 20px;
  border: 1px solid var(--border-on-ink);
}
.case-title-mark > svg { display: block; width: 100%; height: 100%; }

