/* POVA — chrome extras
 * Cross-page interaction layer that did not exist in wave 0.
 * Contains:
 *   1. Pointer-tracked gradient-hover system (CTAs + tiles)
 *   2. Custom cursor (small orange dot + delayed outline ring)
 *   3. Type-as-motion utilities (display lockup scrub-in + scroll parallax)
 *   4. Touch fallbacks for hover-only patterns
 *   5. Schematic scroll micro-utilities (chapter reveal)
 *   6. JSON-LD/SEO inert classes
 *
 * Loads on every page after chrome.css and the page-specific stylesheet,
 * so its rules win where it matters and inherit where it doesn't.
 */

/* ==========================================================================
 * 1. POINTER-TRACKED GRADIENT HOVER
 * --------------------------------------------------------------------------
 * .grad-hover      — base class for any element that should track pointer
 * .grad-hover--cta — orange-bloom variant for primary CTAs
 * .grad-hover--tile — restrained sweep variant for cards / tiles
 *
 * Set --mx / --my (in %, 0-100) per element from JS (motion.js).
 * Set --hover-strength (0-1) on hover/focus so the gradient blooms in.
 * ========================================================================== */

.grad-hover {
  position: relative;
  isolation: isolate;
  transition:
    --hover-strength var(--t-med) var(--ease-out),
    transform var(--t-fast) var(--ease-out),
    border-color var(--t-med) var(--ease-out);
}
.grad-hover::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 0;
  opacity: var(--hover-strength, 0);
  transition: opacity var(--t-med) var(--ease-out);
  background:
    radial-gradient(
      circle 320px at var(--mx, 50%) var(--my, 50%),
      color-mix(in oklab, var(--pova-orange) 18%, transparent) 0%,
      color-mix(in oklab, var(--pova-orange) 6%, transparent) 35%,
      transparent 70%
    );
  mix-blend-mode: screen;
}
/* Make sure children sit above the gradient */
.grad-hover > * { position: relative; z-index: 1; }

/* Default — engage on hover, focus-within, or programmatically via .is-pressed */
.grad-hover:hover,
.grad-hover:focus-within,
.grad-hover.is-pressed { --hover-strength: 1; }

/* Tile variant — softer, brand-grayscale sweep, orange only kisses the edge */
.grad-hover--tile::before {
  background:
    radial-gradient(
      circle 420px at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, 0.10) 0%,
      rgba(255, 255, 255, 0.04) 30%,
      color-mix(in oklab, var(--pova-orange) 4%, transparent) 70%,
      transparent 90%
    );
  mix-blend-mode: screen;
}

/* CTA variant — solid orange hover surface gets a conic sweep highlight */
.grad-hover--cta::before {
  background:
    conic-gradient(
      from calc(var(--hover-angle, 180deg)) at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, 0) 0deg,
      rgba(255, 255, 255, 0.35) 60deg,
      rgba(255, 255, 255, 0) 120deg,
      rgba(255, 255, 255, 0) 360deg
    );
  mix-blend-mode: overlay;
  opacity: calc(var(--hover-strength, 0) * 0.85);
}

/* Touch tap pulse — on touch, JS adds .is-pulsing AND animates the
 * .card-glow child outward from --mx / --my (last touch point). */
.is-pulsing > .card-glow,
.is-pulsing.grad-hover::before {
  animation: pova-tap-pulse 600ms var(--ease-out) forwards;
}
@keyframes pova-tap-pulse {
  0%   { opacity: 0.9; }
  60%  { opacity: 0.5; }
  100% { opacity: 0; }
}

/* Apply to existing classes site-wide — no markup change needed.
 * Each is also a .grad-hover-target so motion.js can attach pointer trackers. */
.product-card,
.note-card,
.value-card,
.retailer-card,
.contact-row,
.gallery-plate,
.faq-item,
.dl-item {
  --hover-strength: 0;
  position: relative;
  isolation: isolate;
}
/* Apply gradient-hover behaviour to existing card families without
 * changing per-card markup. */
.product-card,
.note-card,
.value-card,
.retailer-card,
.contact-row,
.gallery-plate {
  transition:
    border-color var(--t-med) var(--ease-out),
    transform var(--t-fast) var(--ease-out);
}
.product-card:hover,
.product-card:focus-within,
.note-card:hover,
.note-card:focus-within,
.value-card:hover,
.value-card:focus-within,
.retailer-card:hover,
.retailer-card:focus-within,
.contact-row:hover,
.contact-row:focus-within {
  border-color: rgba(255, 93, 1, 0.35);
}

/* Card families get a real pointer-tracked overlay via a pseudo
 * positioned in chrome-extras (instead of polluting their own files). */
.product-card,
.note-card,
.value-card,
.retailer-card,
.contact-row {
  /* keep existing ::before/::after for corners — overlay attaches to a
   * dedicated stacking child created by motion.js (.card-glow), so we
   * don't fight the corner-mark pseudos. */
}
.card-glow {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 0;
  opacity: 0;
  transition: opacity var(--t-med) var(--ease-out);
  background:
    radial-gradient(
      circle 380px at var(--mx, 50%) var(--my, 50%),
      rgba(255, 255, 255, 0.08) 0%,
      color-mix(in oklab, var(--pova-orange) 6%, transparent) 35%,
      transparent 70%
    );
  mix-blend-mode: screen;
}
.product-card:hover .card-glow,
.product-card:focus-within .card-glow,
.note-card:hover .card-glow,
.note-card:focus-within .card-glow,
.value-card:hover .card-glow,
.value-card:focus-within .card-glow,
.retailer-card:hover .card-glow,
.retailer-card:focus-within .card-glow,
.contact-row:hover .card-glow,
.contact-row:focus-within .card-glow,
.gallery-plate:hover .card-glow {
  opacity: 1;
}

/* CTA reflection — apply to the buy-bar CTA and about CTA, plus any
 * .cta-link helper. Uses the same --mx/--my pattern. */
