/* ============================================================
   synqa.ca - pass 3 styles
   Chapters 3, 4, 5, 7, 8, 9 + their transitions.
   Pass-1 site.css holds the persistent shell and Ch 1+2.
   Pass-2 (also site.css) holds Ch 6.
   This file ships the rest of the homepage.
   ============================================================ */

/* ----------------------------------------------------------------
   Chapter transitions - shared
   ----------------------------------------------------------------*/

/* M22 hairline weave + M07 wipe are staged together as 100vh
   transition sections. The wipe panel is absolutely positioned
   inside .chapter-wipe and its translateY is driven from site.js.

   The three weave hairlines (one horizontal from left, one diagonal
   from upper-left, one vertical from bottom) draw via stroke-dasharray
   reveal as the visitor enters the transition zone. They converge on
   a Clay period at the center of the viewport.
*/
.chapter-wipe {
  position: relative;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: transparent;
}
/* Per-transition heights. The interstitial backgrounds (the wipe-fill
   surface that the next chapter resolves on) ran too long, so each is
   shortened below the 100vh base. The wipe-fill animation in applyWipes()
   (pass3.js) carries a matching per-id runwayVh so the fill still completes
   its 100%->0% translate within the shorter section. Keep these two in sync:
   if a height changes here, update the runwayVh map in applyWipes. */
#ch-03half-transition.chapter-wipe { height: 54vh; height: 54svh; }
#ch-04half-transition.chapter-wipe { height: 46vh; height: 46svh; }
#ch-06half-transition.chapter-wipe { height: 64vh; height: 64svh; }
#ch-07half-transition.chapter-wipe { height: 56vh; height: 56svh; }
#ch-08half-transition.chapter-wipe { height: 62vh; height: 62svh; }

.chapter-wipe-weave {
  position: sticky;
  top: 0;
  width: 100%;
  /* Height tracks the (now shortened) section, NOT the full viewport. The weave
     centres its Clay period at 50% of its own height; at 100vh on a sub-viewport
     section the dot fell BELOW the band and was clipped by the section's
     overflow:hidden (it "disappeared"). Sizing to the band keeps the dot centred
     within the visible ink, at any section height. */
  height: 100%;
  z-index: 2;
  pointer-events: none;
  display: block;
}
.chapter-wipe-weave line {
  fill: none;
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.chapter-wipe-weave .weave-h { stroke: var(--bone-400); }
.chapter-wipe-weave .weave-d { stroke: var(--bone-400); }
.chapter-wipe-weave .weave-v { stroke: var(--bone-400); }
.chapter-wipe[data-from="ink"] .weave-h,
.chapter-wipe[data-from="ink"] .weave-d,
.chapter-wipe[data-from="ink"] .weave-v { stroke: var(--border-on-ink); }
.chapter-wipe-weave .weave-period {
  font-family: var(--font-display);
  font-size: 64px;
  fill: var(--clay);
  dominant-baseline: middle;
  text-anchor: middle;
  opacity: 0;
}

/* The full-bleed wipe panel. Lives behind the weave; translateY is
   driven 1:1 from the transition's scroll progress. The 1px edge
   hairline sits flush along the top edge of the wipe. */
.chapter-wipe-fill {
  position: absolute;
  inset: 0;
  transform: translateY(100%);
  will-change: transform;
  pointer-events: none;
}

/* SUB-PIXEL BOUNDARY SEAMS (every chapter/wipe edge). A fractional-pixel gap at a
   section boundary let a sliver of the neighbouring surface show as a faint hairline,
   most visible at the wipe edges (start of ch-05/ch-08/ch-09 + the ch-04->ch-04.5 line).
   Earlier tries (over-extending .chapter-wipe-fill past its section) FAILED because
   `.chapter-wipe` is `overflow:hidden`, which clips the extra pixel. The robust fix:
   pull EVERY chapter up 1px so it overlaps its predecessor's bottom edge - this happens
   OUTSIDE the predecessor's box (a margin), so overflow can't clip it, and the overlap is
   always same-surface-colour (a wipe's source bg = the previous chapter; a wipe's fill =
   the next chapter), so it is invisible while it closes the gap. */
.chapter { margin-bottom: -1px; }
.chapter-wipe[data-from="bone"] .chapter-wipe-fill { background: var(--ink); }
.chapter-wipe[data-from="ink"] .chapter-wipe-fill { background: var(--bone); }
.chapter-wipe-fill::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
}
.chapter-wipe[data-from="bone"] .chapter-wipe-fill::before { background: var(--bone-400); }
.chapter-wipe[data-from="ink"]  .chapter-wipe-fill::before { background: var(--border-on-ink); }

/* ----------------------------------------------------------------
   Chapter 03 · The chair
   Bone surface. Headline + three redacted lines + italic close.
   ----------------------------------------------------------------*/

