/* ==========================================================================
   Thyra Tech — motion & effect layer.
   style.css owns the design system (tokens, type, layout, components).
   This file owns everything that moves or overlays. Kept separate so the
   design system stays readable and so this layer can be swapped or dropped
   without touching the site's structure.
   ========================================================================== */

/* ---------- Intro curtain ----------
   FAIL-SAFE BY CONSTRUCTION. The curtain is `display: none` by default and
   only becomes visible when the inline head script adds `html.intro`. That
   ordering matters: if JavaScript is disabled, blocked, or 404s, the class
   is never added, the curtain never renders, and the site is simply visible.
   The inverse (visible by default, hidden by JS) would mean one failed
   request leaves a permanent black screen over the whole site. The head
   script also arms a watchdog that tears the intro down if the sequence
   never reports completion. Do not "simplify" this to a JS-hides-it model. */
.curtain { display: none; }
html.intro .curtain {
  position: fixed; inset: 0; z-index: 250;
  display: grid; place-items: center;
  background: var(--color-ink);
  overflow: hidden;
}
html.intro .stage { transform: scale(.92); opacity: 0; filter: blur(6px); }
.stage { transform-origin: 50% 45%; }

.curtain-skip {
  position: absolute; bottom: 6vh; left: 50%; transform: translateX(-50%);
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--color-neutral-ink);
  padding: .85rem 1.2rem; min-height: 44px;
}
.curtain-skip:hover, .curtain-skip:focus-visible { color: var(--color-accent); }

/* ---------- Blueprint door (the chosen intro) ---------- */
.bp-wrap { position: relative; width: min(70vw, 380px); aspect-ratio: 1; perspective: 1400px; }
.bp-svg { width: 100%; height: 100%; overflow: visible; }
.bp-outline { fill: none; stroke: var(--color-accent-2); stroke-width: 2; stroke-dasharray: 1600; stroke-dashoffset: 1600; }
.bp-fill { fill: var(--color-accent); opacity: 0; }
.bp-guide { stroke: rgba(79,140,255,.4); stroke-width: 1; stroke-dasharray: 4 5; opacity: 0; }
.bp-dim { fill: rgba(79,140,255,.75); font-family: var(--font-mono); font-size: 13px; letter-spacing: .12em; opacity: 0; }
.bp-caption {
  position: absolute; left: 50%; bottom: -3rem; transform: translateX(-50%);
  font-family: var(--font-mono); font-size: .62rem; letter-spacing: .3em;
  text-transform: uppercase; color: var(--color-neutral-ink); white-space: nowrap; opacity: 0;
}

/* ---------- Background line field ----------
   Sits behind content in dark sections. Purely decorative, aria-hidden, and
   never intercepts pointer events. Fills what was dead space at the top of
   dark sections without adding another moving thing to track. */
.linefield { position: absolute; inset: 0; z-index: 0; pointer-events: none; width: 100%; height: 100%; }
.linefield path, .linefield line, .linefield polyline { fill: none; vector-effect: non-scaling-stroke; }
.lf-warm { stroke: rgba(255,90,31,.42); }
.lf-warm-faint { stroke: rgba(255,90,31,.18); }
.lf-cool { stroke: rgba(79,140,255,.34); }
.lf-node { fill: rgba(255,90,31,.85); }
.lf-node-cool { fill: rgba(79,140,255,.8); }

.hero-motif, .page-hero, .flow-section { position: relative; overflow: hidden; }
.page-hero .container, .hero-motif .container, .flow-section .container { position: relative; z-index: 2; }

/* ---------- Cursor spotlight ---------- */
.spotlight {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(420px circle at var(--sx,50%) var(--sy,40%), rgba(255,90,31,.16), transparent 60%);
  opacity: 0; transition: opacity .4s ease;
}
/* The home hero has its own door light. A second, cursor-driven flare here
   makes the first screen feel busy, so reserve the spotlight for interiors. */
.hero-motif .spotlight { display: none; }

