/* sections-below.css — sticky-chapter rebuild (v9).
 *
 * One pattern repeated per chapter:
 *   outer .sticky-chapter (200vh tall, gives scroll room)
 *     └── .sticky-chapter-inner (position: sticky; top: 0; height: 100vh — the SCENE)
 *           └── label + chapter-specific contents
 *
 * The whitepaper card lives at the page-root, always position: fixed,
 * visibility toggled by JS (opacity) as the user enters/exits the chapter region.
 * Comparison table + footer follow in normal flow after chapter 03.
 */

/* ─── Page-level below-hero container ───────────────────────────────── */
.below {
  background: var(--bg);
  color: var(--fg);
  position: relative;
}

/* ─── Sticky chapter pattern ────────────────────────────────────────── */
/* Heights are set inline per chapter (height + sticky-inner height) so
   each chapter can size its scroll region to its actual content. */
.sticky-chapter {
  position: relative;
  z-index: 10;
}

.sticky-chapter-inner {
  position: sticky;
  top: 0;
  width: 100%;
  overflow: visible;
  display: flex;
  flex-direction: column;
}

.sticky-chapter-label {
  position: absolute;
  top: 28px;
  left: 40px;
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  z-index: 2;
}
.sticky-chapter-label::before {
  content: "";
  width: 7px;
  height: 7px;
  background: var(--fg);
  border-radius: 50%;
  display: inline-block;
}

/* ─── Chapter 02 specifics ──────────────────────────────────────────── */
/* ─── Chapter 02 — normal flow (no sticky pin) ───────────────────── */
.chapter-02 {
  position: relative;
  padding: 8vh 0 2vh;
  z-index: 10;
}

.chapter-02-label {
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 12px;
  /* Align with the start of the grid content below (headline + cards),
     which sits inside a 1400px / 6vw padded centered container. */
  max-width: 1400px;
  margin: 0 auto 4vh;
  padding: 0 6vw;
  width: 100%;
  box-sizing: border-box;
}
.chapter-02-label::before {
  display: none;
}

.problem-marquee {
  position: relative;
  height: 38vh;
  width: 100%;
  margin-bottom: 2vh;
  /* Clip the 3D-transformed marquee text so it cannot spill out the
     top edge into the sticky nav / section above. `overflow: clip`
     beats `hidden` here because preserve-3d children can otherwise
     escape `overflow: hidden` in some browsers. */
  overflow: clip;
  /* Mask the top + bottom edges so any text that approaches the
     boundaries fades out smoothly instead of getting hard-clipped. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 12%, #000 88%, transparent 100%);
}

.problem-content {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4vh;
  width: 100%;
  box-sizing: border-box;
}

.problem-tagline {
  margin: 0;
  padding: 0;
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(28px, 3.5vw, 56px);
  line-height: 1.05;
  letter-spacing: -0.035em;
  color: var(--fg);
  /* allow the headline to wrap inside its column — nowrap was causing
     overflow into the whitepaper card on narrower viewports */
  text-wrap: balance;
}

.problem-card {
  width: 100%;
  border: 2px solid #D4D4D8;
  border-radius: 16px;
  background: #FAFAFA;
  box-shadow:
    0 20px 60px -10px rgba(0, 0, 0, 0.15),
    0 8px 20px -4px rgba(0, 0, 0, 0.08);
  overflow: hidden;
}
:root.dark .problem-card {
  border: 1px solid rgba(255, 255, 255, 0.20);
  background: #0F0F0F;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    0 24px 60px -10px rgba(0, 0, 0, 0.55),
    0 8px 24px -4px rgba(0, 0, 0, 0.40);
}

.problem-card-header {
  padding: 18px 36px;
  border-bottom: 1px solid #D4D4D8;
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  display: flex;
  align-items: center;
  gap: 12px;
}
:root.dark .problem-card-header {
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}
.problem-card-body {
  padding: 32px 36px 36px;
}
.problem-card-body p {
  margin: 0 0 1.4em 0;
  font-family: var(--sans);
  font-weight: 400;
  font-size: 17px;
  line-height: 1.65;
  color: var(--fg);
}
.problem-card-body p:last-child { margin-bottom: 0; }

/* ─── Testimonial section ─────────────────────────────────────────── */
.testimonial-section {
  /* break out of any clamped parent to span the full viewport width */
  position: relative;
  width: 100vw;
  left: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  margin-top: 14vh;
  margin-bottom: 12vh;
  box-sizing: border-box;
  font-family: var(--sans);
}

.testimonial-header {
  margin: 0 6vw 4vh;
}

.testimonial-hint {
  margin-top: 18px;
  margin-left: 6vw;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.testimonial-title {
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(24px, 3vw, 44px);
  line-height: 1.05;
  letter-spacing: -0.03em;
  color: var(--fg);
  margin: 0 0 6px 0;
}
.testimonial-sub {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 15px;
  color: var(--fg-muted);
  margin: 0;
}

/* ─── Marquee primitive ────────────────────────────────────────────── */
/* Plain-CSS port of the Tailwind Marquee component. JSX renders N tracks
   that each hold the same children; the keyframe slides one track-width
   so the loop seams cleanly. Horizontal + vertical variants. */
.marquee {
  --marquee-duration: 40s;
  --marquee-gap: 1rem;
  display: flex;
  overflow: hidden;
  gap: var(--marquee-gap);
  padding: 8px;
}
.marquee.marquee--vertical {
  flex-direction: column;
}

.marquee-track {
  display: flex;
  gap: var(--marquee-gap);
  flex-shrink: 0;
  justify-content: space-around;
  animation: marquee var(--marquee-duration) linear infinite;
  will-change: transform;
}
.marquee.marquee--vertical .marquee-track {
  flex-direction: column;
  animation-name: marquee-vertical;
}
.marquee.marquee--reverse .marquee-track {
  animation-direction: reverse;
}
.marquee.marquee--pause-on-hover:hover .marquee-track {
  animation-play-state: paused;
}

@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(calc(-100% - var(--marquee-gap))); }
}
@keyframes marquee-vertical {
  from { transform: translateY(0); }
  to { transform: translateY(calc(-100% - var(--marquee-gap))); }
}

/* ── Testimonial section ── (uses .marquee primitive, full-bleed) */
.testimonial-section {
  margin: 14vh 0 12vh 0;
  /* break out of any clamped parent to span the full viewport width */
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  box-sizing: border-box;
  font-family: var(--sans);
}

.testimonial-header {
  margin: 0 6vw 4vh;
}

.testimonial-hint {
  margin: 18px 6vw 0;
}

.testimonial-grid {
  /* legacy hook — replaced by .marquee */
}

.testimonial-card {
  position: relative;
  border-radius: 12px;
  border: 1px solid #E4E4E7;
  background: #FAFAFA;
  padding: 24px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 14px;
  overflow: hidden;
  /* fixed width inside the marquee track so cards don't squish */
  width: 320px;
  flex-shrink: 0;
  box-sizing: border-box;
  transition:
    background 360ms ease,
    border-color 360ms ease;
}
:root.dark .testimonial-card {
  border-color: rgba(255, 255, 255, 0.10);
  background: #0F0F0F;
}
.testimonial-card.is-revealed {
  border-color: #FF0022;
  background: rgba(255, 0, 34, 0.16);
}
:root.dark .testimonial-card.is-revealed {
  background: rgba(255, 0, 34, 0.20);
}

/* Red overlay (also driven by the .is-revealed class via background) */

.testimonial-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #A1A1AA;
  color: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
}
:root.dark .testimonial-avatar {
  background: #52525B;
}

.testimonial-stars {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  color: var(--fg);
  font-size: 14px;
  line-height: 1;
}

.testimonial-quote {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.55;
  color: var(--fg);
  margin: 0;
  transition: opacity 360ms ease;
}
.testimonial-card.is-revealed .testimonial-quote {
  opacity: 0.5;
}

.testimonial-divider {
  border: none;
  border-top: 1px solid #E4E4E7;
  margin: 4px 0 0 0;
}
:root.dark .testimonial-divider {
  border-top: 1px solid rgba(255, 255, 255, 0.10);
}

