/* ============================================================================
   G. RENCH — theme.css
   The design system. Everything downstream (landing + wave-2 sub-pages) pulls
   its palette, type, texture and shared components from this one file.

   Design language is derived directly from the cover art:
   night navy, cream moon, muted crimson coat, moss-green undersuit,
   warm window gold, snow, ash, blood accent, heavy paper-cut collage grain.

   Sections:
     1.  @font-face (self-hosted variable fonts, local fallbacks)
     2.  :root design tokens (palette, derived surfaces, type scale, space, motion)
     3.  Reset + base
     4.  Typography (display spaced-caps, serif body, links)
     5.  Layout helpers (container, section rhythm)
     6.  Texture utilities (paper grain, vignette, torn-edge divider)
     7.  Component: string-lights divider (one dead bulb, CSS flicker)
     8.  Component: green LED (steady blink)
     9.  Buttons, form controls, cards, tooltips
     10. Scroll-reveal + motion, prefers-reduced-motion fallbacks
   Shared-module contract: wave-2 pages link this file read-only and must not
   redefine these tokens or component class names.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1. FONTS  (variable woff2, Latin subset, self-hosted — no runtime CDN)
   Josefin Sans  → display / spaced caps (the cover title face)
   Source Serif 4 → bookish serif body
   --------------------------------------------------------------------------- */