.buy-bar .cta,
.about-cta .cta,
.cta-link {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.buy-bar .cta::before,
.about-cta .cta::before,
.cta-link::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    conic-gradient(
      from 200deg at var(--mx, 50%) var(--my, 50%),
      transparent 0deg,
      rgba(255, 255, 255, 0.25) 50deg,
      transparent 110deg,
      transparent 360deg
    );
  opacity: 0;
  transition: opacity var(--t-med) var(--ease-out);
  mix-blend-mode: overlay;
}
.buy-bar .cta:hover::before,
.buy-bar .cta:focus-visible::before,
.about-cta .cta:hover::before,
.about-cta .cta:focus-visible::before,
.cta-link:hover::before,
.cta-link:focus-visible::before { opacity: 1; }
/* Ensure inline children (arrow, label spans) sit above the sweep overlay */
.buy-bar .cta > *,
.about-cta .cta > *,
.cta-link > * { position: relative; z-index: 1; }

/* ==========================================================================
 * 2. CUSTOM CURSOR
 * --------------------------------------------------------------------------
 * Tiny orange dot + larger delayed outline ring. Hides on touch / no-pointer.
 * Morphs on interactive hover (covers: a, button, [role=button], [data-product]).
 * Respects prefers-reduced-motion.
 * ========================================================================== */

/* Hide custom cursor entirely on touch / coarse-pointer devices.
 * The cursor-dot + cursor-ring are desktop-fine-pointer affordances;
 * on mobile they leak through as orphan UI dots. */
@media (hover: none), (pointer: coarse) {
  .cursor-dot, .cursor-ring { display: none !important; }
}
.cursor-dot,
.cursor-ring {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 99999;
  border-radius: 50%;
  will-change: transform;
  /* hidden by default — JS unhides only when fine pointer is detected */
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease-out),
              width var(--t-med) var(--ease-out),
              height var(--t-med) var(--ease-out),
              background var(--t-med) var(--ease-out),
              border-color var(--t-med) var(--ease-out);
}
/* The dot is now a "+" glyph — POVA's registration mark, not a generic
 * follower dot. Holds a steady plus at rest; spins continuously while
 * hovering an interactive surface. The ring stays as a quiet halo.
 *
 * IMPORTANT: motion.js drives the parent .cursor-dot transform via
 * `translate3d(x, y, 0) translate(-50%, -50%)` to follow the pointer.
 * The spin animation MUST live on the inner ::before glyph, NOT the
 * parent — otherwise the keyframe transform overrides the JS-set
 * translation and the cursor snaps to the top-left corner. */
.cursor-dot {
  width: 18px;
  height: 18px;
  background: transparent;
  border-radius: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--pova-orange);
  line-height: 1;
  mix-blend-mode: normal;
  /* Override the parent ".cursor-dot" transition (the parent uses
   * transform: translate3d set inline by JS — never put transform in
   * the transition list, only opacity/color). */
  transition: opacity var(--t-fast) var(--ease-out),
              color var(--t-med) var(--ease-out);
}
.cursor-dot::before {
  content: "+";
  display: block;
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 400;
  line-height: 1;
  /* Spin lives here — leaves the parent transform alone. */
  transform: rotate(0deg);
  transition: font-size var(--t-med) var(--ease-out);
}
.cursor-ring {
  width: 28px;
  height: 28px;
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: transparent;
  transform: translate(-50%, -50%);
}
body.cursor-ready .cursor-dot,
body.cursor-ready .cursor-ring { opacity: 1; }
/* On interactive hover: ring expands and brightens, the + spins. */
@keyframes cursor-plus-spin {
  to { transform: rotate(360deg); }
}
body.cursor-hover .cursor-dot::before {
  animation: cursor-plus-spin 1.6s linear infinite;
  font-size: 18px;
}
body.cursor-hover .cursor-ring {
  width: 44px;
  height: 44px;
  border-color: var(--pova-orange);
}
body.cursor-pressed .cursor-ring {
  width: 22px;
  height: 22px;
}
body.cursor-pressed .cursor-dot::before { font-size: 14px; }
/* Hide on touch / coarse pointer / reduced motion */
@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .cursor-dot, .cursor-ring { display: none !important; }
  /* Restore native cursor when custom one is hidden */
  body { cursor: auto; }
}
/* Hide native cursor only when custom is active */
body.cursor-ready { cursor: none; }
body.cursor-ready a,
body.cursor-ready button,
body.cursor-ready [role="button"],
body.cursor-ready input,
body.cursor-ready textarea { cursor: none; }
/* Keep native cursor for inputs even when custom cursor is on — typing UX */
body.cursor-ready input,
body.cursor-ready textarea { cursor: text; }

/* ==========================================================================
 * 3. TYPE-AS-MOTION
 * --------------------------------------------------------------------------
 * .scrub-in        — display lockup scrubs in on first paint (per-letter)
 * .scroll-parallax — moves slightly slower than scroll
 * .reveal          — generic in-view fade-up, observed by motion.js
 * Reduced-motion collapses all to opacity fades.
 * ========================================================================== */

