/* ============================================================================
   theme.css — deck "standard-text-slide"  (THE STANDARD TEXT SLIDE)

   This is the canonical house text slide: a title + a short left-to-right-wiping
   underline over a centred, per-click, character-by-character bullet reveal.
   See docs/STANDARD-TEXT-SLIDE.md. (The text-fx-bullets demo deck shares this
   theme; keep the two in sync, treating this file as the source of truth.)

   THE EFFECT. Bullets use the SAME per-character focus-in as the titles — each
   glyph fades and lifts a hair into place, staggered — but with NO blur. Blur is
   the title's signature; the body text just fades in, character by character.
   Each bullet ENTERS ON ITS OWN CLICK: it is a `.frag`, and the engine adds the
   `.frag-in` reveal-state class to that fragment on the click that triggers it,
   which starts its glyphs staggering in. So the points arrive one per click, each
   doing the character reveal. The title focus-in is CSS keyed off `.slide.active`.

   THE LAYOUT. Title + bullets are one centred group. Every bullet keeps its box
   from the start (only the glyphs are hidden), so the group is laid out and
   balanced immediately and grows outward as lines/bullets are added — the house
   default for title+bullets.

   Authoring: `.fx-char` spans carry `--i` (glyph index) and drive the stagger. A
   title wraps them in `.fx-line`; a bullet is `<li class="fx-bullet frag">` with
   the spans directly inside, and the slide sets `clicks: N` (one per bullet) in
   frontmatter. Bold glyphs get `.fx-em`.
   ============================================================================ */

:root{
  /* --- palette --- */
  --deck-bg:        #000510;
  --deck-ink:       #f9fafb;
  --deck-ink-dim:   #9aa3b8;
  --deck-ink-faint: #7e879c;
  --deck-accent:    #a9c0ea;

  --deck-card:      rgba(255,255,255,.028);
  --deck-card-bd:   rgba(255,255,255,.075);

  /* --- title reveal (with blur) --- */
  --fx-char-dur: 920ms;
  --fx-stagger: 50ms;

  /* --- bullet reveal (no blur) --- */
  --fx-bchar-dur: 520ms;   /* per-glyph fade duration */
  --fx-bstagger: 15ms;     /* glyph-to-glyph delay within a bullet */

  /* --- title underline wipe (left -> right, a touch slower than the title) --- */
  --fx-wipe-dur: 1500ms;
  --fx-wipe-delay: 300ms;
}

/* ---- centred hero-style moments (intro / close) ---- */
.fx-stage{
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  gap: 2.6cqh;
  padding: 8cqh 10cqw;
}
.fx-stage .fx-title{ font-size: clamp(3rem, 8cqw, 6.2rem); }
.fx-eyebrow{
  font-size: 1.5cqw; font-weight: 500; letter-spacing: .28em; text-transform: uppercase;
  color: var(--deck-accent); margin: 0;
}
.fx-subtitle{
  font-size: 2cqw; font-weight: 300; color: var(--deck-ink-dim); margin: 0;
  max-width: 60cqw;
}

/* ============================================================================
   BALANCED TITLE + BULLETS  (centre-anchored; grows outward)
   A single centred column: title, hairline divider, list. justify-content:center
   keeps the whole group balanced vertically; every line is laid out from the
   start (only the glyphs fade in), so nothing jumps and the group stays centred
   no matter how many bullets the author adds.
   ============================================================================ */
.fx-stage--bullets{
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  gap: 3cqh;
  padding: 6cqh 8cqw;
}
.fx-stage--bullets .fx-title{ font-size: clamp(2.4rem, 5.4cqw, 4.5rem); }

/* Title underline: a #4054b2 bar that wipes in left -> right (a touch slower than the
   title). The <div> is the deck's one `.anim` element (so `.frag` bullets stay hidden until
   clicked); we hold it steady and run the wipe on a ::after so the engine's default anim
   fade doesn't fight it. */