@font-face {
  font-family: "Josefin Sans";
  font-style: normal;
  font-weight: 100 700;              /* variable weight axis */
  font-display: swap;
  src: url("../fonts/josefin-sans-var-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 200 900;              /* variable weight axis */
  font-display: swap;
  src: url("../fonts/source-serif-4-var-latin.woff2") format("woff2");
}

/* ---------------------------------------------------------------------------
   2. TOKENS
   --------------------------------------------------------------------------- */
:root {
  /* -- Core palette (spec §4, taken from the cover) -- */
  --navy:            #0c1420;   /* night sky */
  --navy-deep:       #080e17;   /* deeper shadow */
  --navy-black:      #05080e;   /* the darkest ink */
  --cream:           #e8ddc4;   /* the moon */
  --crimson:         #a8352f;   /* Santa coat, muted */
  --moss:            #8a9631;   /* the grinch-green undersuit */
  --gold:            #e2a13d;   /* window gold */
  --snow:            #f2ede2;   /* snowfall */
  --ash:             #5a5a57;   /* ash gray */
  --char:            #2c2c2a;   /* charred fleck */
  --blood:           #7d1a1a;   /* the drip */

  /* -- Tints / shades derived from the core -- */
  --cream-dim:       #c9bfa6;   /* muted cream */
  --cream-70:        rgba(232, 221, 196, 0.70);
  --cream-45:        rgba(232, 221, 196, 0.45);
  --cream-20:        rgba(232, 221, 196, 0.20);
  --cream-10:        rgba(232, 221, 196, 0.10);
  --cream-06:        rgba(232, 221, 196, 0.06);
  --gold-glow:       rgba(226, 161, 61, 0.55);
  --crimson-glow:    rgba(168, 53, 47, 0.45);
  --moss-glow:       rgba(138, 150, 49, 0.50);

  /* -- Surfaces (page depth) -- */
  --bg:              var(--navy);
  --bg-deep:         var(--navy-deep);
  --surface:         #101a28;   /* raised paper panel */
  --surface-2:       #16212f;   /* hover / active panel */
  --line:            var(--cream-20);   /* hairline borders */
  --line-soft:       var(--cream-10);

  /* -- Ink (text) -- */
  --ink:             var(--cream);            /* primary text */
  --ink-soft:        var(--cream-70);         /* secondary */
  --ink-faint:       var(--cream-45);         /* captions / meta */
  --accent:          var(--gold);             /* links, highlights */
  --accent-warm:     var(--crimson);          /* danger / heat */

  /* -- Type families -- */
  --font-display: "Josefin Sans", "Century Gothic", "Futura",
                  "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  --font-body:    "Source Serif 4", "Iowan Old Style", "Palatino Linotype",
                  "Georgia", "Times New Roman", serif;
  --font-mono:    ui-monospace, "SF Mono", "Menlo", "Consolas", monospace;

  /* -- Fluid type scale (clamp: min / preferred / max) -- */
  --fs-hero: clamp(2.9rem, 1.6rem + 6.4vw, 8rem);
  --fs-900:  clamp(2.1rem, 1.4rem + 3.2vw, 4rem);
  --fs-800:  clamp(1.7rem, 1.3rem + 1.9vw, 2.6rem);
  --fs-700:  clamp(1.4rem, 1.2rem + 1.1vw, 1.95rem);
  --fs-600:  clamp(1.2rem, 1.1rem + 0.5vw, 1.45rem);
  --fs-500:  clamp(1.06rem, 1.0rem + 0.3vw, 1.2rem);
  --fs-400:  clamp(1.0rem, 0.97rem + 0.15vw, 1.08rem);   /* body */
  --fs-300:  0.9rem;
  --fs-200:  0.8rem;
  --fs-100:  0.72rem;

  --lh-tight:   1.08;
  --lh-snug:    1.28;
  --lh-body:    1.62;
  --tracking-caps: 0.34em;    /* the cover's wide letterspacing */
  --tracking-wide: 0.16em;
  --tracking-mid:  0.06em;

  /* -- Spacing scale -- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;
  --space-9: 6.5rem;
  --section-y: clamp(3.5rem, 2rem + 7vw, 8rem);   /* vertical section rhythm */

  /* -- Layout -- */
  --measure:   68ch;          /* readable line length */
  --container:  min(1200px, 92vw);
  --container-narrow: min(760px, 90vw);

  /* -- Radii, borders -- */
  --radius-sm: 3px;
  --radius:    6px;
  --radius-lg: 12px;

  /* -- Elevation -- */
  --shadow-1: 0 1px 2px rgba(0,0,0,0.4);
  --shadow-2: 0 10px 30px -12px rgba(0,0,0,0.7);
  --shadow-3: 0 28px 60px -20px rgba(0,0,0,0.85);

  /* -- Motion -- */
  --ease-cold: cubic-bezier(0.22, 0.61, 0.36, 1);   /* slow settle */
  --ease-drift: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-1: 0.28s;
  --dur-2: 0.6s;
  --dur-3: 1.1s;

  /* -- Z layers (documented so wave-2 pages stay consistent) -- */
  --z-grain:    2;
  --z-content:  5;
  --z-snow:     40;    /* the shared snow canvas: in front of art, behind UI */
  --z-header:   60;
  --z-tooltip:  80;
  --z-skip:     100;

  /* -- Torn-edge mask (a seamless tiling ragged strip).
        preserveAspectRatio='none' is required: without it the SVG letterboxes
        inside the mask tile and leaves gaps between repeats. -- */
  --torn-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='40' viewBox='0 0 180 40' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='M0 8 L15 5 L30 11 L45 4 L60 9 L75 6 L90 12 L105 5 L120 10 L135 7 L150 11 L165 6 L180 8 L180 30 L165 34 L150 28 L135 33 L120 29 L105 35 L90 30 L75 33 L60 28 L45 34 L30 29 L15 33 L0 30 Z'/%3E%3C/svg%3E");

  /* -- Paper grain (fractal noise, desaturated, tiles) -- */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------------------
   3. RESET + BASE
   --------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  min-height: 100%;
  background-color: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-400);
  line-height: var(--lh-body);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;   /* clip (not hidden): hidden would promote overflow-y to
                         auto, make body a scroll container, and trap page scroll */
  /* the page background: a cold vertical gradient over navy */
  background-image:
    radial-gradient(120% 80% at 50% -10%, #16233a 0%, transparent 55%),
    linear-gradient(180deg, var(--navy) 0%, var(--navy-deep) 60%, var(--navy-black) 100%);
  background-attachment: fixed;
  transition: background-color var(--dur-3) var(--ease-cold);
}

img, svg, canvas, video { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
:where(input, textarea, select) { font: inherit; color: inherit; }

::selection { background: var(--crimson); color: var(--snow); }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Skip link */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: var(--z-skip);
  background: var(--gold); color: var(--navy-black);
  padding: 0.6em 1em; font-family: var(--font-display);
  letter-spacing: var(--tracking-wide); text-transform: uppercase; font-size: var(--fs-200);
}
.skip-link:focus { left: var(--space-4); top: var(--space-4); }

/* visually hidden but available to screen readers (stays hidden on focus,
   unlike .skip-link) — use for off-screen form labels, etc. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   4. TYPOGRAPHY
   --------------------------------------------------------------------------- */
.display, h1, h2, .h {
  font-family: var(--font-display);
  font-weight: 300;
  line-height: var(--lh-tight);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ink);
}
/* the cover's title treatment: thin, wide, cream */
.display--hero {
  font-size: var(--fs-hero);
  font-weight: 300;
  letter-spacing: clamp(0.14em, 0.34em, 0.4em);
}
h2, .h2 { font-size: var(--fs-900); letter-spacing: var(--tracking-wide); }
h3, .h3 {
  font-family: var(--font-display); font-weight: 400;
  font-size: var(--fs-700); letter-spacing: var(--tracking-wide);
  text-transform: uppercase; line-height: var(--lh-snug);
}
.eyebrow {
  font-family: var(--font-display);
  font-weight: 400; font-size: var(--fs-200);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--gold);
}
p { max-width: var(--measure); }
.lede {
  font-size: var(--fs-600);
  line-height: var(--lh-snug);
  color: var(--ink-soft);
}
.prose > * + * { margin-top: var(--space-4); }
.prose p { color: var(--ink-soft); }
strong, b { color: var(--ink); font-weight: 600; }
em, i { font-style: italic; }