.scrub-in {
  --scrub-delay: 60ms;
  /* Don't collapse block headings — keep the element's natural display */
  overflow: hidden;
}
.scrub-in .word {
  display: inline-block;
  white-space: nowrap;
}
.scrub-in .char {
  display: inline-block;
}
.scrub-in .char {
  opacity: 0;
  transform: translateY(0.5em);
  animation: pova-scrub-in 700ms var(--ease-out) forwards;
  animation-delay: calc(var(--scrub-delay) * var(--i, 0));
}
@keyframes pova-scrub-in {
  0%   { opacity: 0; transform: translateY(0.5em); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .scrub-in .char {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

/* Scroll parallax — a subtle 8% drift on scroll, applied via inline
 * --parallax-y from motion.js. Reduced-motion makes this a no-op. */
.scroll-parallax {
  will-change: transform;
  transform: translate3d(0, var(--parallax-y, 0), 0);
}
@media (prefers-reduced-motion: reduce) {
  .scroll-parallax { transform: none !important; }
}

/* Reveal on scroll — IntersectionObserver toggles .is-in */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 700ms var(--ease-out),
    transform 700ms var(--ease-out);
}
.reveal.is-in {
  opacity: 1;
  transform: translateY(0);
}
.reveal[data-delay="1"] { transition-delay: 80ms; }
.reveal[data-delay="2"] { transition-delay: 160ms; }
.reveal[data-delay="3"] { transition-delay: 240ms; }
.reveal[data-delay="4"] { transition-delay: 320ms; }
.reveal[data-delay="5"] { transition-delay: 400ms; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: opacity 0.15s linear; }
}

/* ==========================================================================
 * 4. TOUCH FALLBACKS — extend hover-only patterns for touch
 * ========================================================================== */

@media (hover: none) {
  /* Instrument labels stay visible — already in home.css */
  /* Glance the active product on tap-into-view (toggled by motion.js) */
  .instrument.is-tapped .inst-label { opacity: 1; transform: none; }
  /* Card hover effects collapse to a tap-driven .is-tapped state */
  .product-card.is-tapped,
  .note-card.is-tapped,
  .value-card.is-tapped,
  .retailer-card.is-tapped { border-color: rgba(255, 93, 1, 0.35); }
  /* No custom cursor on touch — already handled in §2 */
}

/* ==========================================================================
 * 5. SCHEMATIC SCROLL CHAPTER — used on pova-8.html
 * --------------------------------------------------------------------------
 * Each chapter is a sticky-positioned panel with revealed schematic layers.
 * Layers fade/slide in as the chapter enters the viewport.
 * scroll-driven animations where supported, IntersectionObserver fallback.
 * ========================================================================== */

.schematic-story {
  position: relative;
  margin: 0 calc(-1 * var(--safe-desktop));
  padding: 0;
  background: #000;
}
.schematic-chapter {
  position: relative;
  min-height: 100vh;
  padding: 100px var(--safe-desktop);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  align-items: center;
}
.schematic-chapter .chapter-stage {
  position: sticky;
  top: 12vh;
  height: 76vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.schematic-chapter .chapter-text {
  max-width: 480px;
}
.schematic-chapter .chapter-num {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--pova-orange);
  opacity: 0.85;
  margin-bottom: 18px;
  display: block;
}
.schematic-chapter h3 {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(32px, 4.4vw, 56px);
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0 0 24px;
  max-width: 16ch;
}
.schematic-chapter p {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: 17px;
  line-height: 1.6;
  margin: 0 0 16px;
  opacity: 0.85;
  max-width: 44ch;
}

/* No-JS fallback — when motion.js can't run, every narrative chapter
 * presents fully (no scroll-driven reveal). The .no-js class is on <html>
 * by default and motion.js removes it on load. */
.no-js .narrative-chapter,
.no-js .schematic-chapter { /* both stories — keep readable without JS */ }
.no-js .narrative-chapter .chapter-stage img.product { opacity: 1; transform: none; }
.no-js .narrative-story--horizon .narrative-chapter::before { transform: scaleX(1); }
.no-js .narrative-story--horizon .stage-inner::after { opacity: 1; }
.no-js .narrative-story--signal .stage-grid { opacity: 1; }
.no-js .narrative-story--signal .stage-rule::before { transform: scaleX(1); }
.no-js .narrative-story--signal .stage-rule .tick { transform: translateX(-0.5px) scaleY(1); }
.no-js .narrative-story--signal .stage-figure .crosshair { opacity: 1; }
.no-js .narrative-story--signal .stage-figure .figure-num { opacity: 1; transform: none; }
.no-js .narrative-story--signal .stage-figure .dim-label { opacity: 1; transform: none; }
.no-js .narrative-story--orbit .stage-inner::before { opacity: 0.85; transform: rotate(0deg); }

/* The phone in the stage — fades from base photo to translucent schematic
 * as the chapter is read. */
.chapter-stage .schematic-stack {
  position: relative;
  width: 100%;
  max-width: 420px;
  aspect-ratio: 4 / 5;
}
.chapter-stage .schematic-stack img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 50px 80px rgba(0, 0, 0, 0.6));
  transition: opacity 600ms var(--ease-out);
}
.chapter-stage .layer-base { opacity: 1; }
.chapter-stage .layer-overlay {
  opacity: 0;
  mix-blend-mode: screen;
  filter: drop-shadow(0 0 20px color-mix(in oklab, var(--accent-terminal-green) 50%, transparent));
}
/* When the chapter is in view, fade the schematic overlay in. */
.schematic-chapter.is-in .layer-overlay { opacity: 0.9; }
.schematic-chapter.is-in .layer-base { opacity: 0.7; }

/* Annotation bullets — tiny crosshair + spec label, revealed in sequence.
 * Rendered as <ul role-list> with <li> items so AT exposes them as the
 * chapter's callout structure. List markers and indentation are reset. */
.chapter-stage .annotations {
  position: absolute;
  inset: 0;
  pointer-events: none;
  list-style: none;
  padding: 0;
  margin: 0;
}
.chapter-stage .annotation {
  position: absolute;
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--pova-white);
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity 500ms var(--ease-out),
    transform 500ms var(--ease-out);
  white-space: nowrap;
  list-style: none;
}
.chapter-stage .annotation::before {
  content: "+ ";
  color: var(--pova-orange);
  margin-right: 4px;
}
.schematic-chapter.is-in .annotation {
  opacity: 0.85;
  transform: translateY(0);
}
.schematic-chapter.is-in .annotation:nth-child(1) { transition-delay: 120ms; }
.schematic-chapter.is-in .annotation:nth-child(2) { transition-delay: 220ms; }
.schematic-chapter.is-in .annotation:nth-child(3) { transition-delay: 320ms; }
.schematic-chapter.is-in .annotation:nth-child(4) { transition-delay: 420ms; }