.testimonial-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.testimonial-name {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 13px;
  color: var(--fg);
  /* same dotted-red affordance the HyperText words use */
  text-decoration: underline dotted rgba(255, 0, 34, 0.3);
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.testimonial-card.is-revealed .testimonial-name {
  text-decoration: none;
  /* Default (light mode): dark-red oxblood reads clearly against the
     pink-tinted revealed-card background. Dark-mode override below. */
  color: #7A1820;
  font-family: var(--mono);
  font-weight: 600;
  letter-spacing: 0.04em;
}
:root.dark .testimonial-card.is-revealed .testimonial-name {
  color: #FFFFFF;
}
.testimonial-role {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 12px;
  color: var(--fg-muted);
}
.testimonial-card.is-revealed .testimonial-role {
  color: rgba(122, 24, 32, 0.85);
  font-family: var(--mono);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
:root.dark .testimonial-card.is-revealed .testimonial-role {
  color: rgba(255, 255, 255, 0.85);
}

/* Hover-end transition for scramble back */

/* Status tag in the top-right corner — schematic/terminal aesthetic
   (no rotation, no postal-stamp affect). Content varies per card
   (AI-GEN, PAID, BOT, INTERNAL, INCENTIVIZED, RECIPROCAL, COPY-PASTE,
   UNVERIFIED, …) and is wrapped in square brackets via pseudo-
   elements so the JSX stays unchanged. A subtle red/cyan text-shadow
   matches the GlitchText chromatic aberration used in the quote
   reveal, tying the stamp into the rest of the hacker/glitch
   vocabulary on the page. */
.testimonial-stamp {
  position: absolute;
  top: 16px;
  right: 14px;
  font-family: var(--mono);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #FF0022;
  text-shadow:
    -1px 0 rgba(0, 229, 255, 0.55),
     1px 0 rgba(255, 0, 60, 0.45);
  white-space: nowrap;
  transform: none;
  transform-origin: 100% 50%;
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease;
}
.testimonial-stamp::before {
  content: "[ ";
  opacity: 0.55;
}
.testimonial-stamp::after {
  content: " ]";
  opacity: 0.55;
}
.testimonial-card.is-revealed .testimonial-stamp {
  opacity: 1;
  transform: none;
}

.testimonial-hint {
  margin-top: 18px;
  margin-left: 6vw;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* ─── HyperText scramble-on-hover ────────────────────────────────── */
/* Theme-adaptive tokens for the hover pill background + text inversion.
   Pill is red in both modes (alert signal) — white text on top. */
:root {
  --hyper-default: #0A0A0A;
  --hyper-hover-text: #FFFFFF;
  --hyper-hover-pill: #FF0022;
}
:root.dark {
  --hyper-default: #FAFAFA;
  --hyper-hover-text: #FFFFFF;
  --hyper-hover-pill: #FF0022;
}

.hyper-paragraph {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 17px;
  line-height: 1.65;
  color: var(--hyper-default);
}
.hyper-paragraph + .hyper-paragraph {
  margin-top: 1.5em;
}

.hyper-word {
  position: relative;
  display: inline-block;
  white-space: nowrap;
  cursor: default;
  transition:
    opacity 200ms ease,
    filter 200ms ease,
    transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1);
  z-index: 1;
}
.hyper-word.is-highlightable { cursor: pointer; }
.hyper-word.is-dimmed {
  /* System-distortion recede — tight RGB split + heavy blur so the
     glyphs collapse into an unreadable corrupted-signal smear instead
     of cleanly doubled, legible letters. */
  opacity: 0.55;
  color: transparent;
  text-shadow:
    -1.5px 0 0 rgba(255, 0, 60, 0.55),
     1.5px 0 0 rgba(0, 229, 255, 0.45),
     0 0 10px rgba(255, 0, 60, 0.20);
  filter: blur(3.5px);
}
.hyper-word.is-dimmed .hyper-text {
  color: transparent;
}
.hyper-word.is-hovered {
  /* Scale only; baseline-anchored so the word grows UPWARD instead
     of lifting away from the cursor. The earlier `translateY(-5px)`
     pulled the bottom of the word above the cursor when hovering
     near the word's lower edge, which triggered an immediate
     mouseleave → unscale → mouseenter flicker loop. Pinning the
     transform-origin to `center bottom` keeps the bottom edge
     stationary so the hit region under the cursor never moves. */
  transform: scale(1.18);
  transform-origin: center bottom;
  z-index: 20;
}

/* ── Focus recede on hyper-word hover ──────────────────────────────
   When any trigger word is hovered, the surrounding paragraphs and
   the right-column whitepaper card recede via a chromatic-aberration
   "system distortion" — red/cyan channel split plus a sliver of
   blur — to match the GlitchText vocabulary in the testimonial
   reveal. Original opacity-only blur read as soft white fog;
   this reads as a corrupted signal. Scope is bounded to a handful
   of specific neighbours so the `:has()` evaluation cost is cheap. */
.problem-card-body:has(.hyper-word.is-hovered)
  .hyper-paragraph:not(:has(.hyper-word.is-hovered)),
.ch02-grid:has(.hyper-word.is-hovered) .problem-tagline,
.ch02-grid:has(.hyper-word.is-hovered) .ch02-grid-right {
  opacity: 0.45;
  color: transparent;
  text-shadow:
    -1.5px 0 0 rgba(255, 0, 60, 0.50),
     1.5px 0 0 rgba(0, 229, 255, 0.40),
     0 0 10px rgba(255, 0, 60, 0.18);
  filter: blur(3.5px);
  transition: opacity 220ms ease, filter 220ms ease, text-shadow 220ms ease;
}
/* Force inner text spans to inherit the transparent color so their
   own explicit `color: var(--…)` doesn't override the parent. */
.problem-card-body:has(.hyper-word.is-hovered)
  .hyper-paragraph:not(:has(.hyper-word.is-hovered)) *,
.ch02-grid:has(.hyper-word.is-hovered) .problem-tagline *,
.ch02-grid:has(.hyper-word.is-hovered) .ch02-grid-right * {
  color: transparent !important;
  text-shadow: inherit;
}

.hyper-def {
  /* Position is set inline (position:fixed + top/left) by HyperDef's
     React-portal mount; `left` is the word's horizontal center and
     translateX(-50%) centers the caption on that point. Color is
     the strong foreground token so the line reads cleanly even when
     it overlaps the dimmed paragraph text below the word. */
  transform: translate(-50%, 0);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  line-height: 1.3;
  color: var(--fg);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  animation: hyperDefIn 220ms ease forwards 60ms;
  z-index: 1000;
}
@keyframes hyperDefIn {
  from { opacity: 0; transform: translate(-50%, -4px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

.hyper-pill {
  position: absolute;
  inset: -8px;
  border-radius: 10px;
  background: var(--hyper-hover-pill);
  box-shadow:
    0 10px 25px -5px rgba(255, 0, 34, 0.50),
    0 8px 10px -6px rgba(0, 0, 0, 0.20);
  z-index: -1;
  pointer-events: none;
  animation: hyperPillIn 180ms cubic-bezier(0.4, 0, 0.2, 1) both;
}
@keyframes hyperPillIn {
  from { opacity: 0; transform: scale(0.8); }
  to   { opacity: 1; transform: scale(1); }
}

.hyper-text {
  position: relative;
  z-index: 10;
  /* Tighter inline padding so short words ("on", "the", "is", "a") don't
     feel marooned in oversized gaps. The hover pill uses absolute
     positioning with inset: -8px and is independent of this padding. */
  padding: 0 1px;
  color: var(--hyper-default);
  transition: color 180ms ease, font-family 0ms;
}
.hyper-word.is-hovered .hyper-text {
  color: var(--hyper-hover-text);
  font-family: var(--mono);
  font-weight: 500;
}

.hyper-dot {
  position: absolute;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  z-index: 20;
  pointer-events: none;
  animation: hyperDotIn 220ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.hyper-dot-mint { top: -4px; right: -4px; background: #FF0022; }
.hyper-dot-lav  { bottom: -4px; left: -4px; background: #FF0022; }
@keyframes hyperDotIn {
  from { transform: scale(0); }
  to   { transform: scale(1); }
}

.hyper-space {
  display: inline-block;
  white-space: pre;
}

/* ─── Whitepaper card ───────────────────────────────────────────────── */
/* ─── Chapter 02 two-column grid (left = content, right = whitepaper) ── */
.ch02-grid {
  display: grid;
  grid-template-columns: minmax(0, 58fr) minmax(0, 42fr);
  gap: 3vw;
  align-items: stretch;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 6vw;
  box-sizing: border-box;
}

.ch02-grid-left {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4vh;
}

.ch02-grid-right {
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

/* Static whitepaper card — stretches to match the left column's height
   (grid uses align-items: stretch). Body scrolls internally. */
.wp-card-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  opacity: 1;
  pointer-events: auto;
}

.wp-card-wrap.is-fixed { /* legacy hook, no-op now */ }

/* Override any earlier fixed-position rules */
.wp-card-wrap {
  top: auto !important;
  right: auto !important;
  transform: none !important;
  z-index: auto !important;
}

.wp-card {
  flex: 1 1 0;
  min-height: 0;
  border: 2px solid #D4D4D8;
  border-radius: 16px;
  background: #FAFAFA;
  box-shadow:
    0 20px 60px -10px rgba(0, 0, 0, 0.15),
    0 8px 20px -4px rgba(0, 0, 0, 0.08);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
:root.dark .wp-card {
  border: 1px solid rgba(255, 255, 255, 0.20);
  background: #0F0F0F;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    0 24px 60px -10px rgba(0, 0, 0, 0.55),
    0 8px 24px -4px rgba(0, 0, 0, 0.40);
}

.wp-card-header {
  padding: 18px 22px;
  border-bottom: 1px solid #D4D4D8;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  display: flex;
  align-items: center;
  gap: 10px;
}
:root.dark .wp-card-header {
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}

.wp-card-body-wrap {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
  /* Progressive-blur mask: fade the top and bottom edges of the scroll
     region so chapter text dissolves instead of cutting hard against the
     card border. The mask is applied to the wrap (clipping container) so
     the fade edges stay fixed while the body scrolls beneath. */
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 56px,
    #000 calc(100% - 56px),
    transparent 100%
  );
          mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 56px,
    #000 calc(100% - 56px),
    transparent 100%
  );
}

.wp-card-body {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  padding: 28px 32px;
  font-family: var(--sans);
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--fg-secondary);
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
  box-sizing: border-box;
}
.wp-card-body::-webkit-scrollbar { width: 6px; }
.wp-card-body::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 3px;
}
.wp-card-body::-webkit-scrollbar-track { background: transparent; }

/* ── Whitepaper body typography (used by both inline card and modal).
   The renderer emits .wp-h1 / .wp-h2 / .wp-h3 / .wp-line / .wp-list /
   .wp-sep — base styles below; modal overrides further down bump
   sizes slightly for the larger reading container. */
.wp-h1 {
  margin: 0 0 8px 0;
  font-family: var(--sans);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
  color: var(--fg);
}
.wp-h2 {
  margin: 30px 0 12px 0;
  font-family: var(--sans);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.3;
  color: var(--fg);
}
.wp-h3 {
  margin: 22px 0 8px 0;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.35;
  color: var(--fg);
}
/* Subtitle (first H2 following the H1) sits closer to the title. */
.wp-h1 + .wp-h2 {
  margin-top: 4px;
  font-size: 15.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--fg-muted);
}

.wp-line {
  margin: 0 0 14px 0;
}

.wp-list {
  margin: 0 0 16px 0;
  padding: 0 0 0 18px;
  list-style: none;
}
.wp-list li {
  position: relative;
  padding-left: 14px;
  margin-bottom: 6px;
  line-height: 1.65;
}
.wp-list li::before {
  content: "\2013";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--fg-muted);
}
.wp-list li:last-child { margin-bottom: 0; }

.wp-line strong,
.wp-list li strong,
.wp-h1 strong,
.wp-h2 strong,
.wp-h3 strong {
  font-weight: 600;
  color: var(--fg);
}

.wp-sep {
  border: none;
  border-top: 1px solid var(--border);
  margin: 26px 0;
  width: 32%;
}

/* Legacy hook (old renderer) — keep so existing styles don't break. */
.wp-chapter-title {
  margin: 32px 0 14px 0;
  font-family: var(--sans);
  font-size: 16.5px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.3;
  color: var(--fg);
}
.wp-chapter-title:first-child {
  margin-top: 0;
}

/* Progressive blur edges now handled by mask-image on .wp-card-body-wrap. */

/* ── Whitepaper scroll progress bar ───────────────────────────────
   Sits between the header and the masked body-wrap so the top mask
   doesn't fade it out. The fill width is driven by ScrollProgress
   (React) via JS — spring-eased toward scrollTop / (scrollHeight - clientHeight).
   The gradient fades in from transparent on the left so an empty
   bar reads as nothing, and the dark tip emphasizes forward motion. */
.wp-progress {
  position: relative;
  height: 2px;
  width: 100%;
  flex-shrink: 0;
  overflow: hidden;
  background: transparent;
}
.wp-progress-track {
  position: absolute;
  inset: 0;
  background: transparent;
}
:root.dark .wp-progress-track {
  background: #111111;
}
.wp-progress-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0%;
  background: linear-gradient(
    to right,
    rgba(17, 17, 17, 0) 0%,
    #111111 75%,
    #111111 100%
  );
  will-change: width;
}
:root.dark .wp-progress-fill {
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0) 0%,
    #ffffff 75%,
    #ffffff 100%
  );
}