.ch-03 {
  position: relative;
  min-height: 130vh;
  min-height: 130svh;
  /* Trailing bone trimmed (was 22vh) - too much empty Bone sat between the Ch3
     close and the Ch3.5 weave. */
  padding: 18vh 0 12vh;
}
.ch-03-inner {
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 80px;
  display: flex;
  flex-direction: column;
  gap: 64px;
}
.ch-03-stamp {
  font-family: var(--font-mono);
  font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp);
  text-transform: uppercase;
  color: var(--stamp-fg);
}
.ch-03-stamp .period { color: var(--clay); margin: 0 6px; }

.ch-03-head {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 5.4vw, 84px);
  line-height: 1.04;
  letter-spacing: -0.018em;
  margin: 0;
  color: var(--ink);
  text-wrap: balance;
  max-width: 22ch;
  position: relative;
}
.ch-03-head em { font-style: italic; color: var(--harbor); font-weight: 400; }
.ch-03-head .period { color: var(--clay); }

/* M03 carbon copy on the Ch 3 headline.  Position-locked behind the
   primary; offset reacts to scroll velocity through the global lerp. */
.ch-03-head .carbon {
  position: absolute;
  inset: 0;
  color: rgba(168, 156, 138, 0.42);
  pointer-events: none;
  z-index: -1;
  transform: translate(2px, 2px);
  will-change: transform;
}
.ch-03-head .carbon em { color: rgba(31, 77, 74, 0.32); }
.ch-03-head .carbon .period { color: rgba(181, 87, 58, 0.32); }

/* The redacted line list.  Each row is two elements stacked at the
   same coordinates: the underlying text and the Ink-900 bar painted
   over it.  Cursor proximity within 40px (or click on touch) lifts
   the bar right-to-left. */
.ch-03-lines {
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: 920px;
}
.ch-03-line {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 18px;
  padding: 4px 0;
  cursor: default;
}
.ch-03-line .lead {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 32px;
  color: var(--clay);
  line-height: 1;
  flex: 0 0 auto;
  transform: translateY(-2px);
}
.ch-03-line-body {
  position: relative;
  flex: 1 1 auto;
}
.ch-03-line-text {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 2.6vw, 34px);
  line-height: 1.2;
  color: var(--ink);
  display: inline-block;
  margin: 0;
}
.ch-03-line-text em { font-style: italic; color: var(--harbor); font-weight: 400; }
/* Reserve baseline space for the bar so the row doesn't reflow when
   it lifts.  The bar paints over the text at the exact same baseline. */
.ch-03-line-bar {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 100%;
  background: var(--ink-900);
  border-radius: 1px;
  /* Redacted from the first paint: the bar fully covers its text
     (inset 0 on all sides) so the line is never momentarily exposed,
     even before the script runs or on a fast scroll into the chapter.
     When the visitor "lifts" the bar, the inset sweeps to
     inset(0 100% 0 0) from the right edge, revealing the text
     right-to-left.  (The draw-in entrance was removed by request -
     the redaction now reads as covered-by-default.) */
  clip-path: inset(0 0 0 0);
  will-change: clip-path;
  pointer-events: none;
}
.ch-03-line[data-state="drawn"]   .ch-03-line-bar { clip-path: inset(0 0    0 0); }
.ch-03-line[data-state="lifted"]  .ch-03-line-bar { clip-path: inset(0 100% 0 0); }

/* Cursor hint centered ON the bar (owner ask: not above it); fades out after
   the first lift. z-index keeps it above .ch-03-line-bar; Bone text reads on the
   Ink bar, Clay dot as the accent. */
.ch-03-line-hint {
  position: absolute;
  top: 50%; left: 12px;
  transform: translateY(-50%);
  z-index: 3;
  font-family: var(--font-mono); font-size: 9px;
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--bone);
  opacity: 0;
  pointer-events: none;
  transition: opacity 240ms var(--ease-standard);
}
.ch-03-line[data-state="drawn"] .ch-03-line-hint { opacity: 1; }
.ch-03-line[data-state="lifted"] .ch-03-line-hint { opacity: 0; }
.ch-03-line-hint .dot { color: var(--clay); margin-right: 6px; }

/* M21 (Ch3 variant): the closing phrase is Fraunces italic THROUGHOUT and the
   reveal is COLOUR-ONLY (Stone -> Harbor). Owner ask: it must not change size or
   position when it changes colour, and an Inter->Fraunces font swap necessarily
   reshapes + re-widths the glyphs (Fraunces italic renders ~29px narrower at
   32px, so the word + Clay period jump left). The box itself never moved; only
   the glyphs did. site.js still toggles data-swapped ~200ms after entry; here
   that just fades the colour. (Other M21 instances keep the font swap.) */
.ch-03-close {
  margin-top: 12px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(24px, 2.6vw, 32px);
  color: var(--stone);
  letter-spacing: 0;
  display: inline-block;
  transition: color var(--dur-base) var(--ease-standard);
}
.ch-03-close[data-swapped="true"] {
  color: var(--harbor);
}
.ch-03-close .period { color: var(--clay); }