@media (max-width: 900px) {
  .schematic-story { margin: 0; }
  .schematic-chapter {
    grid-template-columns: 1fr;
    gap: 32px;
    padding: 80px var(--safe-tablet);
    min-height: 0;
  }
  .schematic-chapter .chapter-stage {
    position: relative;
    top: 0;
    height: auto;
    aspect-ratio: 4 / 5;
  }
  .schematic-chapter h3 { font-size: clamp(28px, 6vw, 40px); }
}
@media (max-width: 700px) {
  .chapter-stage .annotations {
    position: static;
    margin-top: 24px;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .chapter-stage .annotation {
    position: static;
    opacity: 0.85;
    transform: none;
    white-space: normal;
  }
}
@media (max-width: 600px) {
  .schematic-chapter { padding: 60px var(--safe-mobile); }
}

/* ==========================================================================
 * 5b. SCHEMATIC OVERLAY — pure-CSS mecha / engineering schematic layer
 * --------------------------------------------------------------------------
 * No new product imagery — the schematic look is a stack of CSS layers
 * (blueprint grid, technical crosshairs, dimension lines, callouts)
 * composed over the existing product render. Used inside .schematic-stack.
 * ========================================================================== */

.schematic-stack { isolation: isolate; }

/* Blueprint grid — fine 20px / coarse 80px lines, masked to the device area.
 * Color stays cool-white at low opacity so the overlay reads like CAD paper,
 * not a UI surface. */
.schematic-stack::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  background-image:
    linear-gradient(to right, rgba(168, 230, 199, 0.22) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(168, 230, 199, 0.22) 1px, transparent 1px),
    linear-gradient(to right, rgba(168, 230, 199, 0.08) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(168, 230, 199, 0.08) 1px, transparent 1px);
  background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
  mix-blend-mode: screen;
  mask-image: radial-gradient(ellipse 70% 80% at 50% 50%, #000 40%, transparent 95%);
  -webkit-mask-image: radial-gradient(ellipse 70% 80% at 50% 50%, #000 40%, transparent 95%);
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
}
.schematic-chapter.is-in .schematic-stack::before { opacity: 0.55; }

/* Crosshair / dimension scaffolding — a center-aligned plus + two
 * dimension caliper rules, drawn with conic + linear gradients only. */
.schematic-stack::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  background:
    /* horizontal dimension line, top */
    linear-gradient(to right, transparent 8%, rgba(255,255,255,0.32) 8%, rgba(255,255,255,0.32) 92%, transparent 92%) top 6% center / 100% 1px no-repeat,
    /* vertical dimension line, left */
    linear-gradient(to bottom, transparent 8%, rgba(255,255,255,0.32) 8%, rgba(255,255,255,0.32) 92%, transparent 92%) left 6% top / 1px 100% no-repeat,
    /* center crosshair — horizontal */
    linear-gradient(to right, rgba(255,93,1,0.6) 0%, rgba(255,93,1,0.6) 100%) center / 14px 1px no-repeat,
    /* center crosshair — vertical */
    linear-gradient(to bottom, rgba(255,93,1,0.6) 0%, rgba(255,93,1,0.6) 100%) center / 1px 14px no-repeat;
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 600ms var(--ease-out), transform 600ms var(--ease-out);
}
.schematic-chapter.is-in .schematic-stack::after {
  opacity: 0.65;
  transform: scale(1);
  transition-delay: 200ms;
}

/* Annotation crosshair markers — each annotation gets a tiny crosshair
 * at the start of its label, in addition to the orange "+ " prefix. */
.chapter-stage .annotation::after {
  content: "";
  position: absolute;
  top: 50%;
  left: -6px;
  width: 1px;
  height: 18px;
  background: rgba(255, 93, 1, 0.45);
  transform: translateY(-50%);
}

/* Per-chapter signal-color tinting on the schematic crosshair. The first
 * chapter (Terminal Green) uses green; the second (16-Bit White) goes
 * cool-white; the third (front view) uses Pulse Orange. */
.schematic-chapter[data-chapter="01"] .schematic-stack::before {
  background-image:
    linear-gradient(to right, rgba(90, 122, 82, 0.30) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(90, 122, 82, 0.30) 1px, transparent 1px),
    linear-gradient(to right, rgba(90, 122, 82, 0.10) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(90, 122, 82, 0.10) 1px, transparent 1px);
  background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
}
.schematic-chapter[data-chapter="02"] .schematic-stack::before {
  background-image:
    linear-gradient(to right, rgba(232, 234, 232, 0.22) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(232, 234, 232, 0.22) 1px, transparent 1px),
    linear-gradient(to right, rgba(232, 234, 232, 0.08) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(232, 234, 232, 0.08) 1px, transparent 1px);
  background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
}
.schematic-chapter[data-chapter="03"] .schematic-stack::before {
  background-image:
    linear-gradient(to right, rgba(255, 93, 1, 0.18) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 93, 1, 0.18) 1px, transparent 1px),
    linear-gradient(to right, rgba(255, 93, 1, 0.06) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 93, 1, 0.06) 1px, transparent 1px);
  background-size: 80px 80px, 80px 80px, 20px 20px, 20px 20px;
}

/* SVG-based schematic frame — drawn as a single inline SVG via background.
 * Ticks at 10% / 25% / 50% / 75% / 90% along each edge, plus a circular
 * inset ring and two diagonal sight-lines. Pure SVG-in-CSS, no extra DOM. */
.schematic-stack > .schematic-frame {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 4;
  opacity: 0;
  transition: opacity 700ms var(--ease-out) 100ms;
}
.schematic-chapter.is-in .schematic-stack > .schematic-frame { opacity: 0.7; }

