/* ==========================================================================
   nodal — animation layer
   --------------------------------------------------------------------------
   1. .anim-slot          containers reserved for custom animations
   2. .slot-poster--*     static fallbacks shown until an animation mounts
   3. [data-reveal]       scroll reveal (only when JS is on)
   4. marquee, hero portal, small built-ins
   5. Debug: add #slots to the URL to outline every slot with its name.
   Motion tokens (--dur-fast, --dur-base, --ease-out) live in components.css.
   ========================================================================== */

/* ---------- 1. Slots ---------- */
.anim-slot { position: relative; overflow: hidden; background: var(--surface-sunken); }
.anim-slot--fill { position: absolute; inset: 0; background: transparent; pointer-events: none; }
.anim-slot--wide { aspect-ratio: 16 / 10; }
.anim-slot--square { aspect-ratio: 1 / 1; border: var(--border-hair) solid var(--line); }
.anim-slot--card { aspect-ratio: 16 / 9; }
.anim-slot > canvas, .anim-slot > svg.anim-layer, .anim-slot > .anim-layer { position: absolute; inset: 0; width: 100%; height: 100%; }
/* When an animation mounts, main.js sets data-anim-state="running"; the poster fades out. */
.anim-slot[data-anim-state="running"] > .slot-poster { opacity: 0; }

/* ---------- 2. Posters (static, on-brand, theme-aware) ---------- */
.slot-poster { position: absolute; inset: 0; transition: opacity 400ms var(--ease-out); }
.slot-poster--portal { position: absolute; inset: 22%; width: 56%; height: auto; margin: auto; }

/* Quantised gradient bands with hairline cuts. */
.slot-poster--bands {
  background:
    repeating-linear-gradient(90deg, transparent 0 39px, var(--surface-sunken) 39px 40px),
    linear-gradient(90deg, var(--nodal-indigo) 0 34%, var(--nodal-blue) 34% 64%, var(--nodal-cyan) 64% 84%, var(--surface-raised) 84% 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 55%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 55%);
}
.slot-poster--reverse { transform: scaleX(-1); }

/* Mesh: a structured grid like a CFD mesh. */
.slot-poster--mesh {
  background:
    radial-gradient(60% 80% at 30% 60%, color-mix(in srgb, var(--nodal-indigo) 45%, transparent), transparent 70%),
    radial-gradient(50% 70% at 75% 40%, color-mix(in srgb, var(--nodal-cyan) 35%, transparent), transparent 70%),
    repeating-linear-gradient(0deg, transparent 0 23px, var(--line) 23px 24px),
    repeating-linear-gradient(90deg, transparent 0 23px, var(--line) 23px 24px);
}

/* Dot lattice: nodes. */
.slot-poster--dots {
  background:
    radial-gradient(circle, var(--accent) 1.5px, transparent 2px) 0 0 / 24px 24px;
  -webkit-mask-image: radial-gradient(55% 60% at 50% 50%, #000, transparent);
          mask-image: radial-gradient(55% 60% at 50% 50%, #000, transparent);
  opacity: .8;
}

/* Plus grid, for semiconductors / layouts. */
.slot-poster--grid {
  background:
    linear-gradient(90deg, var(--nodal-indigo), var(--nodal-cyan)) center / 40% 40% no-repeat,
    repeating-linear-gradient(0deg, transparent 0 15px, var(--line) 15px 16px),
    repeating-linear-gradient(90deg, transparent 0 15px, var(--line) 15px 16px);
}


/* ---------- 3. Scroll reveal ---------- */
.js [data-reveal] { opacity: 0; transform: translateY(16px); transition: opacity 600ms var(--ease-out), transform 600ms var(--ease-out); }
.js [data-reveal].is-inview { opacity: 1; transform: none; }
/* Stagger children: set --reveal-delay inline or via main.js. */
.js [data-reveal] { transition-delay: var(--reveal-delay, 0ms); }

/* ---------- 4. Built-ins ---------- */
/* Marquee: main.js duplicates the track; the animation moves exactly one copy. */
.marquee__track { animation: marquee var(--marquee-duration, 40s) linear infinite; }
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* Hero portal: slow rotation. */
.hero__portal { animation: portal-spin 120s linear infinite; }
@keyframes portal-spin { to { transform: translate(-50%, -46%) rotate(360deg); } }

/* Highlight wipe on load: the indigo block draws left to right. */
.js .hero__title .nd-highlight { background-size: 0% 100%, 100% 3px; background-image: linear-gradient(var(--highlight), var(--highlight)), var(--nodal-gradient); background-repeat: no-repeat; background-position: 0 0, 0 100%; background-color: transparent; transition: background-size 900ms var(--ease-out) 300ms, color 300ms 300ms; }
.js .hero__title.is-inview .nd-highlight { background-size: 100% 100%, 100% 3px; }

/* Engine pulse on the product portal poster. */
.slot-poster--portal { animation: node-pulse 3.2s var(--ease-out) infinite; }
@keyframes node-pulse { 0%, 100% { opacity: .75; transform: scale(.98); } 50% { opacity: 1; transform: scale(1.02); } }

/* ---------- 5. Debug outlines (#slots) ---------- */
.show-slots [data-anim-slot] { outline: 1px dashed var(--accent); outline-offset: -1px; }
.show-slots [data-anim-slot]::after { content: attr(data-anim-slot); position: absolute; left: 8px; top: 8px; z-index: 10; padding: 2px 6px; background: var(--action); color: var(--on-action); font: 500 11px/16px var(--font-mono); letter-spacing: .06em; text-transform: uppercase; }
.show-slots .nd-highlight[data-anim-slot], .show-slots .nd-code[data-anim-slot], .show-slots .footer__portal { position: relative; }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .marquee__track, .hero__portal, .slot-poster--portal { animation: none !important; }
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
  .js .hero__title .nd-highlight { background-size: 100% 100%, 100% 3px; transition: none; }
  /* Reduced motion keeps the hero poster frame (main.js never plays the video). */
}