/* ----------------------------------------------------------------
   Chapter 04 · The turn (M18 split scroll)
   290vh chapter, sticky 100vh inner pin.  Two phases, both inside
   the fixed pinned shell - the half containers never translate.
     Phase A (prog 0 to 0.50): content motion inside the halves
       - left half (Bone)   operator-history words rise from below
                            into the centered stack (reads UP)
       - right half (Ink)   "We've been the client." word-spans
                            descend from above into the centered
                            headline (reads DOWN - opposite)
     Phase B (prog 0.50 to 0.80): the unified Ink panel (inset:0,
       full-bleed over both halves) fades in on easeOutQuint and
       resolves the composition with the headline and body.
     prog 0.80 to 1.0 holds the unified panel at full opacity inside
       the phase runway; then the pin holds for a further 90vh of
       native scroll before the M07 wipe to Ch 5 begins.
   ----------------------------------------------------------------*/

.ch-04 {
  position: relative;
  /* Of the pin runway, 100vh drives the M18 phases (prog 0 to 1):
       Phase A (0.00 to 0.30) content motion inside the halves
       Phase B (0.30 to 0.92) the two halves slide vertically
         off-screen, LINEAR - tied 1:1 to the wheel so the parting
         feels physical and takes ~6 wheel clicks rather than a
         front-loaded shoot-then-crawl.  Reveals the unified Ink
         panel underneath as they go.
       Phase C (0.92 to 1.0) held inside the phase runway
     and the trailing 30vh holds the pinned unified Ink composition
     just long enough to read before the M07 wipe begins.
     Keep in sync with pass3.js: CH04_PHASE_RUNWAY_VH (100)
     + CH04_HOLD_VH (30) + 100 (sticky pin) = 230. */
  height: 230vh;
  height: 230svh;
  background: var(--ink); /* the chapter resolves on Ink - chamber matches */
}
.ch-04-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background: var(--ink);
}

/* Two half-viewport columns, absolutely positioned.  Each half is a
   CARD that slides vertically in Phase B: the left half (bone) rises
   straight up off the top of the viewport, the right half (ink)
   descends off the bottom, revealing the unified Ink composition
   sitting beneath them (.ch-04-unified at z-index 1).  Each half is
   strictly clipped to its own 50vw column - overflow: hidden plus
   exact left/right anchoring guarantees the right half can never
   bleed into the left.  z-index 2 keeps them above the unified panel
   until they slide off. */
.ch-04-half {
  position: absolute;
  top: 0; bottom: 0;
  /* No width.  Each half anchors BOTH horizontal edges to the 50%
     center line of the pin (left: 0 / right: 50% and left: 50% /
     right: 0).  width: 50vw was the trap behind the visible overlap
     seam: vw units include the scrollbar gutter, but right: 0 resolves
     against the scrollbar-excluded content box, so the right half
     landed ~scrollbar-width pixels left of where the left half ended
     and the Ink bled over the Bone.  Anchoring both halves to the
     shared 50% boundary tiles them exactly, scrollbar or not. */
  overflow: hidden;
  z-index: 2;
  will-change: transform;
}
.ch-04-half-left {
  left: 0;
  right: 50%;
  background: var(--bone);
  color: var(--ink);
}
.ch-04-half-right {
  left: 50%;
  right: 0;
  background: var(--ink);
  color: var(--bone);
}

/* Left column - vertical stack of words, each rising from the bottom
   as scroll progresses.  Stack lives at the half-column's center;
   each .ch-04-word translates from below upward into its slot. */
.ch-04-words {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding: 0 6vw;
}
.ch-04-word {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 2.6vw, 40px);
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.015em;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(48px);
  will-change: opacity, transform;
}
.ch-04-word.is-in { opacity: 1; transform: translateY(0); }
.ch-04-word .period { color: var(--clay); }
.ch-04-half-left-stamp {
  position: absolute;
  top: 96px; left: 6vw; right: 6vw;
  font-family: var(--font-mono);
  font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp);
  text-transform: uppercase;
  color: var(--stamp-fg);
}
.ch-04-half-left-stamp .period { color: var(--clay); }

/* Right column - Ink.  "We've been the client." stack, with each
   word descending in from above as scroll progresses.  Stack lives
   at the right half's center. */
.ch-04-right-head {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 6vw;
}
.ch-04-right-head-line {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 5.8vw, 96px);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--bone);
  display: flex;
  gap: 0.22em;
  flex-wrap: wrap;
  justify-content: center;
}
.ch-04-right-head-line em { font-style: italic; color: var(--fg-cool-on-ink); font-weight: 400; }
.ch-04-right-head-line .period { color: var(--clay); }
.ch-04-right-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(-72px);
  will-change: opacity, transform;
}
.ch-04-right-word.is-in { opacity: 1; transform: translateY(0); }