/* ==========================================================================
 * 5b-bis. CHAPTERED SCROLL NARRATIVES — per-PDP visual languages.
 * --------------------------------------------------------------------------
 * Each shipping product has its own scroll signature, sharing the same
 * .narrative-story / [data-narrative-chapter] / .chapter-stage wiring
 * (so motion.js's "active chapter wins" logic works on all of them) but
 * exposing distinct visuals via a .narrative-story--<variant> modifier:
 *
 *   POVA Curve     → .narrative-story--horizon  (horizon arc framing)
 *   POVA 7 Pro     → .narrative-story--signal   (EQ-bar / spec rhythm)
 *   POVA Curve 02  → .narrative-story--orbit    (orbital starfield)
 * The POVA 8 schematic story keeps its existing .schematic-story rules.
 * ========================================================================== */

.narrative-story {
  position: relative;
  margin: 0 calc(-1 * var(--safe-desktop));
  padding: 0;
  background: #000;
}
.narrative-chapter {
  position: relative;
  min-height: 100vh;
  padding: 100px var(--safe-desktop);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  align-items: center;
}
.narrative-chapter .chapter-stage {
  position: sticky;
  top: 12vh;
  height: 76vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.narrative-chapter .chapter-stage > .stage-inner {
  position: relative;
  width: 100%;
  max-width: 480px;
  aspect-ratio: 4 / 5;
}
.narrative-chapter .chapter-stage img.product {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 50px 80px rgba(0, 0, 0, 0.6));
  transform: translateY(20px) scale(0.97);
  opacity: 0.7;
  transition:
    opacity 700ms var(--ease-out),
    transform 800ms var(--ease-out);
}
.narrative-chapter.is-in .chapter-stage img.product {
  opacity: 1;
  transform: translateY(0) scale(1);
}
/* Chapter-render variants — vary scale/crop so the same render reads as
 * a different moment per chapter (silhouette / curve / wider). Wrapping
 * <picture> takes the variant class. Per critic-w3 P1 #6 — light option:
 * use existing renders, vary framing. */
.narrative-chapter .chapter-render {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
}
.narrative-chapter .chapter-render img.product { transition: transform 1100ms var(--ease-out), opacity 700ms var(--ease-out); }
/* Curve — chapter 01 silhouette: full device, slightly smaller. */
.narrative-chapter .chapter-render--silhouette img.product { transform: translateY(20px) scale(0.78); }
.narrative-chapter.is-in .chapter-render--silhouette img.product { transform: translateY(0) scale(0.82); }
/* Curve — chapter 02 arc: scale up, offset right showing only the curve. */
.narrative-chapter .chapter-render--arc img.product { transform: translateY(20px) translateX(-22%) scale(1.45); }
.narrative-chapter.is-in .chapter-render--arc img.product { transform: translateY(0) translateX(-22%) scale(1.5); }
/* Curve — chapter 03 wider: full device centred. */
.narrative-chapter .chapter-render--wide img.product { transform: translateY(20px) scale(0.97); }
.narrative-chapter.is-in .chapter-render--wide img.product { transform: translateY(0) scale(1); }
/* Curve 02 — chapter 01 far: small + offset (silhouette in distance). */
.narrative-chapter .chapter-render--far img.product { transform: translateY(20px) translateY(8%) scale(0.62); opacity: 0.55; }
.narrative-chapter.is-in .chapter-render--far img.product { transform: translateY(8%) scale(0.66); opacity: 0.85; }
/* Curve 02 — chapter 03 near: scaled up, centred. */
.narrative-chapter .chapter-render--near img.product { transform: translateY(20px) scale(1.18); }
.narrative-chapter.is-in .chapter-render--near img.product { transform: translateY(0) scale(1.22); }
.narrative-chapter .chapter-text { max-width: 480px; }
.narrative-chapter .chapter-num {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--pova-orange);
  opacity: 0.85;
  margin-bottom: 18px;
  display: block;
}
.narrative-chapter h3 {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(32px, 4.4vw, 56px);
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0 0 24px;
  max-width: 16ch;
}
.narrative-chapter p {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: 17px;
  line-height: 1.6;
  margin: 0 0 16px;
  opacity: 0.85;
  max-width: 44ch;
}

/* ----- POVA Curve — horizon framing -----
 * A single horizon line sweeps across the stage; the curved silhouette
 * sits as the arc that completes it. Three chapters around the curvature. */
.narrative-story--horizon { background: #000; }
.narrative-story--horizon .narrative-chapter::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: linear-gradient(to right,
    transparent 0%,
    rgba(255, 93, 1, 0.18) 15%,
    rgba(255, 93, 1, 0.32) 50%,
    rgba(255, 93, 1, 0.18) 85%,
    transparent 100%);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1200ms var(--ease-out);
  z-index: 0;
}
.narrative-story--horizon .narrative-chapter.is-in::before { transform: scaleX(1); }
.narrative-story--horizon .stage-inner::after {
  content: "";
  position: absolute;
  left: -10%;
  right: -10%;
  bottom: 8%;
  height: 60%;
  background: radial-gradient(ellipse 60% 70% at 50% 100%,
    color-mix(in oklab, var(--pova-orange) 18%, transparent) 0%,
    transparent 75%);
  z-index: -1;
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
}
.narrative-story--horizon .narrative-chapter.is-in .stage-inner::after { opacity: 1; }

/* ----- POVA 7 Pro — signal/spec rhythm framing -----
 * Each chapter pulses around a single number. Engineering-drawing motif:
 * hairline grid + measurement ticks + crosshair markers around the figure.
 * Mono type for the figure unit; Mint Glow stays the page accent.
 * Ties the page into POVA 8's blueprint family. NO audio-EQ register. */
.narrative-story--signal {
  background:
    linear-gradient(180deg, rgba(156, 229, 212, 0.04) 0%, transparent 60%),
    #000;
}
.narrative-story--signal .stage-inner {
  position: relative;
  width: 100%;
  height: 100%;
  display: block;
}
/* Hairline grid — fades in as the chapter activates. 12-column light grid
 * over the stage, rendered as two linear-gradient layers. */