/* ─── Whitepaper modal ─────────────────────────────────────────────
   Centered overlay opened from the hero CTA. Scrim fades + blurs the
   page; modal card scales 0.95 → 1 and fades in. Body scroll is locked
   by JS while open. */
.wp-modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(8, 8, 10, 0);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4vh 4vw;
  box-sizing: border-box;
  opacity: 0;
  transition:
    background 200ms ease,
    backdrop-filter 200ms ease,
    -webkit-backdrop-filter 200ms ease,
    opacity 200ms ease;
}
.wp-modal-scrim.is-visible {
  opacity: 1;
  background: rgba(8, 8, 10, 0.7);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
:root:not(.dark) .wp-modal-scrim.is-visible {
  background: rgba(255, 255, 255, 0.55);
}

.wp-modal {
  width: min(92vw, 1280px);
  height: min(94vh, 1200px);
  background: #FAFAFA;
  border: 2px solid #D4D4D8;
  border-radius: 16px;
  box-shadow:
    0 40px 120px -20px rgba(0, 0, 0, 0.45),
    0 16px 40px -10px rgba(0, 0, 0, 0.25);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 0;
  transform: scale(0.95);
  transition: opacity 300ms ease-out, transform 300ms ease-out;
  position: relative;
}
.wp-modal-scrim.is-visible .wp-modal {
  opacity: 1;
  transform: scale(1);
}
:root.dark .wp-modal {
  background: #0F0F0F;
  border: 1px solid rgba(255, 255, 255, 0.20);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    0 40px 120px -20px rgba(0, 0, 0, 0.75),
    0 16px 40px -10px rgba(0, 0, 0, 0.55);
}

.wp-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 22px;
  border-bottom: 1px solid #D4D4D8;
  flex-shrink: 0;
}
:root.dark .wp-modal-header {
  border-bottom: 1px solid rgba(255, 255, 255, 0.15);
}
.wp-modal-header-label {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.wp-modal-close {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: none;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 22px;
  line-height: 1;
  color: var(--fg-muted);
  padding: 4px 10px;
  border-radius: 6px;
  transition: color 150ms ease, background 150ms ease;
}
.wp-modal-close:hover {
  color: var(--fg);
  background: rgba(0, 0, 0, 0.05);
}
:root.dark .wp-modal-close:hover {
  background: rgba(255, 255, 255, 0.06);
}

/* Header right-side actions group (Download PDF + close button). */
.wp-modal-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.wp-modal-download {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  text-decoration: none;
  padding: 6px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
  white-space: nowrap;
}
.wp-modal-download:hover {
  color: var(--fg);
  border-color: var(--fg);
  background: rgba(0, 0, 0, 0.04);
}
:root.dark .wp-modal-download:hover {
  background: rgba(255, 255, 255, 0.05);
}

/* PDF embed — fills the modal interior below the header. Rendered
   inline via PDF.js (see PdfViewer component) so it works in
   browsers that block PDF iframes. The wrap handles scrolling; the
   inner container stacks page canvases vertically. */
.wp-modal-pdf-wrap {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  background: #1a1a1d;
  overflow: hidden;
}
:root:not(.dark) .wp-modal-pdf-wrap {
  background: #e8e8ea;
}
.wp-pdf-viewer {
  width: 100%;
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  position: relative;
  padding: 20px 0;
  -webkit-overflow-scrolling: touch;
}
.wp-pdf-viewer::-webkit-scrollbar { width: 8px; }
.wp-pdf-viewer::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 4px;
}
.wp-pdf-viewer::-webkit-scrollbar-track { background: transparent; }
.wp-pdf-pages {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}
.wp-pdf-page {
  max-width: calc(100% - 24px);
  height: auto;
  background: white;
  box-shadow: 0 6px 24px -8px rgba(0, 0, 0, 0.55);
  display: block;
}
.wp-pdf-status {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  pointer-events: none;
}
.wp-pdf-status--error { pointer-events: auto; }
.wp-pdf-status--error a {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.wp-modal-body-wrap {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
  /* Same progressive-blur mask as the inline whitepaper card. */
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 56px,
    #000 calc(100% - 56px),
    transparent 100%
  );
          mask-image: linear-gradient(
    to bottom,
    transparent 0,
    #000 56px,
    #000 calc(100% - 56px),
    transparent 100%
  );
}
.wp-modal-body {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  padding: 40px 48px;
  font-family: var(--sans);
  font-size: 14.5px;
  line-height: 1.75;
  color: var(--fg-secondary);
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
  box-sizing: border-box;
}
.wp-modal-body::-webkit-scrollbar { width: 6px; }
.wp-modal-body::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 3px;
}
.wp-modal-body::-webkit-scrollbar-track { background: transparent; }