.ch-04-half-right-stamp {
  position: absolute;
  bottom: 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);
}
.ch-04-half-right-stamp .period { color: var(--clay); }

/* Unified body - sits BENEATH the two halves at z-index 1, full-bleed
   on Ink.  Fully painted from the start of the chapter; the halves
   cover it (z-index 2) until Phase B slides them off the top and
   bottom of the viewport, progressively revealing the unified
   composition.  No opacity tween - the reveal is the halves moving. */
.ch-04-unified {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 36px;
  padding: 0 6vw;
  text-align: center;
  pointer-events: none;
  opacity: 1;
  z-index: 1;
  background: var(--ink);
  color: var(--bone);
}
.ch-04-unified-head {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(40px, 5.8vw, 96px);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--bone);
  margin: 0;
  max-width: 18ch;
}
.ch-04-unified-head em { font-style: italic; color: var(--fg-cool-on-ink); font-weight: 400; }
.ch-04-unified-head .period { color: var(--clay); }
.ch-04-unified-body {
  max-width: 56ch;
  font-family: var(--font-body);
  font-size: 17px; line-height: 1.6;
  color: var(--fg-quiet-on-ink);
  margin: 0;
}
.ch-04-unified-body em {
  font-family: var(--font-display); font-style: italic;
  color: var(--fg-cool-on-ink); font-weight: 400;
}
.ch-04-unified-stamp {
  font-family: var(--font-mono);
  font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp);
  text-transform: uppercase;
  color: var(--stone);
}
.ch-04-unified-stamp .period { color: var(--clay); }

/* Pagination - three thin marks bottom-center showing phase. */
.ch-04-phase {
  /* Owner: drop the little 3-bar progress meter at the bottom of ch-04. Hidden
     (not deleted) so applyCh04()'s is-active toggling stays harmless. */
  display: none;
  position: absolute;
  bottom: 32px; left: 0; right: 0;
  justify-content: center; gap: 10px;
  z-index: 4;
  pointer-events: none;
}
.ch-04-phase i {
  display: block; width: 24px; height: 1px;
  background: rgba(199, 190, 169, 0.4);
  transition: background 200ms var(--ease-standard);
}
.ch-04-phase i.is-active { background: var(--clay); }

/* ----------------------------------------------------------------
   Chapter 05 · What we build (services)
   Bone surface. Six self-building service cards (M30) on a HORIZONTAL PIN
   (desktop): the chapter header sits in normal flow ABOVE the pin and
   scrolls up and off as the visitor enters; then .ch-05-pin sticks at the
   top and the page's VERTICAL scroll scrubs the horizontal card track
   SIDEWAYS (driver: applyCh05Pin in pass3.js). The scrub is CONTINUOUS
   (card position follows scroll linearly); when the visitor stops and
   settles, a gentle JS settle-snap eases the nearest card to centre ONLY
   if it is already within ~20% of centre - they can rest between cards.
   No CSS scroll-snap, no scroll library (native scrollTo only). A card's
   scene builds ONCE when it becomes the CENTRED card; cards 4/5/6 then
   loop their ambient motion forever. Mobile (<=1024px): the pin/track
   stack vertically (no pin, no transform) and each scene builds on enter
   (IntersectionObserver, viewport root). Per-card scene mock-UI is
   inline-styled (the design handoff's pixel spec).
   ----------------------------------------------------------------*/

.ch-05 {
  position: relative;
  height: 620vh;
  height: 620svh;            /* runway: 6-card sideways scrub (header rides the track now) */
  background: var(--bone);
}
/* Sticky pin: holds the card track centred in the viewport while the page's
   vertical scroll is converted to horizontal track travel by applyCh05Pin.
   The chapter header now lives INSIDE the pin (absolute overlay), so the pin
   sticks from the section top (no normal-flow header above it). */
.ch-05-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Chapter header: section stamp + lede. ABSOLUTE overlay INSIDE the pin,
   centred horizontally so at scrub progress 0 it sits dead-centre over card 1
   (card 1 is itself centred in the viewport at prog 0). applyCh05Pin then
   rides it left in lockstep with card 1 (it gets card 1's own displacement as
   a translate) so the title slides off-screen WITH the card track as the scrub
   advances - it is NOT a fixed header. It sits in the upper band of the pin,
   clear of the cards' vertical centre. Reduced bone above (was a 22vh
   normal-flow padding-top). */