/* Inline text link */
a.link, .prose a {
  color: var(--accent);
  text-decoration: none;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-size: 0% 1px;
  background-position: 0 1.15em;
  background-repeat: no-repeat;
  transition: background-size var(--dur-1) var(--ease-drift), color var(--dur-1);
}
a.link:hover, a.link:focus-visible, .prose a:hover {
  background-size: 100% 1px;
  color: var(--snow);
}

/* ---------------------------------------------------------------------------
   5. LAYOUT
   --------------------------------------------------------------------------- */
.container { width: var(--container); margin-inline: auto; }
.container--narrow { width: var(--container-narrow); margin-inline: auto; }
.section { position: relative; padding-block: var(--section-y); }
.section > .container { position: relative; z-index: var(--z-content); }
.stack > * + * { margin-top: var(--space-5); }
.center { text-align: center; }
.measure { max-width: var(--measure); }

/* ---------------------------------------------------------------------------
   6. TEXTURE
   --------------------------------------------------------------------------- */
/* full-page paper grain overlay (one fixed layer) */
.grain-overlay {
  position: fixed; inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  background-image: var(--grain);
  background-size: 150px 150px;
  opacity: 0.055;
  mix-blend-mode: overlay;
}
/* local grain on a panel */
.has-grain { position: relative; }
.has-grain::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: var(--grain); background-size: 150px 150px;
  opacity: 0.06; mix-blend-mode: overlay; border-radius: inherit;
}

/* torn-edge divider band: a torn paper seam. --torn-fill sets its tone; the
   drop-shadow adds a faint cream lip so it reads as a lifted paper edge. */
.torn-edge {
  --torn-fill: var(--navy-black);
  height: 30px; width: 100%;
  background-color: var(--torn-fill);
  -webkit-mask-image: var(--torn-mask);
  mask-image: var(--torn-mask);
  -webkit-mask-size: 180px 100%;
  mask-size: 180px 100%;
  -webkit-mask-repeat: repeat-x;
  mask-repeat: repeat-x;
  filter: drop-shadow(0 -1.5px 0 var(--cream-10));
}
.torn-edge--flip { transform: scaleY(-1); }
.torn-edge--cream { --torn-fill: var(--cream); opacity: 0.9; }
.torn-edge--crimson { --torn-fill: var(--crimson); }