/* ── Two-column magazine layout (modal only) ──────────────────────
   ONE shared multi-column container holds all body content. Text
   flows top-to-bottom in the left column, then top-to-bottom in the
   right column. Full-width elements (tables, figures, code,
   timelines, references) interrupt the flow via column-span: all
   and the flow resumes below them. */
.wp-modal-body .wp-cols {
  column-count: 2;
  column-gap: 2.5rem;
  column-fill: balance;
  text-align: left;
}
/* Tables, figures, code blocks, timelines, and reference lists span
   both columns. They sit inline in the column flow but break out
   visually to use the full content width. */
.wp-modal-body .wp-cols .wp-table-wrap,
.wp-modal-body .wp-cols .wp-figure,
.wp-modal-body .wp-cols .wp-code,
.wp-modal-body .wp-cols .wp-timeline-wrap,
.wp-modal-body .wp-cols .wp-refs {
  column-span: all;
  -webkit-column-span: all;
  break-inside: avoid;
  margin: 28px 0;
}
/* Everything else stays inside its column. Explicitly set
   column-span: none on headings to defend against UA quirks. */
.wp-modal-body .wp-cols .wp-h2,
.wp-modal-body .wp-cols .wp-h3,
.wp-modal-body .wp-cols .wp-line,
.wp-modal-body .wp-cols .wp-list,
.wp-modal-body .wp-cols .wp-callout {
  column-span: none;
  -webkit-column-span: none;
}
/* Keep a heading glued to the block immediately after it, but allow
   the heading itself to break to a new column if it's about to be
   stranded at the bottom — break-inside ensures the heading + rule
   stay together as one unit. */
.wp-modal-body .wp-cols .wp-h2,
.wp-modal-body .wp-cols .wp-h3 {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
}
/* Drop the big top-margin for headings that land at the top of a
   column — the column boundary already provides separation. */
.wp-modal-body .wp-cols .wp-h2:first-child,
.wp-modal-body .wp-cols .wp-h3:first-child {
  margin-top: 0;
}
/* Lists shouldn't fragment a single bullet across columns. */
.wp-modal-body .wp-cols .wp-list li {
  break-inside: avoid-column;
}
/* Horizontal rules between sections live inside the column flow. */
.wp-modal-body .wp-cols .wp-sep {
  width: 100%;
}
/* Justified text + narrow columns can produce ugly rivers on short
   final lines; pretty wrap helps minimize widows/orphans where
   supported. */
.wp-modal-body .wp-cols .wp-line {
  text-wrap: pretty;
}

/* Modal whitepaper typography — slightly larger than inline card. */
.wp-modal-body .wp-h1 {
  margin: 0 0 8px 0;
  font-size: 36px;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
}
.wp-modal-body .wp-h1 + .wp-h2 {
  margin-top: 6px;
  font-size: 19px;
  font-weight: 500;
  color: var(--fg-muted);
  letter-spacing: -0.008em;
}
.wp-modal-body .wp-h2 {
  margin: 40px 0 14px 0;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.018em;
  line-height: 1.25;
}
.wp-modal-body .wp-h3 {
  margin: 28px 0 10px 0;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.wp-modal-body .wp-line {
  margin: 0 0 16px 0;
}
.wp-modal-body .wp-list {
  margin: 0 0 20px 0;
  padding: 0 0 0 18px;
  list-style: none;
}
.wp-modal-body .wp-list li {
  padding-left: 16px;
  margin-bottom: 8px;
  line-height: 1.65;
}
.wp-modal-body .wp-sep {
  border: none;
  border-top: 1px solid var(--border);
  margin: 32px 0;
  width: 32%;
}

/* Legacy hook (old renderer). */
.wp-modal-body .wp-chapter-title {
  margin: 36px 0 14px 0;
  font-family: var(--sans);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.3;
  color: var(--fg);
}
.wp-modal-body .wp-chapter-title:first-child { margin-top: 0; }

/* ─── Whitepaper structured blocks ────────────────────────────────
   New block types from the structured WhitepaperBody renderer:
   subtitle, tagline, tagline-strong, pill, meta, emphasis, table,
   figure, code, timeline, refs. Tables / figures / code / timelines /
   refs span both columns inside the .wp-cols magazine flow
   (column-span: all). */

/* ── Header block — centered, full-width above the column flow ── */
.wp-header {
  margin: 0 0 8px 0;
  text-align: center;
}
.wp-header .wp-h1 {
  margin: 0 0 6px 0;
  font-family: var(--sans);
  font-weight: 800;
  letter-spacing: -0.025em;
  text-align: center;
}
.wp-modal-body .wp-header .wp-h1 {
  font-size: 56px;
  line-height: 1.05;
  margin: 0 0 10px 0;
}

.wp-subtitle {
  margin: 0 0 18px 0;
  font-family: var(--sans);
  font-weight: 700;
  letter-spacing: -0.012em;
  color: var(--fg);
  text-align: center;
}
.wp-modal-body .wp-subtitle {
  font-size: 26px;
  line-height: 1.2;
  margin: 0 0 22px 0;
}

.wp-tagline {
  margin: 0 0 6px 0;
  font-family: var(--sans);
  font-style: italic;
  color: var(--fg);
  text-align: center;
  text-wrap: balance;
}
.wp-modal-body .wp-tagline {
  font-size: 16px;
  margin: 0 0 8px 0;
}
.wp-tagline--strong {
  font-style: normal;
  font-weight: 700;
}
.wp-modal-body .wp-tagline--strong {
  font-size: 17px;
  margin-bottom: 4px;
}

/* Pill — bordered rectangle around the "[Technical whitepaper…]" line. */
.wp-pill {
  margin: 22px 0 16px 0;
  text-align: center;
}
.wp-pill > span {
  display: inline-block;
  padding: 6px 14px;
  border: 1px solid var(--border-strong);
  border-radius: 2px;
  font-family: var(--sans);
  font-size: 13px;
  color: var(--fg);
  line-height: 1.3;
}
.wp-modal-body .wp-pill > span {
  font-size: 14px;
  padding: 7px 16px;
}

.wp-meta {
  margin: 0 0 4px 0;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--fg-muted);
  text-align: center;
  letter-spacing: 0;
  text-transform: none;
}
.wp-modal-body .wp-meta {
  font-size: 13.5px;
  margin: 0 0 6px 0;
}

/* Emphasis line — sits between the two header HRs. */
.wp-emphasis {
  margin: 18px 0;
  font-family: var(--sans);
  font-weight: 700;
  font-style: italic;
  color: var(--fg);
  text-align: center;
  letter-spacing: -0.005em;
}
.wp-modal-body .wp-emphasis {
  font-size: 22px;
  line-height: 1.3;
  margin: 22px 0;
}

/* ── Callout — bordered box around a single all-bold paragraph,
   matching the PDF treatment of key emphasis lines (e.g. "Economy
   built on trust. Trust built on truth.", "PII never reaches the
   chain.", "Shows the truth. Nothing but the truth. On-chain."). */
.wp-callout {
  margin: 14px 0;
  padding: 11px 16px;
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--fg);
  background: rgba(0, 0, 0, 0.025);
  font-family: var(--sans);
  font-weight: 600;
  font-size: 14px;
  line-height: 1.5;
  color: var(--fg);
  letter-spacing: -0.005em;
  text-wrap: balance;
  break-inside: avoid;
}
:root.dark .wp-callout {
  background: rgba(255, 255, 255, 0.03);
}
.wp-modal-body .wp-callout {
  font-size: 14.5px;
}

/* Header separators — horizontal rules between meta and the
   emphasis line, and between emphasis and the body. The second
   header sep gets a slightly thicker bottom border for visual weight. */
.wp-header .wp-sep {
  margin: 18px 0 4px 0;
  border: none;
  border-top: 1px solid var(--border-strong);
  width: 100%;
}
.wp-header .wp-sep + .wp-emphasis + .wp-sep {
  margin-top: 4px;
  margin-bottom: 20px;
  border-top: 1px solid var(--border-strong);
  position: relative;
}
/* Double rule under "Before agents can pay…" via a layered shadow. */
.wp-header .wp-sep + .wp-emphasis + .wp-sep::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 3px;
  border-top: 1px solid var(--border-strong);
}

/* Ordered list — same visual rhythm as .wp-list but numbered. */
.wp-list--ordered {
  list-style: none;
  counter-reset: wp-ol;
  padding-left: 18px;
}
.wp-list--ordered li {
  counter-increment: wp-ol;
}
.wp-list--ordered li::before {
  content: counter(wp-ol) ".";
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted);
  font-family: var(--mono);
  font-size: 11px;
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 16px;
}
.wp-list--ordered li {
  padding-left: 22px;
}