.ch-05-header {
  position: absolute;
  /* Anchor the title block JUST ABOVE the vertically-centred card track. The
     card is height 644px centred in the 100svh pin, so its top edge is at
     (100svh - 644px)/2; sit the title block above that with a small gap. This
     keeps the title clear of the card surface (no overlap) at any viewport
     height, and clamps so it never rides off the top of short viewports. */
  top: max(3vh, calc((100vh - 644px) / 2 - 96px));
  top: max(3vh, calc((100svh - 644px) / 2 - 96px));
  left: 50%;
  width: min(1100px, 88vw);
  margin-left: calc(min(1100px, 88vw) / -2);
  z-index: 4;
  text-align: center;
  pointer-events: none;
  will-change: transform, opacity;
}
.ch-05-header .mono {
  font-family: var(--font-mono);
  font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp);
  text-transform: uppercase;
  color: var(--stone);
  margin-bottom: 12px;
}
.ch-05-header .mono .period { color: var(--clay); margin: 0 4px; }
.ch-05-lede {
  margin: 0 auto;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(30px, 3.4vw, 46px);
  line-height: 1.02;
  letter-spacing: -0.018em;
  color: var(--ink);
  white-space: nowrap;       /* one line so it tucks into the band above the card */
  max-width: none;
}
.ch-05-lede .period { color: var(--clay); }
html.js .ch-05-header {
  opacity: 0;
  transition: opacity 620ms var(--ease-standard);
}
html.js .ch-05-header.is-in { opacity: 1; }

/* The track-wrap clips the horizontal overflow of the pinned track. The page
   scrolls vertically; applyCh05Pin translates .ch-05-track sideways so exactly
   one card sits centred. No native horizontal scroller, no CSS scroll-snap -
   the settle-snap is JS (applyCh05Pin) on the page's vertical scroll. */
.ch-05-track-wrap {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.ch-05-track {
  display: flex;
  align-items: center;
  gap: 6vw;
  /* Side padding = half the viewport minus half a card so card 1 and card N
     can sit dead-centre at the ends of the scrub. */
  padding: 0 max(6vw, calc(50vw - min(550px, 44vw)));
  will-change: transform;
}
.ch-05-card {
  position: relative;
  flex: 0 0 auto;
  width: min(1100px, 88vw);
  /* Uniform card height: the inline markup carries min-height:620px, but
     card 2 (Mobile Apps: 4 headline lines + body + COMING SOON + 48px store
     badges) overflowed to ~635px. Lock all six to one fixed height so the
     track reads as a clean uniform row; it comfortably fits card 2's content.
     (Overrides the inline min-height by setting the height outright.) */
  height: 644px;
}

/* Headline clip-reveal: each line is an outer overflow:hidden span wrapping
   an inner [data-hl] that pass3.js translates up into view. The line-box had
   no descender room, so a final-line "g"/"y" (Card04 "unglamorous",
   Card05 "Systems", Card06 "physical"/"design") clipped at the bottom edge.
   Give the clip boxes a touch more line-height plus a small bottom pad so
   descenders sit inside the box; the JS hides the inner with translateY 122%
   (> box height incl. pad) so the reveal still starts fully concealed. */
.ch-05-card h2 > span { line-height: 1.12; padding-bottom: 0.06em; }
.ch-05-card h2 > span > [data-hl] { line-height: 1.12; }

/* Mobile / tablet: stack the cards vertically, no pin, no horizontal scrub.
   Matches mobile.css's (max-width:1023px),(pointer:coarse) Ch5 overrides and
   the desktopPin layout switch in pass3.js (applyCh05Pin early-returns here,
   so it never writes a transform; the !important reset clears any stale
   inline transform left from a desktop->mobile resize). */
@media (max-width: 1023px), (pointer: coarse) {
  .ch-05 {
    height: auto;
    padding: 12vh 0 16vh;
  }
  .ch-05-pin {
    position: relative;
    top: auto;
    height: auto;
    overflow: visible;
    justify-content: flex-start;
  }
  .ch-05-track-wrap {
    overflow: visible;
    display: block;
  }
  .ch-05-track {
    flex-direction: column;
    align-items: stretch;
    gap: 8vh;
    padding: 0 24px;
    transform: none !important;
    will-change: auto;
  }
  .ch-05-card {
    width: 100%;
    height: auto;        /* mobile stack: let each card size to its content */
  }
  /* Mobile: the pin is off, so the header returns to a normal-flow block at
     the top of the stacked cards (the desktop absolute-overlay ride is inert
     here; applyCh05Pin early-returns and never writes its transform). */
  .ch-05-header {
    position: relative;
    top: auto;
    left: auto;
    width: auto;
    margin-left: 0;
    margin-bottom: clamp(28px, 6vh, 48px);
    padding: 0 24px;
    text-align: left;
    pointer-events: auto;
    transform: none !important;
  }
  .ch-05-lede { margin-left: 0; max-width: 22ch; white-space: normal; }
}

/* ----------------------------------------------------------------
   Chapter 07 · Theta.  Bone surface, restrained.
   Headline left, mocked WhatsApp-style card right (slides in via M09).
   ----------------------------------------------------------------*/

.ch-07 {
  position: relative;
  background: var(--bone);
  padding: 16vh 0 16vh;
  min-height: 100vh;
  overflow: hidden;
}
.ch-07-inner {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 80px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
}
.ch-07-meta {
  display: flex; flex-direction: column; gap: 28px;
}
.ch-07-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;
}
.ch-07-stamp .label { color: var(--ink); }
.ch-07-stamp .period { color: var(--clay); }
.ch-07-stamp .product { color: var(--ink); font-weight: 500; }
.ch-07-stamp .status { color: var(--clay); }