/* ---------------------------------------------------------------------------
   7. STRING-LIGHTS DIVIDER
   A sagging wire (stretchy SVG) with bulbs hung along a single shallow catenary.
   Bulbs are round DOM elements positioned by --x / --y so they never distort.
   Exactly one bulb carries .bulb--dead: dark, no flicker.

   Markup contract (wave-2 pages copy this verbatim):
     <div class="string-lights" role="presentation">
       <svg class="string-lights__wire" viewBox="0 0 1200 80" preserveAspectRatio="none" aria-hidden="true">
         <path d="M0 8 Q 600 78 1200 8" />
       </svg>
       <span class="bulb" style="--x:0%;--y:8px;--c:var(--gold)"></span>
       ... (bulbs; give ONE the class bulb--dead) ...
     </div>
   The .string-lights-row helper below auto-lays a default 12-bulb string if you
   just drop 12 empty <span class="bulb"> children (positions from --i/--n).
   --------------------------------------------------------------------------- */
.string-lights {
  position: relative;
  width: 100%;
  height: 92px;
  --wire: var(--cream-20);
  isolation: isolate;
}
.string-lights__wire {
  position: absolute; inset: 0; width: 100%; height: 80px;
  overflow: visible;
}
.string-lights__wire path {
  fill: none;
  stroke: var(--wire);
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
}
.bulb {
  position: absolute;
  left: var(--x, 50%);
  top: var(--y, 40px);
  width: 12px; height: 12px;
  margin-left: -6px;
  border-radius: 50%;
  background: var(--c, var(--gold));
  /* tiny stem up to the wire */
  box-shadow: 0 0 8px 1px var(--c, var(--gold)),
              0 0 18px 3px color-mix(in srgb, var(--c, var(--gold)) 55%, transparent);
  transform-origin: center top;
  animation: bulb-flicker 5.5s var(--ease-drift) infinite;
  animation-delay: calc(var(--i, 0) * -0.47s);
  will-change: opacity, filter;
}
.bulb::before {   /* the little stem */
  content: ""; position: absolute; left: 50%; top: -7px;
  width: 1px; height: 7px; margin-left: -0.5px;
  background: var(--wire);
}
.bulb--dead {
  background: #2a2f27;
  box-shadow: inset 0 0 3px rgba(0,0,0,0.8);
  animation: none;
  opacity: 0.9;
}
@keyframes bulb-flicker {
  0%, 100%   { opacity: 1;    filter: brightness(1); }
  8%         { opacity: 0.85; filter: brightness(0.9); }
  9%         { opacity: 1;    filter: brightness(1.15); }
  46%        { opacity: 0.95; }
  47%        { opacity: 0.4;  filter: brightness(0.6); }  /* a stutter */
  48%        { opacity: 0.95; }
  70%, 82%   { opacity: 1; }
  76%        { opacity: 0.7;  filter: brightness(0.85); }
}

/* auto-layout helper: 12 evenly spaced bulbs on the default catenary,
   positions computed from --i (index) and --n (count). */
.string-lights-row .bulb {
  --n: 12;
  --p: calc(var(--i) / (var(--n) - 1));          /* 0 .. 1 */
  /* inset the ends by 3% so the end bulbs never poke past the viewport edge */
  --h: calc(0.03 + var(--p) * 0.94);             /* actual horizontal fraction (matches left) */
  left: calc(var(--h) * 100%);
  /* Sit each bulb ON the wire's real quadratic. Wire path M0 8 Q600 78 1200 8
     in an 80px-tall box resolves to y = 8 + 140·h(1-h); the 7px stem drops the
     bulb top just below the wire so it kisses the string. */
  --wire-sag: calc(var(--h) * (1 - var(--h)));   /* 0 .. 0.25 */
  top: calc(15px + 140px * var(--wire-sag));
}

/* ---------------------------------------------------------------------------
   8. GREEN LED  (the Meridian box light — steady blink everywhere)
   --------------------------------------------------------------------------- */