/* ── Section headings inside the column flow — uppercase Roman
   numeral + title on one line with a thin rule directly under. */
.wp-modal-body .wp-cols .wp-h2 {
  margin: 36px 0 0 0;
  padding: 0 0 8px 0;
  font-family: var(--sans);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  font-size: 16px;
  line-height: 1.25;
  border-bottom: 1px solid var(--border-strong);
  text-wrap: balance;
}
.wp-modal-body .wp-cols .wp-h2:first-child {
  margin-top: 0;
}
/* Restore breathing room between the underlined H2 and its first
   sibling block (paragraph, list, code, table, etc). */
.wp-modal-body .wp-cols .wp-h2 + .wp-line,
.wp-modal-body .wp-cols .wp-h2 + .wp-list,
.wp-modal-body .wp-cols .wp-h2 + .wp-h3 {
  margin-top: 12px;
}
/* H3 sub-headings — italic, lighter weight, no rule. */
.wp-modal-body .wp-cols .wp-h3 {
  margin: 22px 0 6px 0;
  font-family: var(--sans);
  font-weight: 700;
  font-style: italic;
  font-size: 15px;
  letter-spacing: -0.005em;
  text-transform: none;
}

/* Drop the body H2/H3 from the "break-after: avoid" rule we set
   earlier — paired with border-bottom they need to be allowed to
   end a column on their own line if necessary. (The break-inside:
   avoid on the heading itself still prevents internal splits.) */

/* ── Tables ─────────────────────────────────────────────────────── */
.wp-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.015);
}
:root:not(.dark) .wp-table-wrap {
  background: #FAFAFA;
}
.wp-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--fg);
  line-height: 1.5;
}
.wp-table th,
.wp-table td {
  border: 1px solid var(--border);
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
}
.wp-table thead th {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
  font-weight: 500;
  background: rgba(0, 0, 0, 0.02);
}
:root.dark .wp-table thead th {
  background: rgba(255, 255, 255, 0.025);
}
.wp-modal-body .wp-table {
  font-size: 13px;
}
.wp-modal-body .wp-table th,
.wp-modal-body .wp-table td {
  padding: 12px 14px;
}

/* ── Figures (SVG schematics) — no surrounding frame to match PDF. */
.wp-figure {
  margin: 0;
}
.wp-figure-frame {
  border: none;
  padding: 12px 0;
  background: transparent;
  color: var(--fg);
  display: flex;
  justify-content: center;
}
:root.dark .wp-figure-frame {
  background: transparent;
}
.wp-fig-svg {
  width: 100%;
  height: auto;
  max-width: 100%;
  display: block;
}
.wp-figure-caption {
  margin-top: 10px;
  font-family: var(--sans);
  font-size: 13px;
  font-style: italic;
  color: var(--fg-muted);
  text-align: center;
  text-wrap: balance;
}

/* ── Code / algorithm blocks ────────────────────────────────────── */
.wp-code {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.03);
  overflow: hidden;
}
:root.dark .wp-code {
  background: rgba(255, 255, 255, 0.03);
}
.wp-code-title {
  font-family: var(--sans);
  font-style: italic;
  font-weight: 700;
  font-size: 14px;
  color: var(--fg);
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  background: transparent;
  letter-spacing: -0.005em;
  text-transform: none;
}
:root.dark .wp-code-title {
  background: transparent;
}
.wp-code-body {
  margin: 0;
  padding: 14px 16px;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.6;
  white-space: pre;
  overflow-x: auto;
  color: var(--fg);
  -webkit-overflow-scrolling: touch;
}
.wp-modal-body .wp-code-body {
  font-size: 13px;
}

/* ── Timeline (Dispute lifecycle in §XXVII) ────────────────────── */
.wp-timeline-wrap {
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.wp-timeline {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--sans);
  font-size: 13px;
  color: var(--fg);
  line-height: 1.55;
}
.wp-timeline th,
.wp-timeline td {
  padding: 12px 14px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}
.wp-timeline tr:last-child th,
.wp-timeline tr:last-child td {
  border-bottom: none;
}
.wp-timeline th {
  font-family: var(--mono);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--fg-muted);
  width: 80px;
  white-space: nowrap;
  background: rgba(0, 0, 0, 0.02);
  border-right: 1px solid var(--border);
}
:root.dark .wp-timeline th {
  background: rgba(255, 255, 255, 0.025);
}

/* ── References ─────────────────────────────────────────────────── */
.wp-refs {
  margin: 0;
  padding: 0 0 0 30px;
  list-style: decimal;
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.6;
  color: var(--fg);
}
.wp-refs li {
  margin-bottom: 10px;
  padding-left: 6px;
}
.wp-refs li::marker {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-muted);
}
.wp-refs-link {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 2px;
  word-break: break-all;
  font-family: var(--mono);
  font-size: 11.5px;
}
.wp-refs-link:hover {
  color: #18F0BF;
}

/* ─── Chapter 03 — scoreboard ──────────────────────────────────────── */
.chapter-03 {
  position: relative;
  background: var(--bg);
  padding: 6vh 10% 12vh;
  z-index: 20;
  box-sizing: border-box;
  text-align: center;
}

.chapter-03-label,
.chapter-03-title,
.chapter-03-sub {
  text-align: left;
}
.chapter-03-label,
.chapter-03-title,
.chapter-03-sub,
.scoreboard {
  max-width: 1200px;
  margin-left: auto;
  margin-right: auto;
}

.chapter-03-label {
  text-align: center;
  justify-content: center;
  display: flex;
  margin-bottom: 6vh;
}

.chapter-03-label {
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 4vh 0;
}
.chapter-03-label::before {
  display: none;
}

.chapter-03-title {
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(40px, 5.4vw, 92px);
  line-height: 1.02;
  letter-spacing: -0.04em;
  color: var(--fg);
  margin: 0 0 12px 0;
  text-align: left;
}
.chapter-03-sub {
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(14px, 1.2vw, 19px);
  color: var(--fg-muted);
  margin: 0 0 8vh 0;
  text-align: left;
}

.chapter-03-closing {
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(38px, 5vw, 96px);
  line-height: 0.98;
  letter-spacing: -0.04em;
  color: var(--fg);
  margin: 15vh 0;
  text-align: center;
  width: 100vw;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  text-wrap: balance;
}

/* ─── Scoreboard table ─────────────────────────────────────────────
   Grid: 1 scorer-label column + 10 signal columns. Rows are emitted
   in order via JSX cells:
     row 0  →  WEIGHT label + 10 weight values
     row 1  →  empty + 10 signal headers (column titles)
     rows 2-8 → 7 scorer rows (TrustLedger first, mint-outlined)
*/
.scoreboard {
  display: block;
  border: none;
  position: relative;
  /* Shared column template — header strip + every row card both consume
     this so signal labels line up over their dots. Equal-width signal
     columns; the label column is sized just wide enough for the
     scorer names so signal columns get maximum room for their labels. */
  --scoreboard-cols:
    minmax(130px, 1.2fr) repeat(10, minmax(0, 1fr));
}

/* Column header strip — shares the 11-col grid with each row card
   below it so the signal labels align over their respective columns.
   Horizontal padding matches the row card padding so columns line up. */
.scoreboard-header-row {
  display: grid;
  grid-template-columns: var(--scoreboard-cols);
  padding: 0 16px;
  margin-bottom: 12px;
}

/* ── Weight row (mono, muted, small) ── */
.scoreboard-weight {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  text-align: center;
  padding: 14px 6px 8px;
  font-variant-numeric: tabular-nums;
}
.scoreboard-weight--label {
  text-align: left;
  padding-left: 14px;
}

/* ── Signal column headers ── */
.scoreboard-head {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  /* Modest horizontal padding so each label has clear visual separation
     from its neighbours while leaving room for the longer phrases to
     fit on one or two lines without breaking mid-word. */
  padding: 8px 8px 14px;
  min-height: 84px;
  overflow: hidden;
}
.scoreboard-head--first {}
.scoreboard-head-label {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg);
  line-height: 1.25;
  text-align: center;
  /* Wrap at word/hyphen boundaries only — never mid-word. Balanced
     wrapping keeps the two lines of any wrapped label similar length. */
  text-wrap: balance;
  overflow-wrap: normal;
  word-break: normal;
  hyphens: manual;
}

/* ── Row card — wraps each competitor row. Cards are spaced rather
   than touching; the column grid lives inside each card so cells
   still align across rows. ── */