/* Product wordmark - the hero that names Theta unmistakably.  Standalone
   mark, so the Clay period stays. */
.ch-07-wordmark {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(64px, 8vw, 124px);
  line-height: 0.95; letter-spacing: -0.03em;
  color: var(--ink); margin: 0;
}
.ch-07-wordmark .period { color: var(--clay); }

.ch-07-head {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(48px, 6vw, 96px);
  line-height: 1.02; letter-spacing: -0.02em;
  color: var(--ink); margin: 0;
  position: relative;
  max-width: none;
}
.ch-07-head em { font-style: italic; color: var(--harbor); font-weight: 400; }
.ch-07-head .period { color: var(--clay); }
.ch-07-head .carbon {
  position: absolute; inset: 0;
  color: rgba(168, 156, 138, 0.42);
  z-index: -1;
  transform: translate(2px, 2px);
  pointer-events: none;
  will-change: transform;
}
.ch-07-head .carbon em { color: rgba(31, 77, 74, 0.32); }
.ch-07-head .carbon .period { color: rgba(181, 87, 58, 0.32); }

.ch-07-body {
  font-family: var(--font-body); font-size: 17px; line-height: 1.6;
  color: var(--ink);
  display: flex; flex-direction: column; gap: 14px;
  max-width: 46ch;
}
.ch-07-body p { margin: 0; }
.ch-07-body em {
  font-family: var(--font-display); font-style: italic;
  color: var(--harbor); font-weight: 400;
}

.ch-07-channels {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
}
.ch-07-channels .period { color: var(--clay); margin: 0 6px; }

.ch-07-cta {
  display: inline-flex; align-self: flex-start;
  align-items: center; gap: 1px;
  background: var(--clay); color: var(--bone);
  font-family: var(--font-body); font-weight: 500; font-size: 15px;
  padding: 14px 24px; border-radius: var(--radius-md);
  border: none; cursor: pointer;
  text-decoration: none;
  border-bottom: none;
  transition: background 40ms var(--ease-standard);
  will-change: transform;
  margin-top: 8px;
}
.ch-07-cta:hover { background: var(--clay-hover); color: var(--bone); }
.ch-07-cta .period { color: var(--bone); }

/* Theta chat-card mock - Bone-100 surface, hairline border, two
   message bubbles.  Slides in from the right via M09. */
.ch-07-card {
  position: relative;
  background: var(--bone-100);
  border: 1px solid var(--bone-400);
  border-radius: var(--radius-lg);
  padding: 28px;
  display: flex; flex-direction: column; gap: 16px;
  transform: translateX(80vw);
  opacity: 0;
  will-change: transform, opacity;
  box-shadow: var(--shadow-sm);
}
.ch-07-card[data-in="true"] {
  transform: translateX(0);
  opacity: 1;
}
/* On product sub-pages the card is in the viewport from frame 0
   (no scroll needed to reveal it), so start at the resolved state.
   M09 tween becomes a no-op, which is correct: the card simply
   appears rather than sliding in from offscreen. */
[data-subpage] .ch-07-card {
  transform: none;
  opacity: 1;
}
.ch-07-card-head {
  display: flex; align-items: center; justify-content: space-between;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--bone-400);
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
}
.ch-07-card-head .channel { color: var(--ink); font-weight: 500; }
.ch-07-card-head .period { color: var(--clay); }
.ch-07-card-head .meta { color: var(--stone); }
.ch-07-bubbles {
  display: flex; flex-direction: column; gap: 12px;
}
.ch-07-bubble {
  font-family: var(--font-body); font-size: 15px; line-height: 1.5;
  padding: 12px 16px;
  border-radius: 14px;
  max-width: 78%;
  position: relative;
}
.ch-07-bubble[data-from="customer"] {
  align-self: flex-start;
  background: var(--bone);
  border: 1px solid var(--bone-400);
  color: var(--ink);
  border-top-left-radius: 4px;
}
.ch-07-bubble[data-from="theta"] {
  align-self: flex-end;
  background: var(--ink);
  color: var(--bone);
  border-top-right-radius: 4px;
  padding-right: 22px;
}
.ch-07-bubble[data-from="theta"]::after {
  content: ".";
  position: absolute;
  top: 6px; right: 8px;
  font-family: var(--font-display);
  color: var(--clay);
  font-size: 22px; line-height: 0;
}
.ch-07-card-foot {
  display: flex; align-items: baseline; justify-content: space-between;
  padding-top: 14px;
  border-top: 1px solid var(--bone-400);
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
}
.ch-07-card-foot .by { color: var(--ink); }
.ch-07-card-foot .period { color: var(--clay); }
.ch-07-card-foot .rt { color: var(--ink); font-variant-numeric: tabular-nums; }