.green-led {
  --led: var(--moss);
  display: inline-block;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--led);
  box-shadow: 0 0 6px 1px var(--led), 0 0 12px 2px var(--moss-glow);
  animation: led-blink 2s steps(1, end) infinite;
  vertical-align: middle;
}
@keyframes led-blink {
  0%, 44%  { opacity: 1;   box-shadow: 0 0 6px 1px var(--led), 0 0 12px 2px var(--moss-glow); }
  45%, 100%{ opacity: 0.28; box-shadow: 0 0 2px 0 var(--led); }
}
/* .green-led.is-skipping (toggled by JS) holds the light through one beat */
.green-led.is-skipping { animation: none; opacity: 0.28; }

/* ---------------------------------------------------------------------------
   9. CONTROLS: buttons, forms, cards, tooltips
   --------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.6em;
  font-family: var(--font-display);
  font-weight: 400; font-size: var(--fs-200);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.85em 1.6em;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink);
  background: transparent;
  transition: background-color var(--dur-1) var(--ease-drift),
              border-color var(--dur-1), color var(--dur-1),
              transform var(--dur-1), box-shadow var(--dur-1);
}
.btn:hover, .btn:focus-visible {
  border-color: var(--gold);
  color: var(--snow);
  box-shadow: 0 0 0 1px var(--gold-glow), 0 10px 30px -14px var(--gold-glow);
  transform: translateY(-1px);
}
.btn--primary {
  background: linear-gradient(180deg, #b53c35, var(--crimson));
  border-color: transparent; color: var(--snow);
}
.btn--primary:hover, .btn--primary:focus-visible {
  box-shadow: 0 0 0 1px var(--crimson-glow), 0 14px 34px -14px var(--crimson-glow);
  filter: brightness(1.06);
}
.btn--gold {
  background: linear-gradient(180deg, #edb256, var(--gold));
  border-color: transparent; color: var(--navy-black);
}
.btn--ghost { border-color: var(--line-soft); color: var(--ink-soft); }

/* Card / paper panel */
.card {
  position: relative;
  background: linear-gradient(180deg, var(--surface), var(--surface-2));
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-2);
}

/* Form controls */
.field {
  display: flex; gap: var(--space-2); flex-wrap: wrap;
  align-items: stretch;
}
/* The UA's [hidden] { display:none } loses to the flex rule above; without
   this, a form set hidden after a successful subscribe stays visible next to
   its own success message. Class+attribute outranks the bare class. */
.field[hidden] { display: none; }
.input {
  flex: 1 1 14rem;
  background: var(--navy-deep);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.85em 1em;
  color: var(--ink);
  font-family: var(--font-body);
  transition: border-color var(--dur-1), box-shadow var(--dur-1);
}
.input::placeholder { color: var(--ink-faint); }
.input:focus-visible {
  outline: none;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px var(--gold-glow);
}

/* Tooltip (shared, JS-positioned; used by the roofline windows) */
.tooltip {
  position: fixed;
  z-index: var(--z-tooltip);
  max-width: 15rem;
  padding: 0.5em 0.75em;
  font-family: var(--font-body);
  font-size: var(--fs-200);
  font-style: italic;
  line-height: 1.35;
  color: var(--cream);
  background: rgba(8, 14, 23, 0.94);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--dur-1), transform var(--dur-1);
}
.tooltip.is-visible { opacity: 1; transform: translateY(0); }

/* Maxine — the paper-cut footer dog (injected by shared.js dogWalker).
   The .dog element's translateX is owned by JS (scroll position); the gait,
   bob and tail live here. */