.scoreboard-rows {
  display: flex;
  flex-direction: column;
  gap: 8px; /* space-y-2 */
}
.scoreboard-row {
  position: relative;
  display: grid;
  grid-template-columns: var(--scoreboard-cols);
  align-items: stretch;
  padding: 8px 16px;
  border-radius: 12px;
  background: color-mix(in oklab, var(--fg) 4%, transparent);
  border: 1px solid color-mix(in oklab, var(--border-strong) 60%, transparent);
  isolation: isolate;
  transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
  animation: scoreboard-row-enter 720ms cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
  animation-delay: calc(0.1s + var(--row-i, 0) * 0.08s);
}
.scoreboard-row:hover {
  transform: translateY(-1px);
}
/* Hovered row sits above subsequent row cards so its competitor
   dossier popover (which extends out beyond the card bounds) is not
   occluded by rows further down the list. */
.scoreboard-row.is-hovered {
  z-index: 40;
}
.scoreboard-row.is-tl {
  border-color: rgba(24, 240, 191, 0.4);
  background: color-mix(in oklab, var(--fg) 4%, transparent);
}

/* Light-mode rows stay on the base cool greyscale (color-mix against
   #0A0A0A produces a pure cool tint). The TrustLedger row keeps the
   teal accent border — the only color allowed in this view. */
:root:not(.dark) .scoreboard-row.is-tl {
  border-color: rgba(24, 240, 191, 0.55);
}
/* Gradient overlay on the right edge of the TL row — replaces the
   pulsing mint glow. 30% wide, fades right→transparent at left. */
.scoreboard-row.is-tl::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-image: linear-gradient(to left, rgba(24, 240, 191, 0.10), transparent);
  background-size: 30% 100%;
  background-position: right;
  background-repeat: no-repeat;
  z-index: 0;
}
.scoreboard-row > * { position: relative; z-index: 1; }

@keyframes scoreboard-row-enter {
  from {
    opacity: 0;
    transform: translateX(-25px) scale(0.95);
    filter: blur(4px);
  }
  to {
    opacity: 1;
    transform: translateX(0) scale(1);
    filter: blur(0);
  }
}
/* Pause the entry animation until the chapter has revealed (the
   chapter-03.is-pending wrapper still applies during initial fade-in). */
.chapter-03.is-pending .scoreboard-row {
  animation-play-state: paused;
  opacity: 0;
}

/* ── Scorer cells (base) — now sit inside .scoreboard-row cards.
   Row dividers removed; cards provide the separation. ── */
.scoreboard-cell {
  padding: 12px 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  font-family: var(--sans);
  font-size: 14px;
  color: var(--fg);
  transition: opacity 200ms ease;
}

.scoreboard-cell--label {
  justify-content: flex-start;
  text-align: left;
  padding-left: 6px;
  font-weight: 500;
  letter-spacing: -0.005em;
}

/* ── TrustLedger row label — accent green + bold. ── */
.scoreboard-cell--label.is-tl {
  font-family: var(--sans);
  font-weight: 700;
  font-size: 15.5px;
  letter-spacing: -0.01em;
  color: #18F0BF;
}
:root.dark .scoreboard-cell--label.is-tl { color: #18F0BF; }

/* Tooltip needs a positioning context + visible overflow on the label
   cell so it can extend out the right side. */
.scoreboard-cell--label {
  position: relative;
  overflow: visible;
}
/* When the row is hovered, lift the label cell above its later grid
   siblings so the dossier (inside it) paints over the signal cells. */
.scoreboard-cell--label.is-hovered {
  z-index: 31;
}

/* ── Row hover dim (excludes TL row) — applies per-row card now. ── */
.scoreboard.is-hovered .scoreboard-row:not(.is-hovered):not(.is-tl) .scoreboard-cell {
  opacity: 0.45;
}

/* ── Dot rendering ── */
.score-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}
.score-dot.filled {
  background: var(--fg);
}
:root.dark .score-dot.filled {
  background: var(--fg);
}
.score-dot.hollow {
  width: 8px;
  height: 8px;
  border: 1px solid color-mix(in oklab, var(--border-strong) 60%, transparent);
  background: transparent;
}
:root.dark .score-dot.hollow {
  border-color: color-mix(in oklab, var(--border-strong) 80%, transparent);
}
/* ── Partial state ── left half solid (var(--fg)), right half outlined
   ring. The gradient fills the full 10×10 box (clipped to a circle by
   border-radius on .score-dot). A pseudo-element overlay then paints a
   1px ring around the whole circle so the right half reads as the
   hollow variant while the left half stays cleanly half-filled. */
.score-dot.partial {
  position: relative;
  width: 10px;
  height: 10px;
  background: linear-gradient(to right, var(--fg) 0 50%, transparent 50% 100%);
}
.score-dot.partial::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid color-mix(in oklab, var(--border-strong) 60%, transparent);
  box-sizing: border-box;
  pointer-events: none;
}
:root.dark .score-dot.partial::after {
  border-color: color-mix(in oklab, var(--border-strong) 80%, transparent);
}
.score-dot.tl-filled {
  background: #18F0BF;
  width: 11px;
  height: 11px;
  box-shadow: 0 0 12px rgba(24, 240, 191, 0.45);
}

/* ── Footnote dossier line ── */
.dossier-line--footnote {
  margin-top: 10vh;
  margin-bottom: 0;
}

/* Reveal animation hooks (driven by IO via JS) */
.scoreboard-cell, .scoreboard-head, .scoreboard-weight,
.chapter-03-title, .chapter-03-sub, .chapter-03-closing {
  opacity: 1;
  transition: opacity 360ms ease, transform 360ms ease;
}
.chapter-03.is-pending .chapter-03-title,
.chapter-03.is-pending .chapter-03-sub,
.chapter-03.is-pending .chapter-03-closing,
.chapter-03.is-pending .scoreboard-cell,
.chapter-03.is-pending .scoreboard-weight,
.chapter-03.is-pending .scoreboard-head {
  opacity: 0;
  transform: translateY(8px);
}

/* ─── Footer placeholder ───────────────────────────────────────────── */
.footer-ph {
  min-height: 50vh;
  padding: 16vh 40px;
  text-align: center;
  background: var(--bg);
  border-top: 1px solid var(--border);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  position: relative;
  z-index: 20;
}

/* ─── Dossier detail lines — Geist Mono, muted, uppercase ─────────── */
/* Matches the bottom status-strip aesthetic from the hero. Used above
   the comparison table, in the chapter divider, and on testimonial
   hover state. */
.dossier-line {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin: 0 0 4vh 0;
}
.dossier-line--center {
  text-align: center;
  display: block;
}

/* ─── Chapter divider — END CHAPTER N · WORDS · READ-TIME ────────── */
.chapter-divider {
  display: flex;
  align-items: center;
  gap: 18px;
  max-width: 1200px;
  margin: 0 auto;
  padding: 4vh 6vw;
  width: 100%;
  box-sizing: border-box;
}
.chapter-divider-line {
  flex: 1 1 auto;
  height: 1px;
  background: var(--border);
}
.chapter-divider-text {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  white-space: nowrap;
  flex-shrink: 0;
}

/* ─── Manifesto block (Satoshi) ────────────────────────────────────
   Inverted from the rest of the page in each theme so the quote feels
   like its own moment: dark-on-light in light mode, light-on-dark in
   dark mode. */
.manifesto {
  position: relative;
  /* Background + borders intentionally absent — the manifesto and the
     footer below it share one continuous dark canvas (body bg + a
     viewport-fixed raining-letters canvas). Any border or solid bg
     here would re-create the visible seam we're trying to eliminate. */
  background: transparent;
  color: #FAFAFA;
  padding: 14vh 8vw;
  border-top: none;
  border-bottom: none;
}
:root:not(.dark) .manifesto {
  background: transparent;
  color: #0A0A0A;
}
.manifesto-inner {
  max-width: 980px;
  margin: 0 auto;
  text-align: left;
}
.manifesto-quote {
  margin: 0;
  max-width: 880px;
}
.manifesto-quote-text {
  font-family: var(--sans);
  font-weight: 400;
  font-style: italic;
  font-size: clamp(22px, 2.2vw, 36px);
  line-height: 1.4;
  letter-spacing: -0.015em;
  color: rgba(250, 250, 250, 0.92);
  margin: 0 0 4vh 0;
  text-wrap: pretty;
}
:root:not(.dark) .manifesto-quote-text {
  color: rgba(10, 10, 10, 0.92);
}
.manifesto-quote-cite {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(250, 250, 250, 0.55);
}
:root:not(.dark) .manifesto-quote-cite {
  color: rgba(10, 10, 10, 0.55);
}

/* ─── Site footer ────────────────────────────────────────────────── */
.site-footer {
  /* Transparent so the body bg (and the shared manifesto-rain canvas
     that's fixed to the viewport) flow into the footer with no visible
     seam. The rain is owned by <Manifesto/> and runs across both
     sections continuously — see .manifesto-rain. */
  background: transparent;
  border-top: none;
  padding: 5vh 6vw 3vh;
  position: relative;
  overflow: hidden;
  z-index: 20;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  min-height: 52vh;
}