.narrative-story--signal .stage-grid {
  position: absolute;
  inset: 8% 6%;
  background-image:
    linear-gradient(to right,
      rgba(156, 229, 212, 0.10) 0,
      rgba(156, 229, 212, 0.10) 1px,
      transparent 1px,
      transparent calc(100% / 12)),
    linear-gradient(to bottom,
      rgba(156, 229, 212, 0.06) 0,
      rgba(156, 229, 212, 0.06) 1px,
      transparent 1px,
      transparent calc(100% / 8));
  background-size: calc(100% / 12) 100%, 100% calc(100% / 8);
  opacity: 0;
  transform: scale(1.02);
  transition: opacity 800ms var(--ease-out), transform 1000ms var(--ease-out);
  pointer-events: none;
}
.narrative-story--signal .narrative-chapter.is-in .stage-grid {
  opacity: 1;
  transform: scale(1);
}
/* Bottom dimension rule — a single hairline scale-bar with tick marks
 * scrubs across the stage on chapter activation. Industrial-drawing
 * register, not audio. */
.narrative-story--signal .stage-rule {
  position: absolute;
  left: 8%;
  right: 8%;
  bottom: 12%;
  height: 14px;
  pointer-events: none;
}
.narrative-story--signal .stage-rule::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(156, 229, 212, 0.55);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1100ms var(--ease-out);
}
.narrative-story--signal .narrative-chapter.is-in .stage-rule::before {
  transform: scaleX(1);
}
.narrative-story--signal .stage-rule .tick {
  position: absolute;
  left: var(--tx, 0%);
  bottom: 0;
  width: 1px;
  height: 10px;
  background: rgba(156, 229, 212, 0.55);
  transform: translateX(-0.5px) scaleY(0);
  transform-origin: bottom center;
  transition: transform 600ms var(--ease-out);
  transition-delay: calc(400ms + var(--tx, 0%) * 4);
}
.narrative-story--signal .narrative-chapter.is-in .stage-rule .tick {
  transform: translateX(-0.5px) scaleY(1);
}
/* Figure block — centered in the stage, with crosshair schematic markers
 * at each corner. The figure is a numeric lockup, treated like a
 * dimensioned drawing callout. */
.narrative-story--signal .stage-figure {
  position: absolute;
  left: 50%;
  top: 44%;
  transform: translate(-50%, -50%);
  text-align: center;
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(72px, 12vw, 168px);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--pova-white);
  pointer-events: none;
  padding: 28px 36px;
}
.narrative-story--signal .stage-figure .unit {
  font-family: var(--font-mono);
  font-size: 0.18em;
  letter-spacing: 0.4em;
  display: block;
  margin-top: 12px;
  color: var(--accent-mint-glow);
  opacity: 0.85;
  text-transform: uppercase;
}
.narrative-story--signal .stage-figure .dim-label {
  display: block;
  margin-top: 18px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.4em;
  color: rgba(255, 255, 255, 0.4);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 600ms var(--ease-out) 600ms, transform 600ms var(--ease-out) 600ms;
}
.narrative-story--signal .narrative-chapter.is-in .stage-figure .dim-label {
  opacity: 1;
  transform: translateY(0);
}
/* Crosshair schematic markers — corner brackets framing the figure as
 * if it were a dimensioned drawing callout. 14×14px L-shapes drawn with
 * border on two edges. */
.narrative-story--signal .stage-figure .crosshair {
  position: absolute;
  width: 14px;
  height: 14px;
  border: 1px solid rgba(156, 229, 212, 0.6);
  opacity: 0;
  transition: opacity 500ms var(--ease-out) 350ms;
}
.narrative-story--signal .stage-figure .crosshair--tl { top: 0;    left: 0;    border-right: 0; border-bottom: 0; }
.narrative-story--signal .stage-figure .crosshair--tr { top: 0;    right: 0;   border-left: 0;  border-bottom: 0; }
.narrative-story--signal .stage-figure .crosshair--bl { bottom: 0; left: 0;    border-right: 0; border-top: 0; }
.narrative-story--signal .stage-figure .crosshair--br { bottom: 0; right: 0;   border-left: 0;  border-top: 0; }
.narrative-story--signal .narrative-chapter.is-in .stage-figure .crosshair { opacity: 1; }

/* Figure number — static text rendered from data-figure attr; the chapter
 * activation lifts it into view (translate + fade). Keeps it accessible
 * (the value lives in HTML, not CSS) and avoids the @property <integer>
 * + counter-reset gymnastics that have spotty Safari support. */
.narrative-story--signal .stage-figure .figure-num {
  display: inline-block;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 600ms var(--ease-out) 200ms, transform 700ms var(--ease-out) 200ms;
}
.narrative-story--signal .stage-figure .figure-num::after {
  content: attr(data-figure);
}
.narrative-story--signal .narrative-chapter.is-in .stage-figure .figure-num {
  opacity: 1;
  transform: translateY(0);
}

/* ----- POVA Curve 02 — orbital framing -----
 * Stage fills with a sparse starfield; the device is haloed by a SOLID
 * hairline orbital ring that traces in as the chapter activates. Three
 * chapters as orbital approach (far, mid, near). Astronomical, not
 * dot/pixel-LED — solid ring instead of dashed segments; five fixed
 * stars per chapter (no twinkle) per memory feedback_pova_visual_system. */