.dog {
  position: absolute;
  left: 0; bottom: 8px;
  width: clamp(70px, 8vw, 112px);
  pointer-events: none;
  z-index: var(--z-content);
}
.dog__svg { width: 100%; height: auto; overflow: visible; }
.dog--walking .dog__svg { animation: dog-bob 0.52s ease-in-out infinite; }
.dog__leg { transform-box: fill-box; transform-origin: 50% 0%; }
.dog--walking .dog__leg--fl,
.dog--walking .dog__leg--br { animation: dog-trot-a 0.52s ease-in-out infinite; }
.dog--walking .dog__leg--fr,
.dog--walking .dog__leg--bl { animation: dog-trot-b 0.52s ease-in-out infinite; }
.dog--walking .dog__tail { transform-box: fill-box; transform-origin: 0% 50%;
  animation: dog-wag 0.9s ease-in-out infinite; }
@keyframes dog-trot-a { 0%,100% { transform: rotate(13deg); } 50% { transform: rotate(-13deg); } }
@keyframes dog-trot-b { 0%,100% { transform: rotate(-13deg); } 50% { transform: rotate(13deg); } }
@keyframes dog-bob   { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
@keyframes dog-wag   { 0%,100% { transform: rotate(-8deg); } 50% { transform: rotate(10deg); } }

/* item 12: Maxine speaks — a paper-cut speech bubble (cream on navy) + a perk. */
.dog-bubble {
  position: absolute; bottom: calc(100% + 8px);
  /* --bubble-shift nudges the whole bubble so it never runs off either viewport
     edge (measured + set by shared.js after render); --bubble-tail counter-shifts
     the tail so it still points down at Maxine. The shift rides on `left` (a
     normal property) rather than inside the transform, so the % + px offset is
     applied reliably; `transform` stays a pure centering translate. */
  left: calc(50% + var(--bubble-shift, 0px));
  transform: translateX(-50%) translateY(5px);
  transform-origin: bottom center;
  width: max-content; max-width: min(320px, 78vw);
  padding: 0.5em 0.75em;
  font-family: var(--font-body); font-size: var(--fs-200); font-style: italic;
  line-height: 1.32; text-align: center; color: var(--cream);
  background: var(--navy-deep);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  opacity: 0; pointer-events: none; z-index: var(--z-tooltip);
  transition: opacity var(--dur-1) var(--ease-drift), transform var(--dur-1) var(--ease-drift);
}
.dog-bubble.is-visible { opacity: 1; transform: translateX(-50%) translateY(0); }
.dog-bubble::before, .dog-bubble::after {   /* a small tail pointing down at her */
  content: ""; position: absolute; left: calc(50% + var(--bubble-tail, 0px)); top: 100%;
  transform: translateX(-50%); border: 6px solid transparent;
}
.dog-bubble::before { border-top-color: var(--line); }
.dog-bubble::after { margin-top: -1.4px; border-top-color: var(--navy-deep); }
.dog--speaking .dog__svg { animation: dog-perk 0.6s var(--ease-drift) 1; }
@keyframes dog-perk {
  0% { transform: translateY(0) rotate(0); }
  30% { transform: translateY(-3px) rotate(-4deg); }
  60% { transform: translateY(0) rotate(3deg); }
  100% { transform: translateY(0) rotate(0); }
}
.dog--speaking .dog__ear { transform-box: fill-box; transform-origin: 60% 20%;
  animation: dog-earflick 0.55s ease 1; }
@keyframes dog-earflick { 0%,100% { transform: rotate(0); } 45% { transform: rotate(-22deg); } }
.dog--speaking .dog__tail { animation: dog-wag 0.34s ease-in-out 2; }
@media (prefers-reduced-motion: reduce) {
  .dog--speaking .dog__svg,
  .dog--speaking .dog__ear,
  .dog--speaking .dog__tail { animation: none; }
}

/* ---------------------------------------------------------------------------
   10. SCROLL-REVEAL + MOTION
   --------------------------------------------------------------------------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--dur-3) var(--ease-cold),
              transform var(--dur-3) var(--ease-cold);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
[data-reveal].is-revealed { opacity: 1; transform: none; }

/* Shared snow canvas default styling (pages just add <canvas class="snowfield">) */
.snowfield {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  z-index: var(--z-snow);
  pointer-events: none;
}

/* Reduced motion: freeze everything to a calm static state. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .bulb { opacity: 1 !important; }
  .green-led { opacity: 1 !important; }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}