/* ---------- Circuit spine (scroll progress) ---------- */
#page-circuit { position: fixed; top: 0; right: 18px; width: 3px; height: 100vh; z-index: 60; pointer-events: none; }
#page-circuit .track { stroke: rgba(244,242,239,.12); stroke-width: 2; }
#page-circuit .fill { stroke: var(--color-accent); stroke-width: 2; filter: drop-shadow(0 0 6px rgba(255,90,31,.7)); }
@media (max-width: 900px) { #page-circuit { display: none; } }

/* ---------- 3D tilt cards ---------- */
.tilt-card { perspective: 900px; }
.tilt-card > * { transform-style: preserve-3d; }
/* Tilt is mousemove-driven (effects.js) and only ever runs on a fine pointer
   while the card is hovered, so the compositor hint belongs on :hover rather
   than sitting on every card for the whole visit. preserve-3d stays
   unconditional — it establishes the 3D context, it isn't a perf hint. */
.tilt-card:hover > * { will-change: transform; }

/* ---------- Button scan + glitch ---------- */
.btn, .btn--outline { position: relative; overflow: hidden; }
.btn::before, .btn--outline::before {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(180deg, transparent 0%, rgba(255,255,255,.35) 50%, transparent 100%);
  transform: translateY(-100%);
}
@keyframes scan { to { transform: translateY(100%); } }
.glitch-text { position: relative; display: inline-block; }
@keyframes glitch {
  0% { transform: translate(0,0); } 20% { transform: translate(-1px,1px); }
  40% { transform: translate(1px,-1px); } 60% { transform: translate(-1px,0); }
  80% { transform: translate(1px,0); } 100% { transform: translate(0,0); }
}

/* ---------- Flow console (homepage "how it works") ---------- */
.flow-grid { display: grid; grid-template-columns: minmax(0,1fr) minmax(360px,.92fr); gap: clamp(2rem,5vw,5rem); align-items: center; }
@media (max-width: 900px) { .flow-grid { grid-template-columns: 1fr; } }
.flow-console { position: relative; width: 100%; border: 1px solid rgba(244,242,239,.16); background: linear-gradient(150deg, rgba(244,242,239,.05), rgba(244,242,239,.015)); box-shadow: 18px 28px 60px rgba(0,0,0,.35); }
.flow-console-top { display: flex; justify-content: space-between; padding: .85rem 1.1rem; border-bottom: 1px solid rgba(244,242,239,.14); font-family: var(--font-mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .13em; color: var(--color-neutral-ink); }
.flow-console-top span:last-child { color: var(--color-accent-2); }
.flow-steps { position: relative; padding: 1.85rem clamp(1.1rem,4vw,2rem) 2.1rem; display: flex; flex-direction: column; gap: 1.85rem; }
.flow-step { position: relative; display: flex; gap: 1rem; align-items: flex-start; }
/* align-self: stretch is load-bearing — without it the rail collapses to the
   dot's height and the connector renders as a stub instead of reaching the
   next dot. Every step gets an identical line, including the last. */
.flow-rail { position: relative; width: 20px; flex-shrink: 0; display: flex; justify-content: center; align-self: stretch; }
.flow-rail::before { content: ""; position: absolute; top: 22px; bottom: -1.85rem; left: 50%; width: 1px; background: rgba(244,242,239,.16); transform: translateX(-50%); }
.flow-dot { --dot: var(--color-accent); --dot-rgb: 255,90,31; width: 10px; height: 10px; margin-top: 6px; border-radius: 50%; background: var(--dot); box-shadow: 0 0 0 4px rgba(var(--dot-rgb),.16); flex-shrink: 0; }
.flow-dot--two { --dot: var(--color-accent-2); --dot-rgb: 79,140,255; }
@media (prefers-reduced-motion: no-preference) { .flow-dot { animation: flow-pulse 2.6s ease-in-out infinite; } }
@keyframes flow-pulse { 0%,100% { box-shadow: 0 0 0 4px rgba(var(--dot-rgb),.16); } 50% { box-shadow: 0 0 0 7px rgba(var(--dot-rgb),.05); } }
/* --color-accent, not --color-accent-ink: accent-ink is the darkened variant
   for light backgrounds and measures 3.42:1 here, below the AA floor. */
.flow-step-body .node-index { font-family: var(--font-mono); font-size: .72rem; color: var(--color-accent); letter-spacing: .1em; }
.flow-step-body strong { display: block; margin: .35rem 0 .3rem; font-size: 1.15rem; font-family: var(--font-body); font-weight: 600; color: var(--color-paper); }
.flow-step-body small { display: block; font-size: .85rem; line-height: 1.45; color: var(--color-neutral-ink); }
.flow-chips { display: flex; flex-wrap: wrap; gap: .75rem; padding: .25rem clamp(1.1rem,4vw,2rem) 0; }
.flow-chip { padding: .4rem .6rem; border: 1px solid currentColor; font-family: var(--font-mono); font-size: .62rem; text-transform: uppercase; letter-spacing: .11em; }
.flow-chip--one { color: var(--color-accent); }
.flow-chip--two { color: var(--color-accent-2); }
.flow-readout { padding: 1.1rem clamp(1.1rem,4vw,2rem) 1.3rem; font-family: var(--font-mono); font-size: .62rem; text-transform: uppercase; letter-spacing: .08em; color: var(--color-neutral-ink); }
.readout-dot { display: inline-block; width: .5rem; height: .5rem; margin-right: .45rem; background: var(--color-accent); border-radius: 50%; box-shadow: 0 0 0 .26rem rgba(255,90,31,.16); }

/* ---------- Split-flap hosts ---------- */
.cap-line { display: flex; align-items: baseline; flex-wrap: wrap; gap: .6em; margin: 0 0 1.6em; }
.cap-label { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .28em; text-transform: uppercase; color: var(--color-neutral-ink); }
.cap-flap { --sf-gap: .14em; --sf-tile-bg: #17171d; --sf-tile-w: .7em; --sf-radius: 2px; font-size: clamp(.72rem, 1.9vw, .95rem); }
.cap-flap .sf-row { flex-wrap: wrap; row-gap: .14em; }

/* NOTE the compound selectors (`.num.sf`, not `.num .sf`). SplitFlap adds
   the `.sf` class to the host element itself rather than wrapping in a
   child, so a descendant selector never matches — that silently left these
   tiles on the default paper color instead of the accent. */
.num.sf, .step-label.sf, .stat-word.sf { --sf-gap: .1em; --sf-tile-w: .68em; --sf-radius: 2px; }
.num.sf .sf-tile { --sf-tile-bg: rgba(8,8,10,.06); color: var(--color-accent-ink); }
.section--ink .num.sf .sf-tile, .section--ink-alt .num.sf .sf-tile { --sf-tile-bg: rgba(244,242,239,.07); color: var(--color-accent); }
.step-label.sf .sf-tile { --sf-tile-bg: rgba(244,242,239,.07); color: var(--color-neutral-ink); font-size: .72rem; }
/* Stat words run smaller than the display-serif version they replace: mono
   tiles carry far more visual weight per character, and these are meant to
   be a readout accent on an interior page, not the page's loudest element. */
.stat-word.sf { font-size: clamp(1.05rem, 2vw, 1.35rem); }
.stat-word.sf .sf-tile { --sf-tile-bg: rgba(244,242,239,.07); color: var(--color-accent); }

/* ---------- Mobile motion budget ----------
   Hover effects are gated on a real pointer, not width: a touch browser
   fires :hover on tap and leaves it stuck, so ungated these would trigger on
   tap rather than simply not firing. Small looping animations are cut below
   640px, where the caret, status dot, flow dots and ticker would otherwise
   all sit inside one thumb-length of screen. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover::before, .btn--outline:hover::before { animation: scan .6s ease; }
  .btn:hover .glitch-text, .btn--outline:hover .glitch-text { animation: glitch .35s steps(2) 1; }
  .flow-section:hover .spotlight, .page-hero:hover .spotlight { opacity: 1; }
}
/* Circuit routes depend on fixed SVG coordinates, while phone copy is fluid.
   On compact screens, retain the brand's warm/cool atmosphere with soft light
   pools instead of geometry that could land on a word after any text wrap. */
@media (max-width: 900px) {
  .hero-motif .linefield { display: none; }
  .hero-motif::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(80% 48% at 105% 0%, rgba(255,90,31,.18) 0%, rgba(255,90,31,.06) 42%, transparent 75%),
      radial-gradient(76% 44% at -12% 100%, rgba(79,140,255,.15) 0%, rgba(79,140,255,.04) 44%, transparent 72%),
      linear-gradient(145deg, transparent 46%, rgba(255,90,31,.04) 100%);
  }
}
@media (max-width: 700px) {
  .page-hero .linefield { display: none; }
  .page-hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(80% 48% at 105% 0%, rgba(255,90,31,.18) 0%, rgba(255,90,31,.06) 42%, transparent 75%),
      radial-gradient(76% 44% at -12% 100%, rgba(79,140,255,.15) 0%, rgba(79,140,255,.04) 44%, transparent 72%),
      linear-gradient(145deg, transparent 46%, rgba(255,90,31,.04) 100%);
  }
}
@media (max-width: 640px) {
  .flow-dot { animation: none !important; }
  .eyebrow .dot { animation: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .spotlight, #page-circuit { display: none; }
  html.intro .stage { transform: none; opacity: 1; filter: none; }
}