.site-footer-inner {
  position: relative;
  z-index: 1;
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 5vw;
  align-items: center;
}
.site-footer-left {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2.4vh;
}
.site-footer-wordmark {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 24px;
  letter-spacing: -0.02em;
  color: var(--fg);
  text-decoration: none;
}
.site-footer-statement {
  margin: 0;
  font-family: var(--sans);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.35;
  letter-spacing: -0.015em;
  color: var(--fg);
  max-width: 400px;
}
.site-footer-copy {
  position: relative;
  z-index: 1;
  margin: 1.2vh 0 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.site-footer-right {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
}
.site-footer-slogan {
  margin: 0;
  font-family: var(--sans);
  font-weight: 600;
  font-size: clamp(28px, 3.4vw, 56px);
  line-height: 1.02;
  letter-spacing: -0.035em;
  color: var(--fg);
  text-wrap: balance;
}
.site-footer-contact {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 0;
}
.site-footer-link {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 14px;
  color: var(--fg);
  text-decoration: none;
  position: relative;
  width: fit-content;
}
.site-footer-link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 1px;
  background: currentColor;
  opacity: 0;
  transform: scaleX(0.6);
  transform-origin: 0 50%;
  transition: opacity 200ms ease, transform 200ms ease;
}
.site-footer-link:hover::after {
  opacity: 0.7;
  transform: scaleX(1);
}

/* ── Competitor dossier tooltip ──────────────────────────────────────
   Absolute-positioned card extending out the LEFT side of each
   competitor row's label cell. Fades in when the row carries the
   .is-hovered class (driven by hoveredRow state — every cell in the
   row, including the label, picks up .is-hovered together). Pointer-
   events disabled so leaving the row across the tooltip area still
   counts as a leave. TrustLedger row has no entry → no tooltip. */
.competitor-dossier {
  position: absolute;
  top: 50%;
  left: calc(100% + 14px);
  transform: translateY(-50%) translateX(-6px);
  width: 320px;
  padding: 16px 18px;
  border-radius: 12px;
  background: #FAFAFA;
  border: 1px solid #D4D4D8;
  box-shadow:
    0 18px 50px -12px rgba(0, 0, 0, 0.22),
    0 6px 16px -6px rgba(0, 0, 0, 0.14);
  font-family: var(--sans);
  color: var(--fg);
  text-align: left;
  pointer-events: none;
  opacity: 0;
  z-index: 30;
  transition: opacity 150ms ease, transform 150ms ease;
}
:root.dark .competitor-dossier {
  background: #0F0F0F;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.05),
    0 18px 50px -12px rgba(0, 0, 0, 0.7),
    0 6px 16px -6px rgba(0, 0, 0, 0.45);
}
.scoreboard-cell--label.is-hovered .competitor-dossier {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

.competitor-dossier-header {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.competitor-dossier-divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 10px 0;
}
.competitor-dossier-desc {
  margin: 0 0 12px 0;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--fg);
  letter-spacing: -0.005em;
}
.competitor-dossier-mech {
  margin: 0 0 10px 0;
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.5;
  color: var(--fg);
}
.competitor-dossier-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.competitor-dossier-list li {
  position: relative;
  padding-left: 14px;
  margin-bottom: 6px;
  font-family: var(--sans);
  font-size: 12px;
  line-height: 1.45;
  color: var(--fg-muted);
}
.competitor-dossier-list li::before {
  content: "\2013";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--fg-muted);
}
.competitor-dossier-list li:last-child { margin-bottom: 0; }
.competitor-dossier-footnote {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
  text-align: center;
}

/* ─── HoverPeek inline trigger + preview card ──────────────────── */
.hoverpeek-trigger {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: text-decoration-color 200ms ease, color 200ms ease;
}
.hoverpeek-trigger:hover,
.hoverpeek-trigger:focus-visible {
  text-decoration-color: var(--fg);
  color: var(--fg);
}
.hoverpeek-card {
  position: fixed;
  z-index: 1000;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 4px;
  box-shadow:
    0 16px 40px -10px rgba(0, 0, 0, 0.22),
    0 6px 14px -4px rgba(0, 0, 0, 0.14);
  transform: translate(-50%, -100%);
  transform-origin: 50% 100%;
  animation: hoverpeekIn 200ms cubic-bezier(0.4, 0, 0.2, 1) both;
  pointer-events: auto;
}
:root.dark .hoverpeek-card {
  box-shadow:
    0 16px 40px -10px rgba(0, 0, 0, 0.55),
    0 6px 14px -4px rgba(0, 0, 0, 0.40);
}
.hoverpeek-card img {
  display: block;
  border-radius: 6px;
  background: var(--bg);
}
@keyframes hoverpeekIn {
  from { opacity: 0; transform: translate(-50%, calc(-100% + 6px)) scale(0.96); }
  to   { opacity: 1; transform: translate(-50%, -100%) scale(1); }
}

/* ─── LocationMap ──────────────────────────────────────────────── */
.locmap-wrap {
  position: relative;
  perspective: 1000px;
}
.locmap {
  position: relative;
  width: 240px;
  height: 140px;
  border-radius: 16px;
  background: var(--bg);
  border: 1px solid var(--border);
  overflow: hidden;
  cursor: pointer;
  user-select: none;
  transform-style: preserve-3d;
  color: var(--fg);
  transition:
    width 420ms cubic-bezier(0.34, 1.3, 0.6, 1),
    height 420ms cubic-bezier(0.34, 1.3, 0.6, 1),
    transform 220ms ease;
}
.locmap.is-expanded {
  width: 360px;
  height: 280px;
}
.locmap-grid {
  position: absolute;
  inset: 0;
  opacity: 0.05;
  pointer-events: none;
  transition: opacity 300ms ease;
}
.locmap.is-expanded .locmap-grid { opacity: 0; }

.locmap-streets {
  position: absolute;
  inset: 0;
  pointer-events: none;
  animation: locmapStreetsIn 320ms ease both;
}
@keyframes locmapStreetsIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.locmap-streets-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.locmap-road {
  stroke: currentColor;
  stroke-opacity: 0.25;
  stroke-width: 4;
}
.locmap-road--v {
  stroke-width: 3;
  stroke-opacity: 0.20;
}
.locmap-street {
  stroke: currentColor;
  stroke-opacity: 0.10;
  stroke-width: 1.5;
}
.locmap-bldg {
  position: absolute;
  background: rgba(0, 0, 0, 0.22);
  border: 1px solid rgba(0, 0, 0, 0.10);
  border-radius: 2px;
}
:root.dark .locmap-bldg {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.06);
}
.locmap-pin {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -100%);
  color: var(--fg);
  filter: drop-shadow(0 0 10px rgba(0, 0, 0, 0.30));
  animation: locmapPinIn 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
:root.dark .locmap-pin {
  filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.18));
}
@keyframes locmapPinIn {
  from { opacity: 0; transform: translate(-50%, -80%) scale(0.6); }
  to   { opacity: 1; transform: translate(-50%, -100%) scale(1); }
}
.locmap-vignette {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, var(--bg), transparent 60%);
  opacity: 0.6;
  pointer-events: none;
}

.locmap-content {
  position: relative;
  z-index: 5;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 14px 18px;
  box-sizing: border-box;
}
.locmap-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
}
.locmap-icon {
  color: var(--fg);
  transition: opacity 200ms ease;
}
.locmap-icon.is-faded { opacity: 0; }
.locmap-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.05);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  transition: background 200ms ease, transform 200ms ease;
}
.locmap.is-hovered .locmap-status {
  background: rgba(0, 0, 0, 0.08);
  transform: scale(1.04);
}
:root.dark .locmap-status {
  background: rgba(255, 255, 255, 0.06);
}
:root.dark .locmap.is-hovered .locmap-status {
  background: rgba(255, 255, 255, 0.10);
}
.locmap-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--fg);
}
.locmap-status-text {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.locmap-bottom {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.locmap-loc {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: -0.01em;
  color: var(--fg);
  margin: 0;
  transition: transform 200ms ease;
}
.locmap.is-hovered .locmap-loc { transform: translateX(4px); }
.locmap-coords {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--fg-muted);
  margin: 2px 0 0 0;
}
.locmap-underline {
  display: block;
  height: 1px;
  margin-top: 6px;
  background: linear-gradient(
    to right,
    rgba(0, 0, 0, 0.45) 0%,
    rgba(0, 0, 0, 0.20) 40%,
    transparent 100%
  );
  transform-origin: 0 50%;
  transform: scaleX(0.3);
  transition: transform 360ms ease;
}
:root.dark .locmap-underline {
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0.45) 0%,
    rgba(255, 255, 255, 0.20) 40%,
    transparent 100%
  );
}
.locmap.is-hovered .locmap-underline,
.locmap.is-expanded .locmap-underline {
  transform: scaleX(1);
}
.locmap-hint {
  position: absolute;
  left: 0;
  bottom: -22px;
  width: 240px;
  margin: 0;
  text-align: center;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 200ms ease, transform 200ms ease;
  pointer-events: none;
}
.locmap-hint.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ─── Theme-toggle tooltip ────────────────────────────────────────── */
/* Floating pill anchored below the dark/light toggle. Copy switches by
   mode: "scared of the dark?" in dark, "too bright?" in light. */