.narrative-story--orbit {
  background:
    radial-gradient(ellipse 80% 60% at 50% 0%,
      rgba(122, 126, 132, 0.10) 0%, transparent 60%),
    #000;
}
.narrative-story--orbit .stage-inner::before {
  /* Solid hairline orbital ring — masked to a thin annulus around the
   * device. No dashes. Reads unambiguously as orbit/planet, not LED. */
  content: "";
  position: absolute;
  inset: -8%;
  border-radius: 50%;
  background: rgba(232, 234, 232, 0.55);
  -webkit-mask-image: radial-gradient(circle, transparent 50%, #000 50.5%, #000 51.5%, transparent 52%);
  mask-image: radial-gradient(circle, transparent 50%, #000 50.5%, #000 51.5%, transparent 52%);
  opacity: 0;
  transform: rotate(-30deg);
  transition: opacity 800ms var(--ease-out), transform 1500ms var(--ease-out);
}
.narrative-story--orbit .narrative-chapter.is-in .stage-inner::before {
  opacity: 0.85;
  transform: rotate(0deg);
}
.narrative-story--orbit .stage-inner::after {
  /* faint planet glow behind the device */
  content: "";
  position: absolute;
  inset: 12%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    color-mix(in oklab, var(--accent-storm-titanium) 40%, transparent) 0%,
    transparent 60%);
  z-index: -1;
}
.narrative-story--orbit .orbit-stars {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* Fixed stars — astronomical, not pixelated. No twinkle (per critic-w3
 * P2: dotted twinkling reads as LED). Subtle opacity pulse on .star-pulse
 * variant only, very slow, on no more than two stars per chapter. */
.narrative-story--orbit .orbit-stars span {
  position: absolute;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.7);
  box-shadow: 0 0 6px rgba(255, 255, 255, 0.35);
  opacity: 0.7;
}
.narrative-story--orbit .orbit-stars span.star-pulse {
  animation: orbit-pulse 7s var(--ease-in-out) infinite;
}
@media (prefers-reduced-motion: reduce) {
  .orbit-stars span,
  .orbit-stars span.star-pulse,
  .narrative-story--orbit .stage-inner::before,
  .narrative-story--horizon .narrative-chapter::before,
  .narrative-story--signal .stage-grid,
  .narrative-story--signal .stage-rule::before,
  .narrative-story--signal .stage-rule .tick { animation: none !important; transition: none !important; }
}

/* Shared mobile collapse for narrative chapters */
@media (max-width: 900px) {
  .narrative-story { margin: 0; }
  .narrative-chapter {
    grid-template-columns: 1fr;
    gap: 32px;
    padding: 80px var(--safe-tablet);
    min-height: 0;
  }
  .narrative-chapter .chapter-stage {
    position: relative;
    top: 0;
    height: auto;
    aspect-ratio: 4 / 5;
  }
  .narrative-chapter h3 { font-size: clamp(28px, 6vw, 40px); }
  .narrative-story--signal .stage-figure { font-size: clamp(56px, 16vw, 96px); top: 50%; padding: 18px 22px; }
  .narrative-story--signal .stage-grid { inset: 6% 4%; }
  .narrative-story--signal .stage-rule { left: 6%; right: 6%; bottom: 8%; }
}
@media (max-width: 600px) {
  .narrative-chapter { padding: 60px var(--safe-mobile); }
}

/* ==========================================================================
 * 5c. KINETIC TYPOGRAPHIC LOCKUP — masked text reveal on scroll-driven
 *     display headlines. Apply .masked-reveal to any heading you want
 *     to scrub in on first paint or on scroll-into-view. JS toggles
 *     .is-revealed when the element enters the viewport (motion.js).
 * ========================================================================== */
/* .masked-reveal — was a linear-gradient text reveal animation. The mask
 * was clipping FH Lecturis glyphs at scale (visible glyph-cuts on the
 * about H1 and other display lockups). Neutralised: the class stays in
 * the HTML so no markup churn, but no mask is applied. Plain headings. */
.masked-reveal { -webkit-mask-image: none; mask-image: none; }

/* ==========================================================================
 * 5d. ENGINEERED SPEC TREATMENT — replaces / supplements .spec-table.
 *     Engineered-looking row layout: monospace label · dot-leader fill ·
 *     scroll-revealed value. Like a technical drawing's data block.
 *     Drop into .spec-block in HTML. Reveals via .reveal observer.
 * ========================================================================== */
.spec-block {
  display: flex;
  flex-direction: column;
  gap: 0;
  font-family: var(--font-mono);
  font-size: var(--fs-mono-md);
  letter-spacing: 0.04em;
  border-top: 1px solid rgba(255, 255, 255, 0.10);
}
.spec-row {
  display: grid;
  grid-template-columns: 16ch 1fr auto;
  align-items: baseline;
  gap: 16px;
  padding: 18px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  opacity: 0;
  transform: translateY(8px);
  transition:
    opacity 600ms var(--ease-out),
    transform 600ms var(--ease-out);
}
.spec-row.is-in,
.no-js .spec-row { opacity: 1; transform: none; }
.spec-row .k {
  text-transform: uppercase;
  opacity: 0.55;
  letter-spacing: 0.2em;
  font-size: var(--fs-mono-xs);
}
.spec-row .leader {
  display: block;
  height: 1px;
  border-bottom: 1px dashed rgba(255, 255, 255, 0.18);
  align-self: end;
  margin-bottom: 6px;
}
.spec-row .v {
  color: var(--pova-white);
  text-align: right;
  font-size: var(--fs-mono-md);
  white-space: nowrap;
}
.spec-row .v .unit {
  opacity: 0.55;
  margin-left: 4px;
  font-size: var(--fs-mono-xs);
}
.spec-row + .spec-row { transition-delay: 60ms; }
.spec-row + .spec-row + .spec-row { transition-delay: 120ms; }
.spec-row + .spec-row + .spec-row + .spec-row { transition-delay: 180ms; }
.spec-row + .spec-row + .spec-row + .spec-row + .spec-row { transition-delay: 240ms; }
@media (max-width: 600px) {
  .spec-row {
    grid-template-columns: 1fr auto;
    gap: 4px 16px;
    padding: 14px 0;
  }
  .spec-row .leader { display: none; }
}

/* ==========================================================================
 * 5e. PRODUCT FAMILY CONSTELLATION — products.html interactive node graph.
 *     Four products as nodes, connected by hairlines. Hover/tap a node to
 *     highlight its connections. Pure CSS — JS adds .is-active toggles.
 * ========================================================================== */
.constellation {
  position: relative;
  width: 100%;
  max-width: 920px;
  margin: 0 auto;
  aspect-ratio: 16 / 9;
  pointer-events: none;
}
.constellation svg.lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
.constellation .node {
  position: absolute;
  width: 130px;
  height: 130px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(15, 15, 17, 0.6);
  backdrop-filter: blur(12px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  transition:
    border-color var(--t-med) var(--ease-out),
    transform var(--t-med) var(--ease-out),
    box-shadow var(--t-med) var(--ease-out);
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  color: var(--pova-white);
  overflow: hidden;
}
.constellation .node .node-render {
  display: block;
  width: 60%;
  height: 60%;
  position: relative;
  margin-bottom: 2px;
}
.constellation .node .node-render img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Greyscale silhouette by default; full-colour on activation. */
  filter: grayscale(1) brightness(1.05) contrast(1.05);
  transition: filter var(--t-med) var(--ease-out);
}
.constellation .node:hover,
.constellation .node:focus-visible,
.constellation .node.is-active {
  border-color: var(--pova-orange);
  transform: scale(1.06);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--pova-orange) 30%, transparent),
              0 18px 40px rgba(0, 0, 0, 0.45);
  outline: none;
}
.constellation .node:hover .node-render img,
.constellation .node:focus-visible .node-render img,
.constellation .node.is-active .node-render img {
  filter: grayscale(0) brightness(1) contrast(1);
}
.constellation .node .label {
  font-family: var(--font-mono);
  font-size: var(--fs-mono-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0.85;
  padding: 2px 4px 4px;
}
.constellation .node[data-pos="a"] { top: 8%;  left: 4%;  }
.constellation .node[data-pos="b"] { top: 8%;  right: 4%; }
.constellation .node[data-pos="c"] { bottom: 8%; left: 18%; }
.constellation .node[data-pos="d"] { bottom: 8%; right: 18%; }

.constellation svg.lines line {
  stroke: rgba(255, 255, 255, 0.18);
  stroke-width: 1;
  transition: stroke 320ms var(--ease-out), stroke-width 320ms var(--ease-out);
}
/* Subtle global lift when hovering anywhere in the constellation —
 * but only the active node's connecting edges get the orange tint. JS
 * sets data-active="<pos>" on the constellation root, the rules below
 * highlight exactly the four edges touching that node. */
.constellation:hover svg.lines line,
.constellation:focus-within svg.lines line { stroke: rgba(255, 255, 255, 0.32); }
.constellation[data-active="a"] svg.lines line[data-edge="ab"],
.constellation[data-active="a"] svg.lines line[data-edge="ac"],
.constellation[data-active="a"] svg.lines line[data-edge="ad"],
.constellation[data-active="b"] svg.lines line[data-edge="ab"],
.constellation[data-active="b"] svg.lines line[data-edge="bd"],
.constellation[data-active="b"] svg.lines line[data-edge="bc"],
.constellation[data-active="c"] svg.lines line[data-edge="ac"],
.constellation[data-active="c"] svg.lines line[data-edge="cd"],
.constellation[data-active="c"] svg.lines line[data-edge="bc"],
.constellation[data-active="d"] svg.lines line[data-edge="bd"],
.constellation[data-active="d"] svg.lines line[data-edge="cd"],
.constellation[data-active="d"] svg.lines line[data-edge="ad"] {
  stroke: var(--pova-orange);
  stroke-width: 1.5;
}

/* Mobile — 2x2 grid of clickable cards, hairline frame retained. */
@media (max-width: 700px) {
  .constellation {
    aspect-ratio: auto;
    max-width: 360px;
    height: auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: 12px;
    padding: 18px;
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: var(--glass-radius);
    pointer-events: auto;
  }
  .constellation svg.lines {
    /* Hairline X across the grid — keeps the connection idea on mobile. */
    inset: 18px;
    opacity: 0.5;
  }
  .constellation .node {
    position: relative;
    top: auto !important;
    bottom: auto !important;
    left: auto !important;
    right: auto !important;
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    border-radius: var(--glass-radius);
    min-height: 88px;
  }
  .constellation .node .node-render { width: 50%; height: 50%; }
}

/* ==========================================================================
 * 5f. CTA-LINK SYSTEM — used on 404, 500, shop cart-empty, anywhere we
 *     need a buy-bar-shaped link outside the buy bar. .cta-link is the
 *     base; .cta-primary fills with Pulse Orange, .cta-secondary outlines
 *     in white. Replaces the wave-2 inline style blobs.
 * ========================================================================== */
.cta-link {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 14px 24px;
  border-radius: 4px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--fs-mono-md);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  min-height: 44px;
  text-decoration: none;
  transition: transform var(--t-fast) var(--ease-out), filter var(--t-fast) var(--ease-out);
  cursor: pointer;
  border: 1px solid transparent;
  background: transparent;
  color: var(--pova-white);
}
.cta-link.cta-primary {
  background: var(--pova-orange);
  color: var(--pova-black);
  border-color: var(--pova-orange);
}
.cta-link.cta-primary:hover { filter: brightness(1.06); transform: translateY(-2px); }
.cta-link.cta-secondary {
  border-color: rgba(255, 255, 255, 0.18);
  color: var(--pova-white);
}
.cta-link.cta-secondary:hover { border-color: var(--pova-orange); }

/* Error-page CTA cluster — flex row that wraps on mobile. */
.error-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 36px;
}

/* 404 / 500 hero size — replaces the inline style="min-height:86vh" blob. */
.error-hero { min-height: 86vh; }

/* Generic CTA-paragraph spacing — lifts a CTA-link off the body copy
 * above. Replaces wave-3 inline style="margin-top:24px;" on shop. */
.cta-row { margin-top: 24px; }

/* ==========================================================================
 * 6. UTILITY — visually hidden, JSON-LD inert, etc
 * ========================================================================== */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* When JS is loaded, body gets .js — use to gate progressive-enhancement
 * features (so no-JS visitors still see the content). */
.no-js .reveal { opacity: 1; transform: none; }
.no-js .scrub-in .char { opacity: 1; transform: none; animation: none; }