/* ----------------------------------------------------------------
   Chapter 08 · Two continents - Ink surface.
   Arc from Toronto (Clay) to Cape Town (Harbor) on a flat 2D
   equirectangular projection.  Bone-300 dot grid runs reverse-
   parallax behind.  Two columns of body beneath the arc.
   ----------------------------------------------------------------*/

.ch-08 {
  position: relative;
  background: var(--ink);
  color: var(--bone);
  padding: 18vh 0 16vh;
  overflow: hidden;
  min-height: 130vh;
  min-height: 130svh;
}
/* Dot-grid background - radial-gradient repeat.  M17 reverse-
   parallax driven from site.js sets background-position-y. */
.ch-08-grid {
  position: absolute; inset: -20vh 0 -20vh 0;
  background-image: radial-gradient(circle at 1px 1px, rgba(213, 200, 168, 0.18) 1px, transparent 1px);
  background-size: 36px 36px;
  will-change: transform;
  pointer-events: none;
}
/* Fade the dot grid into the Ink surface at the section's top & bottom edges
   instead of a hard overflow clip.  Anchored to the section (not the reverse-
   parallax grid, which drifts up to 80px), painted above the grid (z auto) and
   below the z-index:2 content - so only the dots fade, the arc/text stay crisp. */
.ch-08::before,
.ch-08::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  height: clamp(96px, 24vh, 260px);
  z-index: 1;
  pointer-events: none;
}
.ch-08::before { top: 0;    background: linear-gradient(to bottom, var(--ink), transparent); }
.ch-08::after  { bottom: 0; background: linear-gradient(to top,    var(--ink), transparent); }
.ch-08-inner {
  position: relative;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 80px;
  display: flex; flex-direction: column; gap: 80px;
  z-index: 2;
}
.ch-08-stamp {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stamp-fg);
}
/* ch-08 lives on an Ink surface - stamp text needs to be readable on dark. */
[data-surface="ink"] .ch-08-stamp { color: var(--fg-quiet-on-ink); }
.ch-08-stamp .period { color: var(--clay); margin: 0 6px; }

.ch-08-head {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(44px, 5.8vw, 92px);
  line-height: 1.02; letter-spacing: -0.02em;
  color: var(--bone); margin: 0;
  text-wrap: balance;
  max-width: 22ch;
}
.ch-08-head em { font-style: italic; color: var(--fg-cool-on-ink); font-weight: 400; }
.ch-08-head .period { color: var(--clay); }

/* Arc panel - position context for the fixed-size HTML city dots.
   SVG + inner element styles live in site.css (.synqa-map-*). */
.ch-08-arc-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 1280 / 460;
}

.ch-08-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 80px;
}
.ch-08-col {
  display: flex; flex-direction: column; gap: 16px;
  opacity: 0; transform: translateY(20px);
  will-change: opacity, transform;
}
.ch-08-col.is-in {
  opacity: 1; transform: translateY(0);
  transition: opacity 600ms var(--ease-out), transform 600ms var(--ease-out);
}
.ch-08-col-stamp {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
}
.ch-08-col-stamp.toronto { color: var(--clay); }
.ch-08-col-stamp.capetown { color: var(--fg-cool-on-ink); }
.ch-08-col-stamp .period { color: var(--clay); }
.ch-08-col-head {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(28px, 2.6vw, 36px);
  color: var(--bone); margin: 0;
  letter-spacing: -0.01em;
}
.ch-08-col-body {
  font-family: var(--font-body); font-size: 16px; line-height: 1.6;
  color: var(--fg-quiet-on-ink); margin: 0;
  max-width: 44ch;
}
.ch-08-col-body em {
  font-family: var(--font-display); font-style: italic;
  color: var(--fg-cool-on-ink); font-weight: 400;
}

/* ----------------------------------------------------------------
   Chapter 09 · The close - Bone surface, restrained.
   Headline + contact form.  Only ambient motion is M14 magnetic on
   the submit button.
   ----------------------------------------------------------------*/

.ch-09 {
  position: relative;
  background: var(--bone);
  padding: 22vh 0 18vh;
  min-height: 100vh;
  min-height: 100svh;
}
.ch-09-inner {
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 80px;
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: 96px;
  align-items: start;
}
.ch-09-stamp {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
  margin-bottom: 28px;
}
.ch-09-stamp .period { color: var(--clay); margin: 0 6px; }