.theme-toggle-tip {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  text-transform: lowercase;
  letter-spacing: 0.01em;
  color: var(--bg);
  background: var(--fg);
  padding: 6px 10px;
  border-radius: 999px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity 150ms ease, transform 150ms ease;
}
:root.dark .theme-toggle-tip {
  background: rgba(250, 250, 250, 0.92);
  color: #0A0A0A;
}
:root:not(.dark) .theme-toggle-tip {
  background: rgba(10, 10, 10, 0.88);
  color: #FAFAFA;
}
.theme-toggle:hover .theme-toggle-tip,
.theme-toggle:focus-visible .theme-toggle-tip {
  opacity: 1;
  transform: translateY(0);
}

/* ─── Testimonial glitch reveal ─────────────────────────────────────
   Hover plays a fast, chaotic sequence:
     1. ~80ms invert flash       (.glitch-invert)
     2. ~150ms chromatic RGB-split jitter on text (.testimonial-{quote,name,role})
     3. ~320ms scanline pass top→bottom (.glitch-scan)
     4. ~200ms static noise burst (.glitch-noise) at the stamp moment
     5. Quote scrambles → "[VERIFICATION FAILED]" (handled in JSX)
   All driven by .is-glitching class; resolves to .is-revealed steady state. */

.testimonial-card .glitch-invert,
.testimonial-card .glitch-scan,
.testimonial-card .glitch-noise {
  position: absolute;
  pointer-events: none;
  inset: 0;
  opacity: 0;
  display: block;
}

/* Quote text in revealed state: GlitchText drives its own visuals, so
   the wrapper just needs to size + clear the default body styling. */
.testimonial-card.is-revealed .testimonial-quote {
  opacity: 1;
  color: transparent; /* layers paint their own colors */
  font-size: 14px;
  line-height: 1.55;
  min-height: 1.55em;
}

/* ── Layer 1: invert flash (~80ms) ── */
.testimonial-card .glitch-invert {
  background: #FAFAFA;
  mix-blend-mode: difference;
  z-index: 6;
}
.testimonial-card.is-glitching .glitch-invert {
  animation: glitchInvertFlash 90ms ease-out 1;
}
@keyframes glitchInvertFlash {
  0%   { opacity: 0; }
  18%  { opacity: 1; }
  100% { opacity: 0; }
}

/* ── Layer 2: scanline pass (~320ms) ── */
.testimonial-card .glitch-scan {
  inset: auto 0 auto 0;
  top: 0;
  height: 2px;
  background: linear-gradient(
    to bottom,
    transparent 0%,
    rgba(255, 255, 255, 0.85) 50%,
    transparent 100%
  );
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.6);
  z-index: 5;
}
:root:not(.dark) .testimonial-card .glitch-scan {
  background: linear-gradient(
    to bottom,
    transparent 0%,
    rgba(10, 10, 10, 0.75) 50%,
    transparent 100%
  );
  box-shadow: 0 0 8px rgba(10, 10, 10, 0.45);
}
.testimonial-card.is-glitching .glitch-scan {
  animation: glitchScanPass 320ms cubic-bezier(0.25, 0, 0.4, 1) 1;
}
@keyframes glitchScanPass {
  0%   { top: -4px; opacity: 0; }
  10%  { opacity: 1; }
  90%  { opacity: 1; }
  100% { top: calc(100% + 4px); opacity: 0; }
}

/* ── Layer 3: static noise burst (~200ms, kicks in slightly after enter) ── */
.testimonial-card .glitch-noise {
  z-index: 4;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220 220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1   0 0 0 0 1   0 0 0 0 1   0 0 0 0.7 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 220px 220px;
  mix-blend-mode: screen;
}
:root:not(.dark) .testimonial-card .glitch-noise {
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220 220'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0   0 0 0 0 0   0 0 0 0 0   0 0 0 0.7 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}
.testimonial-card.is-glitching .glitch-noise {
  animation: glitchNoiseBurst 220ms steps(7, end) 1;
}
@keyframes glitchNoiseBurst {
  0%   { opacity: 0;    background-position: 0 0; }
  20%  { opacity: 0.42; background-position: 30px 60px; }
  40%  { opacity: 0.35; background-position: 60px 20px; }
  60%  { opacity: 0.40; background-position: 10px 90px; }
  80%  { opacity: 0.30; background-position: 90px 30px; }
  100% { opacity: 0;    background-position: 0 0; }
}

/* ── Layer 4: chromatic aberration on text (name + role only;
   the quote runs its own GlitchText jitter when revealed). ── */
.testimonial-card.is-glitching .testimonial-name,
.testimonial-card.is-glitching .testimonial-role {
  animation: glitchChroma 160ms steps(7, end) 2;
}
/* Apply to the fake quote only on the hover-out window (when reverting
   from GlitchText back to plain text). */
.testimonial-card.is-glitching:not(.is-revealed) .testimonial-quote {
  animation: glitchChroma 160ms steps(7, end) 2;
}
@keyframes glitchChroma {
  0%   { text-shadow: -4px 0 #FF003C, 4px 0 #00E5FF; transform: translate(2px, -1px); }
  15%  { text-shadow: 3px 0 #FF003C, -3px 0 #00E5FF; transform: translate(-3px, 1px); }
  30%  { text-shadow: -2px 2px #FF003C, 2px -2px #00E5FF; transform: translate(1px, 2px); }
  45%  { text-shadow: 5px 0 #00FF66, -5px 0 #FF00FF; transform: translate(-2px, 0); }
  60%  { text-shadow: -3px 1px #FF003C, 3px -1px #00E5FF; transform: translate(3px, -1px); }
  75%  { text-shadow: 2px 0 #FF003C, -2px 0 #00E5FF; transform: translate(-1px, 0); }
  100% { text-shadow: none; transform: translate(0, 0); }
}

/* ─── GlitchText (used for the redacted quote) ──────────────────────
   Three stacked text layers (red / green / blue) jittering at slightly
   different durations. Continuous while the card is revealed. The
   wrapper is inline-block so it sits inline in the .testimonial-quote
   paragraph. */
.glitch-text {
  position: relative;
  display: inline-block;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.04em;
  line-height: 1.55;
  text-transform: uppercase;
}
.glitch-text-layer {
  display: inline-block;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  white-space: nowrap;
  mix-blend-mode: screen;
  will-change: transform, opacity;
}
:root:not(.dark) .glitch-text-layer {
  mix-blend-mode: multiply;
}
/* The R and G layers are absolutely positioned on top of the B layer
   (which sits in normal flow and sizes the wrapper). */
.glitch-text-layer--r,
.glitch-text-layer--g {
  position: absolute;
  inset: 0;
}
.glitch-text-layer--r {
  animation: glitchTextR 520ms cubic-bezier(0.4, 0.2, 0.4, 1) infinite alternate;
}
.glitch-text-layer--g {
  animation: glitchTextG 460ms cubic-bezier(0.4, 0.2, 0.4, 1) infinite alternate;
}
.glitch-text-layer--b {
  animation: glitchTextB 400ms cubic-bezier(0.4, 0.2, 0.4, 1) infinite alternate;
}

@keyframes glitchTextR {
  0%   { transform: translate(-2px, 0)    skew(0deg);  opacity: 1.00; }
  50%  { transform: translate( 2px, -1px) skew(-2deg); opacity: 0.80; }
  100% { transform: translate(-2px, 1px)  skew(2deg);  opacity: 0.90; }
}
@keyframes glitchTextG {
  0%   { transform: translate( 2px, 1px)  skew(-2deg); opacity: 0.90; }
  50%  { transform: translate(-2px, -1px) skew(2deg);  opacity: 1.00; }
  100% { transform: translate( 2px, 0)    skew(0deg);  opacity: 0.80; }
}
@keyframes glitchTextB {
  0%   { transform: translate(-1px, -1px) skew(2deg);  opacity: 0.80; }
  50%  { transform: translate( 1px, 1px)  skew(-2deg); opacity: 0.90; }
  100% { transform: translate(-1px, 0)    skew(0deg);  opacity: 1.00; }
}