.fx-divider{
  position: relative;
  width: 19.5cqw; height: 2px;
  background: transparent;
  opacity: 1 !important;        /* neutralise the engine's .anim opacity fade */
  transform: none !important;   /* ...and its translateY, so only the wipe moves */
}
.fx-divider::after{
  content: ""; position: absolute; inset: 0;
  /* solid #4054b2 held across the left ~40%, then a shorter (less gradual) fade to
     transparent on the right */
  background: linear-gradient(to right, #4054b2 0%, #4054b2 40%, rgba(64, 84, 178, 0) 100%);
  transform: scaleX(0); transform-origin: left center;
}
.slide.active .fx-divider::after{
  animation: fxWipe var(--fx-wipe-dur) cubic-bezier(.22,1,.36,1) both;
  animation-delay: var(--fx-wipe-delay);
}
@keyframes fxWipe{ from { transform: scaleX(0); } to { transform: scaleX(1); } }

.fx-bullets{
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; align-items: center; gap: 2.2cqh;
  max-width: 84cqw;
}
.fx-bullet{
  font-size: 3.2cqw; line-height: 1.4; color: var(--deck-ink);
}
.fx-em{ color: var(--deck-ink); font-weight: 500; }

/* whisper of depth behind the full-screen title moments */
.fx-glow{
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 60% at 50% 50%, rgba(249,250,251,.07), transparent 70%);
  filter: blur(10px);
  opacity: 0;
  transition: opacity 1.4s ease;
}
.slide.active .fx-glow{ opacity: 1; }

/* ============================================================================
   THE CHARACTER REVEAL
   Shared span; two flavours. Titles focus in through a blur; bullets do the same
   rise + fade WITHOUT the blur, and start on a per-bullet beat.
   ============================================================================ */
.fx-title{ margin: 0; }
.fx-line{ position: relative; display: inline-block; max-width: 92cqw; }
/* Glyphs are inline-block (so they can transform), which would let a line break between any
   two of them. Grouping them into `.fx-word` (nowrap) means lines only break at the spaces
   between words — never mid-word. */
.fx-word{ display: inline-block; white-space: nowrap; }
.fx-char{ display: inline-block; opacity: 0; }

/* Titles and bullets: Segoe UI, light weight (the body text should read light, not heavy). */
.fx-title, .fx-bullet{
  font-family: "Segoe UI", "Segoe UI Web (West European)", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  font-weight: 300;
}

/* title — with blur */
.slide.active .fx-title .fx-char{
  animation: fxFocusIn var(--fx-char-dur) cubic-bezier(.16,1,.3,1) both;
  animation-delay: calc(var(--i) * var(--fx-stagger));
}
@keyframes fxFocusIn{
  from { opacity: 0; transform: translateY(2px); filter: blur(12px); }
  to   { opacity: 1; transform: translateY(0);    filter: blur(0);    }
}

/* bullets — same rise + fade, no blur. Each bullet is a `.frag`, so it enters on ITS OWN
   click: the engine adds `.frag-in` to that fragment, which triggers its glyphs to stagger
   in from opacity 0. Nothing animates until the fragment is triggered. */
.slide.active .fx-bullet.frag-in .fx-char{
  animation: fxCharIn var(--fx-bchar-dur) cubic-bezier(.16,1,.3,1) both;
  animation-delay: calc(var(--i) * var(--fx-bstagger));
}
@keyframes fxCharIn{
  from { opacity: 0; transform: translateY(5px); }
  to   { opacity: 1; transform: translateY(0);   }
}
/* Hold the fragment box still and let the glyphs do the reveal: neutralise the engine's
   default block fade/rise on the <li> so only the characters animate. (Reduced-motion keeps
   the engine's plain block reveal instead — see below.) */
@media (prefers-reduced-motion: no-preference){
  .fx-bullet.frag{ opacity: 1 !important; transform: none !important; }
}

@media (prefers-reduced-motion: reduce){
  .fx-char{ opacity: 1; animation: none !important; filter: none; transform: none; }
  .fx-divider::after{ transform: scaleX(1); animation: none !important; }
  .fx-glow{ transition: none; }
}