.ch-09-head {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(48px, 6vw, 96px);
  line-height: 1.02; letter-spacing: -0.022em;
  color: var(--ink); margin: 0;
  text-wrap: balance;
  position: relative;
  max-width: 16ch;
}
.ch-09-head em { font-style: italic; color: var(--harbor); font-weight: 400; }
.ch-09-head .period { color: var(--clay); }
.ch-09-head .carbon {
  position: absolute; inset: 0;
  color: rgba(168, 156, 138, 0.42);
  z-index: -1;
  transform: translate(2px, 2px);
  pointer-events: none;
  will-change: transform;
}
.ch-09-head .carbon em { color: rgba(31, 77, 74, 0.32); }
.ch-09-head .carbon .period { color: rgba(181, 87, 58, 0.32); }

.ch-09-body {
  margin-top: 28px;
  font-family: var(--font-body); font-size: 17px; line-height: 1.6;
  color: var(--ink);
  max-width: 44ch;
}
.ch-09-side {
  display: flex; flex-direction: column; gap: 6px;
  margin-top: 18px;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
}
.ch-09-side a {
  color: var(--ink);
  border-bottom: 1px solid var(--bone-400);
  padding-bottom: 1px;
  transition: color 120ms var(--ease-standard), border-color 120ms var(--ease-standard);
}
.ch-09-side a:hover { color: var(--clay); border-color: var(--clay); }
.ch-09-side .period { color: var(--clay); margin: 0 4px; }

.ch-09-form {
  display: flex; flex-direction: column;
  gap: 20px;
  background: var(--bone-100);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 40px;
  box-shadow: var(--shadow-sm);
}
.ch-09-form-row {
  display: flex; flex-direction: column; gap: 8px;
}
.ch-09-form label {
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--ink);
  font-weight: 500;
}
.ch-09-form label .period { color: var(--clay); }
.ch-09-form input,
.ch-09-form textarea {
  font-family: var(--font-body); font-size: 16px;
  color: var(--ink);
  background: var(--bone);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  min-height: 46px;
  outline: none;
  resize: none;
  transition: border-color var(--dur-fast) var(--ease-standard);
}
.ch-09-form input::placeholder,
.ch-09-form textarea::placeholder { color: var(--stone); }
.ch-09-form input:hover,
.ch-09-form textarea:hover { border-color: var(--ink); }
/* R-15: plain :focus in its OWN rule block (not grouped with :focus-visible)
   so an old parser that drops an unknown :focus-visible selector still keeps
   this whole list intact and the inputs keep a visible focus ring. */
.ch-09-form input:focus,
.ch-09-form textarea:focus {
  border-color: var(--clay);
  outline: 2px solid var(--clay);
  outline-offset: 2px;
}
.ch-09-form input:focus-visible,
.ch-09-form textarea:focus-visible {
  border-color: var(--clay);
  outline: 2px solid var(--clay);
  outline-offset: 2px;
}
.ch-09-form textarea { min-height: 132px; line-height: 1.5; padding-top: 12px; }
.ch-09-form-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; flex-wrap: wrap;
  margin-top: 6px;
  font-family: var(--font-mono); font-size: var(--fs-stamp);
  letter-spacing: var(--tracking-stamp); text-transform: uppercase;
  color: var(--stone);
}
.ch-09-form-foot .period { color: var(--clay); }
.ch-09-submit {
  display: inline-flex; align-items: center; gap: 1px;
  background: var(--clay); color: var(--bone);
  font-family: var(--font-display); font-weight: 500;
  font-size: 17px; letter-spacing: -0.005em;
  padding: 16px 32px; border-radius: var(--radius-md);
  border: none; cursor: pointer;
  text-decoration: none;
  min-height: 48px;
  transition: background var(--dur-fast) var(--ease-standard);
  will-change: transform;
}
.ch-09-submit:hover { background: var(--clay-hover); color: var(--bone); }
/* R-15: separate plain :focus block so this CTA keeps a visible focus ring
   on old parsers that drop the :focus-visible rule below entirely. */
.ch-09-submit:focus {
  outline: 2px solid var(--clay);
  outline-offset: 2px;
}
.ch-09-submit:focus-visible {
  outline: 2px solid var(--clay);
  outline-offset: 2px;
}
.ch-09-submit .period { color: var(--bone); }

/* ----------------------------------------------------------------
   Responsive - Ch 7 hero collapse at tablet and below.
   At 1023px and under, the two-column grid becomes a single column
   so the slot headline reads full-width above the card.
   ----------------------------------------------------------------*/

@media (max-width: 1023px) {
  .ch-07-inner {
    grid-template-columns: 1fr;
    gap: 48px;
    padding: 0 32px;
  }
  .ch-07-head {
    font-size: clamp(40px, 8vw, 72px);
  }
}

@media (max-width: 767px) {
  .ch-07-head {
    font-size: clamp(36px, 10vw, 56px);
  }
  .ch-07-inner {
    padding: 0 24px;
    gap: 36px;
  }
}

