/* =========================================================
   ELENA PARR — DESIGN SYSTEM TOKENS
   ========================================================= */
:root {
  /* Color — WARM JEWEL palette (locked 2026-08-02). Full colour throughout;
     the old grayscale-to-colour narrative was retired. */
  --color-bg: #FBFAF7;        /* warm white (Porcelain) */
  --color-bg-alt: #F7F4F0;    /* Parchment — alternating sections (Elena's pick,
                                 Coolors fbfaf7-f7f4f0, 2026-08-02). Reads as the same
                                 warm white one step down, not a separate colour.
                                 Replaced #F3ECE8 pink-gray (cool undertone read as an
                                 "ugly gray" in big flat areas) and a trial #F5EFE1 ivory
                                 (leaned too gold). */
  --color-ink: #421106;       /* Rich Mahogany — text, headings, footer */
  --color-ink-soft: #5A4A44;  /* warm brown-grey — body copy */

  /* Named brand accents */
  --color-plum: #786BA0;      /* Vintage Lavender — main purple ANCHOR: nav, dark sections, buttons, titles (Elena's pick; ~4.7:1 w/ cream text) */
  --color-plum-smoke: #887BAB;/* Amethyst Smoke — lighter mid-tone in the purple ramp (kept for gradients/accents) */
  --color-plum-dark: #675A8C; /* darker Vintage Lavender for button hover/pressed states */
  --color-plum-deep: #3B2A51; /* Dark Amethyst — deepest: hero sketch outline + fine ink details (Elena) */
  --color-plum-mauve: #574C77; /* readable amethyst-family body text on light (lilac is too light to read) */
  --color-copper: #AE7222;    /* Copperwood — PRIMARY metallic accent: links, underlines */
  --color-gold-pop: #DE962B;  /* bright orangey-gold — achievement popup titles (Elena; eyeballed from swatch) */
  --color-lilac: #DECEF5;     /* decorative only (too light for text) — it's lit-up plum */
  /* (Bubblegum-as-primary was tested 2026-08-04 and dropped — see the
     bubblegum-accent-not-primary memory. The --color-pop token was unused, removed.) */

  /* Legacy aliases remapped so existing rules cascade to the new palette.
     'teal' is retired — those usages (eyebrows, focus ring) now read as plum. */
  --color-accent-purple: var(--color-plum);
  --color-accent-gold: var(--color-copper);
  --color-accent-teal: var(--color-plum);

  --color-border: rgba(66, 17, 6, 0.12);
  --color-card-bg: #FFFDF9;

  /* Type */
  /* FONT SYSTEM — 2 families (hard rule, don't add more). */
  --font-display: "Bebas Neue", sans-serif;  /* ALL headers — tall condensed caps, "bolded" via text-stroke (Bebas ships one weight) */
  --font-body: "Work Sans", sans-serif;       /* body, subheads, UI */

  /* Spacing scale (8px base) */
  --sp-1: 0.5rem;
  --sp-2: 1rem;
  --sp-3: 1.5rem;
  --sp-4: 2rem;
  --sp-6: 3rem;
  --sp-8: 4rem;
  --sp-12: 6rem;
  --sp-16: 8rem;

  /* Radius & shadow */
  --radius-art: 30px;
  --radius-card: 20px;
  --radius-pill: 999px;
  --shadow-soft: 0 10px 30px rgba(46, 42, 36, 0.10), 0 2px 8px rgba(46, 42, 36, 0.06);
  --shadow-lift: 0 18px 40px rgba(46, 42, 36, 0.16), 0 4px 12px rgba(46, 42, 36, 0.08);

  /* Motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast: 0.3s;
  --dur-med: 0.5s;
  --dur-slow: 0.9s;
}

@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;
  }
}

/* =========================================================
   RESET & BASE
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; overflow-x: hidden; }
body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden; /* full-bleed elements (100vw technique, e.g. the film reel) can otherwise cause the whole page to scroll sideways */
}
img { max-width: 100%; display: block; }
a { color: inherit; }
h1, h2, h3 { margin: 0; }
p { margin: 0 0 var(--sp-2); }

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--color-ink);
  color: var(--color-bg);
  padding: var(--sp-1) var(--sp-2);
  z-index: 1000;
}
.skip-link:focus {
  left: var(--sp-2);
  top: var(--sp-2);
}

:focus-visible {
  outline: 3px solid var(--color-accent-teal);
  outline-offset: 3px;
}

.eyebrow {
  font-family: var(--font-body);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: 0.75rem;
  color: var(--color-lilac); /* scheme C (2026-08-02): soft "lit-up plum" eyebrow — intentionally
                                faint; eyebrows are supplementary labels, the big Bebas title carries meaning */
  margin-bottom: var(--sp-1);
}

.section-title {
  font-family: var(--font-display); /* Bebas Neue */
  text-transform: uppercase;
  font-weight: 400; /* Bebas ships a single weight */
  -webkit-text-stroke: 0.018em currentColor; /* "bolds" Bebas — no real bold weight exists */
  font-size: clamp(3rem, 9vw, 7.5rem); /* display-scale — echoes the oversized Connect header
      (which reads at ~3.6× the old 4.25rem cap and fills ~¾ of the width). Bumped 2026-08-03
      so section titles land as *moments*, not corner labels. Long titles wrap to 2 confident lines. */
  line-height: 1.0;
  letter-spacing: 0.01em;
  color: var(--color-plum); /* scheme C (2026-08-02): plum titles site-wide */
  margin-bottom: var(--sp-3);
}

.section-intro {
  max-width: 62ch;
  font-size: 1.05rem;
  color: var(--color-plum-mauve); /* scheme C (2026-08-02): readable plum-family body site-wide */
  margin-bottom: var(--sp-8);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  padding: 0.85em 1.8em;
  border-radius: var(--radius-pill);
  border: none;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
  text-decoration: none;
}
.btn--primary {
  background: var(--color-accent-purple);
  color: #fff;
  box-shadow: var(--shadow-soft);
}
.btn--primary:hover {
  background: var(--color-plum-dark);
  transform: translateY(-2px);
  box-shadow: var(--shadow-lift);
}

/* =========================================================
   HEADER / NAV
   ========================================================= */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: rgba(120, 107, 160, 0.92); /* Royal Plum, translucent — leans into the inverted theme */
  backdrop-filter: blur(8px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.site-header.is-scrolled {
  border-bottom-color: rgba(222, 206, 245, 0.35); /* faint lilac hairline */
  box-shadow: 0 4px 16px rgba(30, 8, 20, 0.18);
}
.header-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: var(--sp-2) var(--sp-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.logo-mark {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.35rem;
  text-decoration: none;
  color: #FBFAF7; /* cream logo on plum */
}
.primary-nav {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  font-size: 0.92rem;
  font-weight: 500;
}
.primary-nav a {
  text-decoration: none;
  color: #E7C9DC; /* light lilac links on plum */
  position: relative;
  transition: color var(--dur-fast) var(--ease-out);
}
.primary-nav a:hover { color: #FBFAF7; }
.nav-cta {
  background: var(--color-lilac);
  color: var(--color-plum-dark) !important; /* lilac pill, dark-plum label — pops on the plum bar */
  font-weight: 700;
  padding: 0.5em 1.2em;
  border-radius: var(--radius-pill);
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.nav-cta:hover { background: #E7C9DC; transform: translateY(-2px); }

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--sp-1);
}
.nav-toggle span {
  width: 24px;
  height: 2px;
  background: #FBFAF7; /* cream hamburger on plum */
  transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}

/* =========================================================
   HERO
   ========================================================= */
.hero {
  position: relative;
  display: block;
  padding-top: 0;
  overflow: hidden;
}

.hero-bg-illustration {
  position: relative;
  z-index: 0;
  width: 100vw;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  margin-top: 2rem; /* nudges the illustration down — starting estimate, likely needs live fine-tuning since the wider export shifted vertical framing; adjust this one value if it's still off */
  display: grid;
  justify-items: center;
  pointer-events: none;
}
.hero-bg-illustration img {
  grid-area: 1 / 1; /* both images stack in the same cell — later in DOM paints on top */
  width: 100%; /* full-bleed — was capped at 1500px, which left margins on a wider image */
  height: auto;
  opacity: 1;
  display: block;
}
/* Color layer sits underneath the line art (see DOM order) and is
   revealed with a one-time directional wipe as the visitor scrolls away
   from the hero — triggered once via IntersectionObserver (see main.js),
   then locked in permanently rather than continuously scroll-tied. This
   was a deliberate choice: tying it to scroll both ways risked the color
   "retreating" if someone scrolls back up to reread something, which
   would read as a glitch rather than a reveal. A permanent one-time
   unlock also matches the same narrative logic as the achievement
   popups elsewhere on the site — things unlock as you engage and stay
   unlocked, rather than introducing a third, reversible behavior
   alongside "always responsive" (ambient light, wheel, film reel) and
   "one-time unlock" (achievements). */
.hero-sketch-color {
  --reveal: 0; /* 0 = hidden, 1 = fully revealed. Driven by JS from scroll — see setupHeroSketchReveal in main.js */
  z-index: 0;  /* colour layer sits UNDER the line art */
  /* Feathered reveal edge instead of a hard clip line: the colour washes in
     gradually under the sketch as --reveal grows. The soft ~18% band moving
     top→bottom is what reads as luxury (a hard edge looked abrupt). */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(var(--reveal) * 118% - 18%), transparent calc(var(--reveal) * 118%));
  mask-image: linear-gradient(to bottom, #000 calc(var(--reveal) * 118% - 18%), transparent calc(var(--reveal) * 118%));
}
/* Line art is explicitly kept above the colour layer — no longer relying on
   DOM paint order or a grayscale-filter stacking context (which was removed). */
.hero-bg-illustration img:not(.hero-sketch-color) { z-index: 1; }
/* Elena: soften the colour illustration (80%) */
.hero-bg-illustration img.hero-sketch-color { opacity: 0.8; }
/* Plum outline layer (2026-08-03): replaces the black line-art <img>. A plum
   fill masked by the sketch's alpha — keeps the charcoal texture but in plum.
   Sits on top (z-index 1) so the colour reveal still washes in underneath. */
.hero-sketch-ink {
  grid-area: 1 / 1;
  justify-self: stretch;        /* override the parent's justify-items:center so the div
                                   fills the same cell the colour <img> defines (an empty
                                   div has 0 content width, so width:100% alone collapsed) */
  align-self: stretch;
  z-index: 1;
  background: var(--color-plum-deep);
  -webkit-mask: url('../assets/images/hero-sketch.png') center / 100% 100% no-repeat;
          mask: url('../assets/images/hero-sketch.png') center / 100% 100% no-repeat;
}
.no-js .hero-sketch-color, .reduced-motion .hero-sketch-color {
  --reveal: 1;
  -webkit-mask-image: none;
  mask-image: none; /* show it fully, no wipe animation */
}

.hero-bee-field { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.bee { position: absolute; width: 48px; opacity: 0.9; }
.bee-a { top: 14%; left: 8%; }
.bee-b { top: 20%; right: 10%; width: 40px; }

.hero-content {
  position: absolute;
  z-index: 3;
  top: calc(72px + 1rem);
  left: calc(var(--sp-4) + 6rem);
  width: fit-content;
  max-width: min(90vw, 780px);
  text-align: left;
  background: rgba(251, 250, 246, 0.45);
  border-radius: var(--radius-card);
  padding: var(--sp-2) var(--sp-3) var(--sp-3);
}

.hero-writing {
  position: relative;
  width: min(90vw, 760px);
  height: clamp(160px, 24vw, 300px);
  margin: 0 0 -0.5rem;
  /* -5.36% measured directly from the source video frame: the handwritten
     ink starts 5.36% in from the video's own left edge (baked into the
     recording), which object-position:left can't correct on its own since
     it repositions the whole frame, not the ink within it. This keeps the
     ink visually flush with the tagline text below regardless of the
     pop-in transform below (translateX stays constant in both states). */
  opacity: 0;
  transform: translateX(-5.36%) translateY(0.35em) scale(0.92);
  transition: opacity 650ms var(--ease-out), transform 650ms var(--ease-out);
}
.hero-writing.is-revealed {
  opacity: 1;
  transform: translateX(-5.36%) translateY(0) scale(1);
}
.no-js .hero-writing, .reduced-motion .hero-writing {
  opacity: 1;
  transform: translateX(-5.36%);
  transition: none;
}
.hero-clip {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left center;
  opacity: 1;
  mix-blend-mode: multiply;
  /* Feathers the video's rectangular edge so the multiply-blend boundary
     fades out gradually into the page instead of stopping at a hard,
     visible line — the recording's "white" is never quite pure white,
     which is what made the edge noticeable before. */
  mask-image: linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%),
              linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
  mask-composite: intersect;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%),
                       linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
  -webkit-mask-composite: source-in;
}
.hero-clip-final {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left center;
  opacity: 0;
  mix-blend-mode: multiply;
  mask-image: linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%),
              linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
  mask-composite: intersect;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%),
                       linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
  -webkit-mask-composite: source-in;
  transition: opacity 200ms var(--ease-out);
}
.hero-clip-final.is-visible { opacity: 1; }
.hero-clip.is-hidden { opacity: 0; }

/* ---- Returning interactive title "HI, I'M ELENA PARR" (2026-08-04, Elena) ----
   Rebuilt from Elena's own per-letter PNGs (each on the same 2550×1298 canvas, so
   stacking them recomposes her exact layout). The hero-video sequence reveals it
   after "nice to meet ya". Each letter's hover region is clip-path'd to its own
   glyph (measured bboxes) so the wobble is truly per-letter; transform-origin is the
   glyph's centre so it pivots naturally. Transform carries the clip, so the wobble
   is never clipped. */
.hero-name {
  position: relative;
  width: min(92vw, 720px);
  aspect-ratio: 2550 / 1298;
  /* the art has ~21% empty top/bottom + ~5.3% empty left baked into the canvas — pull it
     tight so the title doesn't float, and shift left so the visible text lines up with the
     tagline's left edge (measured content-left = 5.33%). */
  margin: -3.5% 0 -5.5%;
  transform: translateX(-5.33%);
  z-index: 4;
}
.hero-name img { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* each piece starts hidden and bubble-pops in one at a time on load (setupHeroName, WAA) */
.hero-name-prefix, .hero-letter { opacity: 0; }
.hero-name-prefix { pointer-events: none; }
.hero-letter { transition: transform 180ms var(--ease-out); will-change: transform, opacity; }
/* big springy zoom that SETTLES enlarged while hovered (forwards), then springs back on
   mouse-out via the transition above. Much bigger + snappier per Elena (2026-08-04). */
.hero-letter:hover { animation: heroLetterWobble 0.45s var(--ease-out) forwards; z-index: 30; }
@keyframes heroLetterWobble {
  0%   { transform: rotate(0deg) scale(1); }
  18%  { transform: rotate(-16deg) scale(1.62); }
  42%  { transform: rotate(12deg) scale(1.42); }
  64%  { transform: rotate(-7deg) scale(1.34); }
  82%  { transform: rotate(3deg) scale(1.3); }
  100% { transform: rotate(0deg) scale(1.28); }
}
.hl-e1     { clip-path: inset(53.54% 87.76% 21.8% 5.33%);  transform-origin: 8.78% 65.87%; }
.hl-l2     { clip-path: inset(53.54% 79.96% 21.8% 12.12%); transform-origin: 16.08% 65.87%; }
.hl-e3     { clip-path: inset(53.54% 72.24% 21.8% 20.86%); transform-origin: 24.31% 65.87%; }
.hl-n4     { clip-path: inset(53.54% 61.88% 21.8% 28.98%); transform-origin: 33.55% 65.87%; }
.hl-a5     { clip-path: inset(53.54% 51.06% 21.8% 39.22%); transform-origin: 44.08% 65.87%; }
.hl-p6     { clip-path: inset(53.54% 37.29% 21.8% 54.35%); transform-origin: 58.53% 65.87%; }
.hl-a7     { clip-path: inset(53.54% 27.33% 21.8% 63.06%); transform-origin: 67.86% 65.87%; }
.hl-r8     { clip-path: inset(53.54% 17.37% 21.8% 73.8%);  transform-origin: 78.22% 65.87%; }
.hl-r9     { clip-path: inset(53.54% 7.41% 21.8% 83.84%);  transform-origin: 88.22% 65.87%; }
.hl-period { clip-path: inset(72.5% 2.04% 21.42% 94.47%);  transform-origin: 96.22% 75.54%; }
@media (prefers-reduced-motion: reduce) {
  .hero-letter:hover { animation: none; transform: scale(1.28); z-index: 30; }
}

.hero-static-fallback {
  /* visually hidden always now — the final still image serves this purpose for everyone,
     but real text stays in the DOM for screen readers / SEO */
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}
.no-js .hero-clip, .reduced-motion .hero-clip { display: none; }
.no-js .hero-clip-final, .reduced-motion .hero-clip-final {
  position: static;
  opacity: 1;
  width: 100%;
  height: auto;
}

.hero-tagline {
  font-size: 1.05rem;
  color: var(--color-ink-soft);
  max-width: 52ch;
  margin: 0 0 var(--sp-3);
}
/* Word-by-word pop-in reveal — each word is wrapped in a span by JS
   (setupHeroTaglineReveal in main.js) with its own transition-delay for
   the stagger. Falls back to always-visible for no-js/reduced-motion. */
.hero-tagline .reveal-word {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.35em) scale(0.92);
  transition: opacity 650ms var(--ease-out), transform 650ms var(--ease-out);
}
.hero-tagline.is-revealed .reveal-word {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.no-js .hero-tagline .reveal-word,
.reduced-motion .hero-tagline .reveal-word {
  opacity: 1;
  transform: none;
  transition: none;
}
.hero-tagline a {
  color: var(--color-accent-purple);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 2px solid var(--color-accent-gold);
}

.hero-scroll-cue {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-1);
  text-decoration: none;
  color: var(--color-ink-soft);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.scroll-dot { animation: scrollBob 1.8s ease-in-out infinite; }
@keyframes scrollBob {
  0%, 100% { transform: translateY(0); opacity: 1; }
  50% { transform: translateY(6px); opacity: 0.4; }
}

@media (max-width: 700px) {
  /* Mobile hero = a clean vertical STACK, not the desktop overlay. On desktop
     .hero-content is position:absolute floating on top of the full-bleed sketch,
     which cramped the title against the drawing (Elena's note). .hero-content is
     the FIRST child in the DOM (sketch comes after), so switching it back into
     normal flow makes the title block sit ABOVE the illustration — the letters
     get their own space and the sketch drops below it. */
  .hero-content {
    position: relative;   /* in flow, but keep z-index above the bees */
    z-index: 3;
    top: auto;
    left: auto;
    width: auto;
    max-width: 100%;
    background: transparent;   /* no readability card needed once it's off the sketch */
    padding: calc(72px + var(--sp-4)) var(--sp-4) var(--sp-3);
  }
  .hero-name { width: 100%; margin: 0 0 var(--sp-3); }
  .hero-tagline { font-size: 0.9rem; max-width: none; }   /* smaller "multidisciplinary…" line */
  .hero-bg-illustration { margin-top: var(--sp-1); }      /* sketch now sits below the title */
  .hero-writing { height: clamp(110px, 34vw, 180px); }
}

/* =========================================================
   SECTIONS (shared)
   ========================================================= */
.section {
  padding: var(--sp-16) var(--sp-4);
  position: relative;
}
.section-inner {
  max-width: 1200px;
  margin: 0 auto;
  position: relative;
  z-index: 2; /* sits above .ambient-light (z-index:1) so real content is never covered by it */
}
/* Publications = inverted "gallery" dark section (2026-08-03, Elena's call):
   deep plum bg makes the near-white book covers pop like objects on velvet, and
   it bookends the site with the plum Contact section (cream sections between).
   All text flips LIGHT here (scoped, doesn't touch the global scheme). Title is
   LILAC per Elena; body/borders go light so nothing sits dark-on-dark. */
.section--publications { background: var(--color-plum); color: #EFE6EC; }
.section--publications .eyebrow { color: #E7C9DC; }
.section--publications .section-title { color: var(--color-lilac); }
.section--publications .section-intro { color: #EFE6EC; }
.section--publications .pub-card-title { color: #FBFAF7; }
.section--publications .pub-card-year,
.section--publications .pub-card-spine-title,
.section--publications .pub-card-spine-year { color: #D9C2D2; }
.section--publications .pub-card-media,
.section--publications .pub-card-footer { border-color: rgba(222, 206, 245, 0.55); } /* lilac datum lines */
.section--publications a { color: var(--color-lilac); }

/* ---- Publications 3-up books (2026-08-03): trimmed transparent covers on plum,
   with a CSS lilac card offset to the lower-right + one consistent drop-shadow
   (my part; Elena supplies clean die-cut covers). Covers are height-normalized
   so the two portraits and the near-square back cover read as a set. ---- */
.book-row {
  list-style: none; margin: var(--sp-4) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, auto); /* two covers, sized to content */
  justify-content: start;                                 /* flanked left, aligned with the intro copy */
  gap: clamp(var(--sp-8), 8vw, var(--sp-16)); align-items: end;
}
.book { text-align: left; }
.book-frame {
  position: relative; display: inline-block; margin: 0 0 var(--sp-4);
  max-width: 100%;
}
.book-frame img {
  position: relative; z-index: 2;
  display: block; width: auto; height: auto;
  max-height: 420px; max-width: 100%;
  border-radius: 5px;
  filter: drop-shadow(0 16px 26px rgba(30, 8, 20, 0.42));
}
.book-frame::before {                 /* lilac card, peeking out lower-right */
  content: ""; position: absolute; z-index: 1;
  top: 20px; left: 20px; right: -20px; bottom: -20px;
  background: var(--color-lilac);
  border-radius: 26px;
}
.book-title {
  font-family: var(--font-display); text-transform: uppercase;
  font-size: clamp(1.1rem, 1.8vw, 1.6rem); line-height: 1.05;
  color: #FBFAF7; margin: 0 0 0.2rem;
}
.book-year {
  font-family: var(--font-body); font-size: 0.85rem;
  color: #D9C2D2; margin: 0;
}
@media (max-width: 760px) {
  .book-row { grid-template-columns: 1fr; gap: var(--sp-12); justify-items: center; }
}
/* Overview block under the book cards (on plum) */
.pub-overview { margin-top: var(--sp-8); max-width: 64ch; }
.pub-overview-title {
  font-family: var(--font-display); text-transform: uppercase;
  font-size: clamp(1.4rem, 2.2vw, 2rem); letter-spacing: 0.02em;
  color: var(--color-lilac); margin: 0 0 var(--sp-2);
}
.pub-overview-text {
  font-family: var(--font-body); font-size: 1.05rem; line-height: 1.6;
  color: #EFE6EC; margin: 0 0 var(--sp-3);
}
.pub-overview-text:last-child { margin-bottom: 0; }
/* About layout (2026-08-03): books left-flanked, overview copy in a right column
   to balance the left-weighted imagery. Pro working photos will join the left
   column later. Stacks to a single column on mobile. */
/* Story-led (2026-08-04, Elena): the two covers sit as a row up top (the "published
   at 15" credential), then the bio flows beneath at a comfortable reading measure.
   Replaced the old books-left / narrow-bio-right 2-col, which stranded the covers and
   crammed the bio into a too-narrow column. */
.about-layout { margin-top: var(--sp-4); }
.about-layout .pub-overview { margin-top: var(--sp-8); max-width: 62ch; }
@media (max-width: 860px) {
  .about-layout .pub-overview { margin-top: var(--sp-6); }
}

/* ===== About — tight "newspaper" masonry (2026-08-04, Elena): two independent columns
   that pack their items separately, so text + image cards break up like bricks (no aligned
   rows). Body copy is justified for the newspaper feel; images on lilac "mat" cards. ===== */
.about-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;   /* columns match heights so the brainbow can drop to align with the last paragraph */
  column-gap: clamp(var(--sp-4), 3vw, var(--sp-8));
  margin-top: var(--sp-6);
}
.about-col { display: flex; flex-direction: column; gap: clamp(var(--sp-4), 2.6vw, var(--sp-6)); }
/* push the Brainbow card to the bottom of its column so its title lines up with the bottom
   of the final (Tampa) paragraph in the other column, and cap its width so the columns end
   together (measured: ~320px aligns at the ~1160px width; Elena 2026-08-04) */
.about-card--brainbow { margin-top: auto; width: 100%; max-width: 320px; }
.about-para  {
  font-family: var(--font-body); font-size: 1.02rem; line-height: 1.6; color: #EFE6EC;
  margin: 0; text-align: justify; hyphens: auto;
}

/* image cards — lilac rounded mat + inset artwork + lift shadow */
.about-card { margin: 0; }
.about-card-media {
  background: var(--color-lilac);
  border-radius: 20px;
  padding: clamp(9px, 1.1vw, 15px);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}
.about-card-media--pad { padding: clamp(14px, 2vw, 26px); }
.about-card-media img,
.about-card-media video { display: block; width: 100%; height: auto; border-radius: 12px; }
.about-cap { margin-top: var(--sp-1); text-align: left; }
.about-cap-title {
  display: block; font-family: var(--font-display); text-transform: uppercase;
  font-size: clamp(1rem, 1.5vw, 1.3rem); letter-spacing: 0.02em; line-height: 1.05; color: #FBFAF7;
}
.about-cap-sub { display: block; font-family: var(--font-body); font-size: 0.85rem; color: #D9C2D2; margin-top: 0.15rem; }

/* the two published books sit side by side within the left column — wider gap so the two
   lilac mats read as separate cards, not one block (Elena 2026-08-04) */
.about-books { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(var(--sp-4), 3.5vw, var(--sp-8)); align-items: start; }

@media (max-width: 760px) {
  /* Chronological single column (Elena): dissolve the two brick-columns with display:contents
     so all six items become direct grid items of .about-cols, then re-sequence them in TIME
     order via `order`. The desktop masonry (the two independent .about-col stacks) is untouched.
     Story order: intro'19 → books → MECA'21 → Buttons comic → Tampa'24 → Brainbow. */
  .about-cols { grid-template-columns: 1fr; row-gap: clamp(var(--sp-4), 5vw, var(--sp-6)); }
  .about-col { display: contents; }
  .about-cols > .about-col:nth-child(1) > .about-text:nth-child(1)  { order: 1; }  /* intro · 2019 */
  .about-cols > .about-col:nth-child(1) > .about-books             { order: 2; }  /* the two books */
  .about-cols > .about-col:nth-child(2) > .about-text             { order: 3; }  /* MECA · 2021 */
  .about-cols > .about-col:nth-child(2) > .about-card--wide       { order: 4; }  /* Buttons comic */
  .about-cols > .about-col:nth-child(1) > .about-text:nth-child(3) { order: 5; }  /* Tampa · 2024 */
  .about-cols > .about-col:nth-child(2) > .about-card--brainbow   { order: 6; }  /* Brainbow */
  /* brainbow: drop the desktop bottom-align + width cap so it stacks full-width like the rest */
  .about-card--brainbow { margin-top: 0; max-width: none; }
}

/* ---- Achievements: now its own warm-white section (split from Publications) ---- */
.section--achievements {
  /* warm white at the top (behind the sheet's crest), fading to the Fine Art parchment
     lower down so the parchment sheet connects into Fine Art with no warm-white gap */
  background: linear-gradient(to bottom,
    var(--color-bg) 0%, var(--color-bg) 20%,
    var(--color-bg-alt) 46%, var(--color-bg-alt) 100%);
  padding-bottom: 0; /* board bottom butts directly against the Fine Art section */
}
.section--achievements .the-archive { margin-top: 0; } /* section padding provides the spacing now */
.the-archive-title { color: var(--color-plum); }
.the-archive-intro { color: var(--color-plum-mauve); }
.section--fineart { background: var(--color-bg-alt); }
.section--edu { background: var(--color-bg); padding-bottom: 0; }   /* lilac panel runs flush into Presentation — no warm-white gap (Elena) */
.section--branding { background: var(--color-bg-alt); }
/* Scheme C is now global (see .eyebrow / .section-title / .section-intro):
   lilac eyebrow, plum title, plum-mauve body — across all standard sections +
   Stellina. The earlier Branding-scoped gold-eyebrow override was removed. */
.section--stellina {
  background: linear-gradient(180deg, var(--color-bg) 0%, #F4ECFF 100%);
  text-align: center;
  padding-bottom: 0; /* the full-screen scene flows straight into Contact — no gap */
}
.section--contact { background: var(--color-plum); color: var(--color-bg); }

.reveal {
  opacity: 0;
  transform: translateY(28px);
}
.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

/* =========================================================
   PUBLICATIONS — archive cards
   ========================================================= */
/* --- Publications cards: cover + divider + bold title/year + vertical spine label ---
   Proportions measured directly from Elena's Canva mockup. Sized down ~8%
   from the previous pass per feedback ("a little smaller, 5-10%"). The
   Ivory Butterflies side label sits in normal flex flow next to its cover
   (not absolutely positioned/overflowing) so the whole two-book composition
   stays inside .section-inner's centered 1200px column — equal margins
   both sides — rather than the label spilling into the outer page margin. */
.archive {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
  gap: 3.5rem;
}
@media (min-width: 900px) {
  .archive { grid-template-columns: 2.27fr 1fr; }
}
.pub-card {
  display: flex;
  flex-direction: column;
  /* stretch (grid default) is intentional here: book 2's cover crops via
     object-fit:cover to exactly match book 1's natural total height
     (image + divider + title/year), rather than showing its full
     uncropped height — this is what makes the shared bottom line work. */
}
.pub-card:nth-child(odd) {
  justify-content: flex-end; /* anchors content to the bottom of the stretched cell, guaranteeing the footer's bottom border lands on the shared datum line even if stretch leaves a hair of slack */
}
.pub-card-media {
  display: flex;
  align-items: stretch;
  border-bottom: 2px solid var(--color-ink);
  /* no padding, no background — the cover is the card, not a photo in a mat */
}
/* Publications needs to fit heading + intro + both books in one screen
   without scrolling, per the mockup — the global section padding/heading
   spacing is too generous for that here specifically, so it's tightened
   just for this section rather than changed everywhere. */
/* About padding normalised to the standard .section 128/128 rhythm 2026-08-03
   (was sp-6/sp-8 = 48/64 — out of step with every other section). */
.section--publications .section-title { margin-bottom: var(--sp-1); }
.section--publications .section-intro { margin-bottom: var(--sp-4); }

.pub-card-media img {
  width: 100%;
  height: auto;
  max-height: 230px;
  object-fit: contain; /* the front+back spread already matches its box, nothing to crop */
}
/* Portrait single cover (even card, Ivory Butterflies): image + side label
   sit side by side in normal flex flow, both counted within the grid
   column's own width — this is what keeps the whole thing centered. */
.pub-card:nth-child(even) {
  height: 100%;
}
.pub-card:nth-child(even) .pub-card-media {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
}
.pub-card:nth-child(even) .pub-card-media img {
  width: 100%;
  height: 100%;
  max-height: none;
  object-fit: cover;
  object-position: center top; /* favor the top of the cover over the bottom when cropping */
}
.pub-card-footer {
  margin-top: 1.4rem;
  padding-bottom: 1.4rem;
  border-bottom: 2px solid var(--color-ink); /* closes the bottom of the text block — shared datum line */
}
.pub-card-footer h3.pub-card-title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.45rem, 3vw, 2.45rem); /* oversized editorial headline, not a card label */
  line-height: 1.05;
  margin-bottom: 0.4rem;
}
.pub-card-year {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(0.85rem, 1.45vw, 1rem);
  color: var(--color-ink-soft);
  margin: 0;
}
.pub-card-spine {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap; /* keep title+year in a single column, never wrap to a second one */
  margin: 0; /* reset default <p> margin */
  display: none; /* Ivory Butterflies only, shown via nth-child(even) below */
}
.pub-card-spine-title {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-ink-soft);
  margin-inline-start: 0.5rem; /* gap after year, before title — vertical-rl's block axis is horizontal, so inline-start is the vertical gap we actually want */
}
.pub-card-spine-year {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-ink-soft);
}
.pub-card { position: relative; }
/* Ivory Butterflies side label: shown once there's room for it (900px+),
   sitting in-flow beside its cover — not absolutely positioned — so the
   grid column's own width includes it and the whole spread stays centered. */
@media (min-width: 900px) {
  .pub-card:nth-child(even) {
    flex-direction: row;
    align-items: stretch;
  }
  .pub-card:nth-child(even) .pub-card-spine {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    border-left: 1px solid var(--color-border);
    border-right: 1px solid var(--color-border);
    padding-left: 0.9rem;
    padding-right: 0.9rem;
    margin-left: 1.5rem;
  }
  .pub-card:nth-child(even) .pub-card-footer {
    display: none;
  }
}
@media (min-width: 1500px) {
  .pub-card:nth-child(even) .pub-card-spine {
    border-left: 2px solid var(--color-ink);
    border-right: 2px solid var(--color-ink);
    padding-left: 1.5rem;
    padding-right: 1.5rem;
  }
  .pub-card:nth-child(even) .pub-card-spine-title {
    font-size: clamp(1.1rem, 2vw, 1.55rem);
    color: var(--color-ink);
    margin-inline-start: 0.75rem;
  }
  .pub-card:nth-child(even) .pub-card-spine-year {
    font-size: clamp(1rem, 1.85vw, 1.4rem); /* much closer to title's weight, per feedback */
    color: var(--color-ink);
  }
}


/* =========================================================
   THE ARCHIVE — full editorial interactive crossword feature
   ========================================================= */
.the-archive {
  margin-top: var(--sp-16);
  padding: var(--sp-16) var(--sp-4);
  text-align: center;
  max-width: 1100px;
  margin-left: auto;
  margin-right: auto;
}
.the-archive-title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(2rem, 4vw, 2.75rem);
  margin-bottom: var(--sp-2);
}
.the-archive-intro {
  color: var(--color-plum-mauve); /* scheme C body colour (was ink-soft) */
  margin-bottom: var(--sp-6);
}

/* ===== Sticker sheet — scattered "sheet" per Elena's Canva mockup (2026-08-03).
   Her cream-board asset is the backdrop (crest at top holds the title); stickers
   are absolutely scattered at varied size + rotation (positions set inline by JS).
   Labels are dropped (the mockup has none; aria-labels still carry meaning).
   NOTE: the board is cream on a warm-white section — a soft drop-shadow gives its
   edge just enough contrast to read (see the "needs backdrop to contrast" filename).
   Desktop scatter only; mobile is a separate pass (deferred to 2026-08-04). ===== */
.section--achievements .the-archive {
  position: relative;
  /* full-bleed: the cream board spans the whole section so it reads as the surface,
     not a floating card. 100vw is safe here — html/#main have overflow-x:hidden. */
  width: 100vw; margin-left: 50%; transform: translateX(-50%);
  max-width: none; padding: 0;
  height: clamp(540px, 47vw, 760px);  /* taller — room for the words→stickers gap + bigger stickers (Elena) */
}
/* The parchment board is a BEHIND-content layer (::before), not the container itself —
   a mask on the container would clip the title & stickers. The PNG supplies only the
   shape; fill is the Fine Art parchment; its bottom flows into Fine Art with no gap. */
/* OUTER: carries the drop-shadow ONLY. Not masked, so the shadow renders freely
   around the sheet shape (a mask here would clip the shadow away — the old bug). */
.section--achievements .sheet-shadow {
  position: absolute; inset: 0; z-index: 0;
  filter: drop-shadow(0 6px 6px rgba(66, 17, 6, 0.55)) drop-shadow(0 13px 22px rgba(66, 17, 6, 0.30));
  /* clip the shadow layer to the TOP half: keeps the crest shadow, discards the
     sheet's bottom-edge shadow so it never lands on the Fine Art seam. The sheet is
     parchment on parchment below the clip, so the clip edge is invisible. */
  clip-path: inset(-40% 0 48% 0);
}
/* INNER: the solid parchment sheet. No bottom fade now — so the only shadow-casting
   edge in view is the top crest (→ crest shadow, no mid-section bar). The sheet's
   bottom sits at the section edge; its downward shadow overflows into Fine Art and
   is painted over by Fine Art's opaque parchment, keeping that seam seamless. */
.section--achievements .sheet-fill {
  position: absolute; inset: 0;
  background: var(--color-bg-alt);
  -webkit-mask: url("../assets/images/sticker_sheet_needs_backdrop_to_contrast_cream.png") center top / 100% 100% no-repeat;
          mask: url("../assets/images/sticker_sheet_needs_backdrop_to_contrast_cream.png") center top / 100% 100% no-repeat;
}
/* title + intro grouped as one neat block, seated in the crest (no visible box) */
.section--achievements .archive-header {
  position: absolute; top: 19%; left: 50%; transform: translateX(-50%);
  width: 100%; text-align: center; z-index: 3;
}
.section--achievements .the-archive-title {
  position: static; margin: 0; line-height: 1;
}
.section--achievements .the-archive-intro {
  position: static; margin: var(--sp-3) auto 0; max-width: 40ch;
}
.sticker-sheet {
  position: absolute; inset: 0; margin: 0; padding: 0;
  list-style: none; max-width: none; background: none; border-radius: 0;
  display: block; z-index: 1;   /* above the board ::before layer */
}
/* each sticker: left/top = its centre, width = % of the board — all set inline by JS */
.sticker {
  position: absolute;
  transform: translate(-50%, -50%) rotate(var(--rot, 0deg));
}
.sticker-btn {
  background: none; border: none; padding: 0; cursor: pointer;
  display: block; width: 100%;
  transition: transform var(--dur-fast) var(--ease-out);
}
.sticker-btn:hover, .sticker-btn:focus-visible {
  transform: translateY(-6px) scale(1.06);   /* peel-hint lift (on the btn, so it composes with the li's rotate) */
  outline: none;
}
.sticker-btn img {
  display: block; width: 100%; height: auto;
  filter: drop-shadow(0 8px 12px rgba(30, 8, 20, 0.30));
}
.sticker-label { display: none; }   /* mockup has no visible labels; aria-label carries meaning */
@media (prefers-reduced-motion: reduce) {
  .sticker-btn, .sticker-btn:hover, .sticker-btn:focus-visible { transform: none; }
}
/* Mobile: taller board so the stickers can go BIGGER + VERTICAL (Elena's note). The
   scatter itself is set inline by setupStickerSheet's MOBILE_POS; here we just give the
   board the height that column needs and lift the title block toward the crest top. */
@media (max-width: 760px) {
  .section--achievements .the-archive { height: clamp(1000px, 262vw, 1200px); }
  /* Seat the title lower in the crest so it isn't crammed against the top edge (Elena — same
     issue we fixed on desktop). top:10% gives a comfortable margin above the words across
     phone widths; the sticker column below is shifted down to match (MOBILE_POS in main.js). */
  .section--achievements .archive-header { top: 10%; }
  /* The mask PNG is landscape (2550×1693); forcing it to 100%×100% of the tall mobile board
     squished the sheet narrow + thin (Elena). `cover` scales it to fill the width and crops
     the overflow instead — so the parchment fills the board edge-to-edge. The shadow layer
     follows because the drop-shadow tracks .sheet-fill's masked shape. */
  .section--achievements .sheet-fill {
    -webkit-mask-size: cover; mask-size: cover;
    -webkit-mask-position: center top; mask-position: center top;
  }
}

.the-archive-body {
  display: flex;
  flex-direction: column;
  align-items: center;
}
@media (min-width: 900px) {
  /* Desktop: editorial split-screen, per Elena's mockup — this is the
     source of truth for this layout, not a centered-widget composition.
     Clues left, crossword right, roughly equal visual weight, vertically
     aligned so they read as two halves of one interaction. */
  .the-archive-body {
    display: grid;
    grid-template-columns: minmax(300px, 420px) 1fr;
    align-items: center;
    gap: var(--sp-12);
    max-width: 1100px;
    margin: 0 auto;
  }
  .the-archive-body .crossword-clues { order: 1; } /* left, even though it's second in DOM (crossword is announced first for screen readers) */
  .the-archive-body .crossword-puzzle { order: 2; margin: 0; justify-self: center; } /* right */
}

/* --- puzzle grid --- */
.crossword-puzzle {
  display: grid;
  gap: 5px;
  width: 100%;
  max-width: 480px;
  margin: 0 auto var(--sp-8);
}
.cwx-cell {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
}
.cwx-blank { visibility: hidden; }
.cwx-num {
  position: absolute;
  top: 3px;
  left: 5px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.6rem;
  line-height: 1;
  color: var(--color-accent-purple);
  pointer-events: none;
  z-index: 2;
}
.cwx-input {
  width: 100%;
  height: 100%;
  border: 1px solid rgba(122, 104, 80, 0.38);
  background: #F4ECDC;
  text-align: center;
  text-transform: uppercase;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: clamp(1rem, 2.6vw, 1.3rem);
  color: var(--color-ink);
  border-radius: 11px;
  padding: 0;
  box-shadow:
    0 5px 12px rgba(46, 42, 36, 0.16),
    0 1px 2px rgba(46, 42, 36, 0.08),
    inset 0 1px 0 rgba(255, 255, 255, 0.65);
  transition: box-shadow var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.cwx-input:hover {
  transform: translateY(-4px) scale(1.16);
  box-shadow:
    0 14px 26px rgba(46, 42, 36, 0.24),
    0 3px 8px rgba(46, 42, 36, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);
  border-color: rgba(107, 78, 113, 0.4);
  z-index: 5;
}
.cwx-input:focus {
  outline: none;
  border-color: var(--color-accent-purple);
  transform: translateY(-3px) scale(1.03);
  box-shadow:
    0 10px 20px rgba(46, 42, 36, 0.20),
    0 0 0 3px rgba(107, 78, 113, 0.12);
}
.cwx-cell.is-active-word .cwx-input {
  border-color: rgba(107, 78, 113, 0.45);
}
.cwx-cell.is-correct .cwx-input {
  background: rgba(63, 122, 120, 0.14);
  border-color: var(--color-accent-teal);
  color: var(--color-accent-teal);
  box-shadow: 0 2px 6px rgba(63, 122, 120, 0.12);
  transform: none;
}
@keyframes cwxCelebrate {
  0% { box-shadow: 0 5px 12px rgba(46,42,36,0.16), inset 0 1px 0 rgba(255,255,255,0.65); transform: scale(1); }
  45% { box-shadow: 0 0 0 7px rgba(63,122,120,0.28), 0 6px 14px rgba(46,42,36,0.14); transform: scale(1.08); }
  100% { box-shadow: 0 2px 6px rgba(63,122,120,0.12); transform: scale(1); }
}
.cwx-input.is-celebrating {
  animation: cwxCelebrate 550ms var(--ease-out);
}

/* --- clues: editorial footnotes, styled like a subtle newspaper column --- */
.crossword-clues {
  display: flex;
  justify-content: center;
  gap: var(--sp-8);
  width: 100%;
  max-width: 620px;
  text-align: left;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  padding: var(--sp-6) var(--sp-6);
}
.crossword-clues-col { flex: 1 1 0; }
.crossword-clues-heading {
  font-family: var(--font-body);
  text-transform: uppercase;
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.14em;
  color: var(--color-ink);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-1);
  border-bottom: 2px solid var(--color-ink);
  opacity: 0.9;
}
.crossword-clues-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.crossword-clues-list li {
  font-size: 0.92rem;
  font-weight: 400;
  color: var(--color-ink-soft);
  opacity: 0.78;
  line-height: 1.5;
  transition: color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.crossword-clues-list li strong {
  color: var(--color-ink);
  font-weight: 600;
  opacity: 0.95;
  margin-right: 0.4em;
}
.crossword-clues-list li.is-solved {
  color: var(--color-accent-teal);
  opacity: 0.9;
  text-decoration: line-through;
  text-decoration-color: rgba(63,122,120,0.35);
}

@media (max-width: 600px) {
  .crossword-clues { flex-direction: column; gap: var(--sp-6); padding: var(--sp-4); }
  /* Safety per Elena's spec: bias slightly left rather than perfectly
     centered, so the puzzle can never end up straddling the edge of the
     viewport with a tile clipped off. */
  .crossword-puzzle { margin: 0 auto 0 0; max-width: min(94vw, 480px); }
}

/* --- achievement overlay ---
   position:absolute (not fixed) — see AMBIENT RAINBOW-LIGHT REFLECTION
   comment above for why: GSAP/ScrollTrigger applies a transform to
   <body>, which breaks position:fixed for ALL descendants (they end up
   positioned relative to the transformed ancestor's document position
   instead of the viewport). This was causing the popup to render up near
   Publications/Achievements — where this element actually sits in the
   DOM — instead of centered over whatever the visitor is currently
   looking at, and focusing the close button then auto-scrolled the page
   there. Top is set via JS (openAchievement in main.js) at the moment
   the modal opens, so it always matches the current scroll position. */
.achievement-overlay {
  position: absolute;
  left: 0;
  width: 100%;
  height: 100vh;
  top: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  background: rgba(120, 107, 160, 0.55); /* plum-lilac backdrop (was a dark scrim) — 2026-08-03 */
  backdrop-filter: blur(4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out);
}
.achievement-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
}
.achievement-panel {
  position: relative;
  background: var(--color-bg);
  border-radius: var(--radius-card);
  max-width: 460px;
  width: 100%;
  overflow: visible;              /* let the pinned sticker overhang the top edge */
  margin-top: 88px;               /* leaves room above for the (now larger) overhang */
  box-shadow: var(--shadow-lift);
  transform: scale(0.9);
  transition: transform var(--dur-med) var(--ease-out);
}
/* the clicked sticker, "pinned" straddling the top edge of the card */
.achievement-sticker {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -60%);
  z-index: 2;
  pointer-events: none;
}
.achievement-sticker img {
  display: block; width: auto; height: auto;
  max-height: 200px; max-width: 260px;
  filter: drop-shadow(0 10px 16px rgba(30, 8, 20, 0.38));
}
.achievement-scroll {
  max-height: 74vh;
  overflow-y: auto;
  padding: 104px var(--sp-6) var(--sp-6); /* top padding clears the larger sticker's lower half */
  text-align: center;
}
.achievement-overlay.is-open .achievement-panel {
  transform: scale(1);
}
.achievement-close {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: var(--color-bg-alt);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  color: var(--color-ink-soft);
  transition: background var(--dur-fast) var(--ease-out);
}
.achievement-close:hover { background: var(--color-border); }
.achievement-eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-accent-teal);
  margin-bottom: var(--sp-1);
}
.achievement-title {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 2rem;
  color: var(--color-gold-pop); /* orangey-gold pop (Elena) — was amethyst */
  margin-bottom: var(--sp-3);
}
.achievement-image {
  max-width: 220px;
  margin: 0 auto var(--sp-4);
  border-radius: 14px;
  overflow: hidden;
}
.achievement-image img { width: 100%; height: auto; display: block; object-fit: contain; }
.achievement-body {
  color: var(--color-ink-soft);
  line-height: 1.6;
  text-align: left;
}
/* "learn more" button inside the popup (for the blurbs that ended in "…") */
.achievement-link {
  display: inline-block;
  margin-top: var(--sp-5);
  padding: 0.7em 1.5em;
  background: var(--color-plum);
  color: #FBFAF7;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: none;
  border-radius: 999px;
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.achievement-link:hover,
.achievement-link:focus-visible {
  background: var(--color-plum-dark);
  transform: translateY(-2px);
}
.achievement-link[hidden] { display: none; }  /* the hidden attr must win over display:inline-block */

/* =========================================================
   FINE ART — stationary film-reel frame + continuously
   scrolling artwork belt clipped inside it
   ========================================================= */
.film-reel {
  position: relative;
  width: 100vw;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  max-width: none;
  margin-top: var(--sp-4); /* extra breathing room above, so the hover-zoom has clear room to expand into */
  margin-bottom: var(--sp-12); /* was sp-8 — more room below too, same reason */
  height: clamp(200px, 25vw, 325px); /* ~25% taller than before, per feedback, so more of each piece is visible even before hover */
}

/* top/bottom sprocket strips: the same source crop, tiled at native-ish
   scale (never stretched) so the pattern stays crisp at any width */
.film-reel-strip {
  position: absolute;
  left: 0;
  width: 100%;
  height: 28px;
  background-image: url('../assets/images/film-reel-strip.webp');
  background-size: auto 28px;
  background-repeat: repeat-x;
  z-index: 2;
}
.film-reel-strip--top { top: 0; }
.film-reel-strip--bottom { bottom: 0; transform: scaleY(-1); }

.film-reel-viewport {
  position: absolute;
  top: 28px;
  bottom: 28px;
  left: 0;
  width: 100%;
  overflow: visible; /* NOT overflow-x/-y split — see clip-path below for why */
  clip-path: inset(-1000px 0px -1000px 0px); /* clips left/right at the box edges (horizontal belt), leaves top/bottom fully open so a hovered piece can scale up without being clipped. overflow-x:hidden + overflow-y:visible looks like it should do this, but per the CSS Overflow spec that combination computes overflow-y as 'auto' — which still clips. clip-path has no such axis-coupling. */
  background: rgb(33, 35, 34); /* matches the asset's own dark tone so nothing seams */
}

.film-reel-track {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: max-content;
  gap: var(--sp-4);
  padding: 0 var(--sp-4);
  /* no CSS animation here — see setupFilmReel in main.js. Motion is
     scroll-linked rather than autoplaying on its own timer, so it shares
     the same "responds to you" language as the wheel and ambient light
     instead of being the one element on autopilot. */
}
@media (prefers-reduced-motion: reduce) {
  .film-reel-track { overflow-x: auto; }
}

.film-reel-track img {
  height: 78%;
  width: auto;
  object-fit: contain; /* never crop the drawings */
  flex: 0 0 auto;
  border-radius: 6px;
  transform-origin: center;
  transition: transform 320ms ease-in-out; /* deliberate, cinematic — not a snap */
  position: relative;
}
.film-reel-track img:hover {
  transform: scale(1.5); /* dialed back further as a safety margin on top of the clip-path fix below */
  z-index: 5;
  box-shadow: 0 20px 50px rgba(0,0,0,0.4);
}

/* Fine Art — still gallery grid (replaced the film-reel belt per Elena's
   mockup). 3×2 charcoal/graphite studies on a soft lilac ground, so the
   foundation drawings read like a gallery wall rather than a moving strip. */
/* ---- Fine Art click-through DECK (replaced the 3×2 grid 2026-08-04 — a grid here
   competed with the Achievements sticker grid directly above; the deck gives Fine Art
   its own gesture). setupFineartDeck() adds .is-fdeck to enhance the fallback into a pile.
   NB: classes are `fdeck-*` NOT `deck-*` — the Presentation pitch cards already own
   `.deck-card`/`.deck-row`/`.deck-video` (collision blanked/clipped them, see Traps). ---- */
.fineart-deck { margin: var(--sp-6) 0 var(--sp-12); }

/* Fallback (no JS / reduced motion): a calm centered grid so all six still show. */
.fdeck-stack {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(1rem, 2.5vw, 2rem);
}
.fdeck-card {
  margin: 0; width: min(30%, 340px); aspect-ratio: 4 / 3;
  border-radius: 16px; overflow: hidden;
  background: var(--color-card-bg); box-shadow: var(--shadow-soft);
}
.fdeck-card img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* Enhanced: the messy tilted pile. Cards are stacked absolutely and positioned by JS. */
.fdeck-stack.is-fdeck {
  position: relative; display: block;
  width: min(90%, 520px); aspect-ratio: 4 / 3;
  margin-inline: auto; cursor: pointer; touch-action: manipulation;
  overflow: visible;
}
.fdeck-stack.is-fdeck:focus-visible { outline: 2px solid var(--color-copper); outline-offset: 10px; border-radius: 20px; }
.is-fdeck .fdeck-card {
  position: absolute; inset: 0; width: 100%; height: 100%;
  transform-origin: 50% 60%;
  transition: transform 0.42s var(--ease-out), opacity 0.42s var(--ease-out), box-shadow 0.42s var(--ease-out);
}
.is-fdeck .fdeck-card.is-front { box-shadow: var(--shadow-lift); }
.is-fdeck .fdeck-card.is-leaving { transition-duration: 0.3s; }

.fdeck-nav { display: flex; align-items: center; justify-content: center; gap: var(--sp-3); margin-top: var(--sp-6); }
.fdeck-btn {
  width: 46px; height: 46px; border-radius: 50%;
  border: 1.5px solid var(--color-plum); background: transparent; color: var(--color-plum);
  font-size: 1.5rem; line-height: 1; cursor: pointer; display: grid; place-items: center;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.fdeck-btn:hover { background: var(--color-plum); color: #FBFAF7; }
.fdeck-btn:active { transform: scale(0.9); }
.fdeck-count { font-family: var(--font-body); font-weight: 600; color: var(--color-plum-mauve); margin: 0; min-width: 4ch; text-align: center; letter-spacing: 0.06em; }

@media (prefers-reduced-motion: reduce) {
  .is-fdeck .fdeck-card { transition: opacity 0.2s linear; }
}
@media (max-width: 520px) { .fdeck-card { width: min(46%, 300px); } }

.art-wheel-label {
  text-align: center;
  font-family: var(--font-body);
  text-transform: uppercase;
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  color: var(--color-ink-soft);
  opacity: 0.65;
  margin: var(--sp-8) 0 var(--sp-3);
}

.art-wheel {
  position: relative;
  width: 100vw;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  height: clamp(480px, 50vw, 660px);
  overflow: hidden;
}
.art-wheel-track {
  position: absolute;
  inset: 0;
}
.art-wheel-piece {
  position: absolute;
  top: 0;
  left: 0;
  width: clamp(160px, 14vw, 230px);
  margin: 0;
  will-change: transform, opacity;
}
.art-wheel-piece img {
  width: 100%;
  aspect-ratio: 3/4;
  height: auto;
  object-fit: cover;
  border-radius: 30%;
  box-shadow: var(--shadow-lift);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
  display: block;
}
/* ghost gallery labels under each piece (Elena 2026-08-04) — intentionally faint */
.art-wheel-caption { margin-top: 0.6rem; text-align: center; padding: 0 0.25rem; }
.art-wheel-caption .awc-title {
  display: block; font-family: var(--font-body); font-style: italic;
  font-size: 0.8rem; line-height: 1.25; color: var(--color-plum-mauve); opacity: 0.72;
}
.art-wheel-caption .awc-year {
  display: block; margin-top: 0.15rem; font-family: var(--font-body);
  font-size: 0.7rem; letter-spacing: 0.08em; color: var(--color-ink-soft); opacity: 0.5;
}
.art-wheel-piece:hover img {
  transform: scale(1.14);
  box-shadow: 0 24px 48px rgba(46,42,36,0.28);
}
@media (prefers-reduced-motion: reduce) {
  .art-wheel { overflow-x: auto; height: auto; padding: var(--sp-4) 0; }
  .art-wheel-track { position: static; display: flex; gap: var(--sp-3); width: max-content; }
  .art-wheel-piece { position: static; margin: 0 !important; transform: none !important; opacity: 1 !important; width: 200px; }
}
/* Mobile: smaller pieces + a wider radius (set in JS) so the paintings sit apart, not
   overlapping, on a narrow screen (Elena, session 7). */
@media (max-width: 760px) {
  .art-wheel-piece { width: clamp(120px, 34vw, 150px); }
}

/* =========================================================
   DESIGN FOR LEARNING
   ========================================================= */
.edu-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
}
.edu-feature {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-alt);
  border-radius: var(--radius-card);
  padding: var(--sp-4);
}
.edu-feature img { border-radius: var(--radius-art); }
.edu-card {
  background: var(--color-card-bg);
  border-radius: var(--radius-card);
  padding: var(--sp-3);
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.edu-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lift); }
.edu-card img { border-radius: var(--radius-art); margin-bottom: var(--sp-2); aspect-ratio: 3/4; object-fit: cover; width: 100%; }
.edu-card h3 { font-weight: 700; margin-bottom: var(--sp-1); }
.edu-card p { font-size: 0.9rem; color: var(--color-ink-soft); margin: 0; }

@media (max-width: 860px) {
  .edu-grid { grid-template-columns: 1fr; }
}

.edu-evidence { margin-top: var(--sp-6); }
.edu-evidence-label {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-ink-soft);
  margin-bottom: var(--sp-2);
}
.edu-evidence-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--sp-3);
}
.edu-evidence-grid img { border-radius: var(--radius-art); box-shadow: var(--shadow-soft); }

/* ===== Design for Learning — two equal labeled cards (Kids Art + Social Story) + the
   Stellina character, on a soft pink-lilac panel (Elena's mockup composition, 2026-08-04). ===== */
.section--edu .section-inner { max-width: 1340px; }   /* let the panel space further across the page (Elena) */
.edu-panel {
  margin-top: var(--sp-8);
  display: grid;
  /* two SMALL card columns packed left + the character taking the rest → left-flanked,
     Stellina floats in the open lilac on the right (Elena, 2026-08-04) */
  grid-template-columns: minmax(0, 340px) minmax(0, 340px) minmax(0, 1fr);
  align-items: start;
  gap: clamp(var(--sp-4), 3vw, var(--sp-8));   /* NB: --sp-5/-7/-9 don't exist — using defined tokens */
  background: #F3E7EF;
  /* full-bleed lilac band: the colour spans the whole viewport (100vw technique — safe,
     html/#main have overflow-x:hidden). Left padding aligns the cards under the title
     (matches .section-inner's 1340px column) so the left margin reads equal to the page;
     the band still runs to the right screen edge. */
  width: 100vw; margin-left: 50%; transform: translateX(-50%);
  border-radius: 0;
  padding-top: clamp(var(--sp-6), 4vw, var(--sp-12));
  padding-bottom: clamp(var(--sp-6), 4vw, var(--sp-12));
  padding-left: max(var(--sp-4), calc((100vw - 1340px) / 2));
  padding-right: max(var(--sp-4), calc((100vw - 1340px) / 2));
}
/* footnote-star used to flag work that isn't Elena's original illustration */
.attr-mark { color: var(--color-copper); font-size: 0.9em; font-weight: 700; vertical-align: super; margin-left: 0.15em; cursor: help; }
/* the two cards */
.edu-card2 { display: flex; flex-direction: column; margin: 0; }
.edu-card2-media {
  margin: 0; position: relative; overflow: hidden;
  border-radius: var(--radius-art);
  height: clamp(280px, 30vw, 440px);
  box-shadow: var(--shadow-soft); background: #fff;
}
.edu-card2-media > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.edu-card2-title { font-family: var(--font-body); font-weight: 700; color: var(--color-plum); margin: var(--sp-3) 0 var(--sp-1); }
.edu-card2-copy p { font-family: var(--font-body); font-size: 0.9rem; line-height: 1.55; color: var(--color-plum-mauve); margin: 0 0 var(--sp-2); }
.edu-card2-copy p:last-child { margin-bottom: 0; }

/* the "velt" — the Social Story card's media: pages scroll VERTICALLY within the card,
   numbered in reading order. setupStoryBelt duplicates #storyBelt for a seamless loop. */
.story-belt {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
}
.story-belt-track { display: flex; flex-direction: column; gap: var(--sp-3); margin: 0; padding: 0; list-style: none; animation: beltScroll 50s linear infinite; }
.story-belt:hover .story-belt-track { animation-play-state: paused; }
.story-page { position: relative; flex: 0 0 auto; width: 100%; }
.story-page img { display: block; width: 100%; height: auto; border-radius: 10px; box-shadow: var(--shadow-soft); }
.story-page-num {
  position: absolute; top: 10px; left: 10px; width: 28px; height: 28px; border-radius: 50%; z-index: 1;
  display: grid; place-items: center; background: var(--color-plum); color: #FBFAF7;
  font-family: var(--font-body); font-weight: 700; font-size: 0.85rem;
}
@keyframes beltScroll { to { transform: translateY(-50%); } }

/* RIGHT — the layered Stellina rig (7 same-canvas PNGs; classes are `rig-*`, NOT `stel-*`,
   because the Stellina SCENE owns `.stel-layer`). Calm idle: head sways; the OPEN eyes/mouth
   (`.rig-active`) fade OUT while the CLOSED ones (`.rig-rest`) fade in → clean blink + neutral. */
.edu-character { display: flex; justify-content: center; align-self: end; }   /* sits low on the panel (Elena) */
.stellina-rig { position: relative; width: 100%; max-width: 400px; aspect-ratio: 1214 / 1933; }   /* larger (Elena) */
.rig-layer { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.rig-head { position: absolute; inset: 0; transform-origin: 50% 35%; animation: stelSway 6.5s ease-in-out infinite; }
.rig-brows { animation: stelBrows 5s ease-in-out infinite; }
.rig-active { animation: stelActive 5s ease-in-out infinite; }   /* eyes-open (quick blink only) */
.rig-rest { opacity: 0; animation: stelRest 5s ease-in-out infinite; }   /* eyes-closed (quick blink only) */
/* Mouth DECOUPLED from the blink (Elena 2026-08-04): it must NOT snap with the eyes.
   Its own long 10s cycle — a slow, gentle open↔closed crossfade every now and then. */
.rig-mouth-open   { animation: stelMouthOpen 10s ease-in-out infinite; }
.rig-mouth-closed { opacity: 0; animation: stelMouthClosed 10s ease-in-out infinite; }
/* Blink retuned 2026-08-04 (Elena): a quick, realistic ~250ms blink once per 5s cycle —
   the old version dwelled closed for ~2s, which read as sleepy. Sway stays on its own 6.5s. */
@keyframes stelSway   { 0%, 100% { transform: rotate(-1.3deg); } 50% { transform: rotate(1.3deg); } }
@keyframes stelBrows  { 0%, 89%, 100% { transform: translateY(0); } 93% { transform: translateY(1%); } }
@keyframes stelActive { 0%, 91% { opacity: 1; } 93%, 94% { opacity: 0; } 96%, 100% { opacity: 1; } }
@keyframes stelRest   { 0%, 91% { opacity: 0; } 93%, 94% { opacity: 1; } 96%, 100% { opacity: 0; } }
/* slow mouth: open most of the cycle, gentle ~1.2s close, hold ~2s, gentle ~1.2s open */
@keyframes stelMouthOpen   { 0%, 50% { opacity: 1; } 62%, 84% { opacity: 0; } 96%, 100% { opacity: 1; } }
@keyframes stelMouthClosed { 0%, 50% { opacity: 0; } 62%, 84% { opacity: 1; } 96%, 100% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .story-belt-track { animation: none; }
  .story-belt { overflow-y: auto; }
  .rig-head, .rig-brows, .rig-active, .rig-rest, .rig-mouth-open, .rig-mouth-closed { animation: none; }
  .rig-active, .rig-mouth-open { opacity: 1; } .rig-rest, .rig-mouth-closed { opacity: 0; }   /* rest at eyes-open + mouth-open */
}
@media (max-width: 860px) {
  .edu-panel { grid-template-columns: 1fr; }
  /* Drop the Stellina character rig on mobile (Elena): stacked single-column it just
     added a big empty gap under the two cards. The two story cards carry the section. */
  .edu-character { display: none; }
}

/* =========================================================
   BRANDING
   ========================================================= */
.branding-hero {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-6);
  margin-bottom: var(--sp-8);
}
.branding-hero-copy {
  flex: 1 1 420px;
  max-width: 560px;
}
.branding-hero-copy .section-intro { margin-bottom: 0; }

/* Swatch belts removed 2026-08-02 — the top logo band carries the colour;
   the rainbow swatch columns competed with it. (.brand-swatch-*/.swatch-* CSS
   and the swatchTrack duplication in main.js were deleted with them.) */

@media (max-width: 760px) {
  .branding-hero { flex-direction: column; }
}

.brand-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--sp-4);
}
.brand-card {
  background: var(--color-card-bg);
  border-radius: var(--radius-card);
  padding: var(--sp-4);
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.brand-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lift); }
.brand-card img { max-width: 180px; margin: 0 auto var(--sp-3); }
.brand-card--note {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  border: 2px dashed var(--color-border);
  box-shadow: none;
  color: var(--color-ink-soft);
}

/* ===== Branding CONCEPT (2026-08-02): identity logo wall + pitch decks =====
   Angle: RANGE + fast story-driven design, not illustration. Equal neutral
   cells so wildly different logos read as one system. */
.brand-block { margin-top: var(--sp-8); }
.brand-block-title {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 3vw, 2.4rem);
  letter-spacing: 0.02em;
  color: var(--color-ink);
  -webkit-text-stroke: 0.4px var(--color-ink);
  margin-bottom: var(--sp-1);
}
.brand-block-lead {
  font-family: var(--font-body);
  color: var(--color-ink-soft);
  max-width: 60ch;
  margin-bottom: var(--sp-4);
}

/* Logo lockup band — 3 cohesive rainbow logos crowning the section, no cells
   (the set is unified enough to sit directly on the bg). Height-normalized so
   the wide Stellina wordmark and square Brainbow read as one row; optical, not
   mathematical — Stellina naturally spans widest. */
/* Logo MARQUEE (auto-belt experiment 2026-08-02). Seamless because JS duplicates
   the set once and we scroll exactly -50%; spacing is per-item margin-right (NOT
   flex `gap`) so every item — including the seam — carries equal trailing space,
   which is what makes -50% line up perfectly. Edge mask fades logos in/out.
   TO REVERT to the static lockup: unwrap .logo-marquee + remove the JS dup, then
   set .logo-band back to { flex-wrap:wrap; width:auto; justify-content:space-between;
   gap:var(--sp-6); animation:none } and drop the margin-right. */
.logo-marquee {
  overflow: hidden;
  margin: 0 0 var(--sp-8);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.logo-band {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  width: max-content;
  animation: logoScroll 34s linear infinite;
}
.logo-marquee:hover .logo-band { animation-play-state: paused; }
@keyframes logoScroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); } /* two identical halves → seamless */
}
.logo-band-item { flex: 0 0 auto; margin-right: var(--sp-12); }
.logo-band-item img {
  display: block;
  width: auto;
  height: auto;
  max-height: 160px;
  max-width: 480px;
  transition: transform var(--dur-fast) var(--ease-out);
}
.logo-band-item img:hover { transform: translateY(-4px) scale(1.03); }
@media (max-width: 760px) {
  .logo-band-item { margin-right: var(--sp-8); }
  .logo-band-item img { max-height: 96px; }
}
@media (prefers-reduced-motion: reduce) {
  .logo-band { animation: none; flex-wrap: wrap; width: auto; justify-content: center; }
  .logo-band-item { margin-right: 0; }
  .logo-band-item img { transition: none; }
  .logo-band-item img:hover { transform: none; }
}

.deck-row {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--sp-4);
}
.deck-card {
  background: #F4ECFF; /* light lilac (the Stellina gradient tone) — was near-white (Elena 2026-08-04) */
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
  display: flex; flex-direction: column;   /* even, full-height boxes across the row */
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.deck-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lift); }
.deck-video {
  display: block; width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--color-ink);
  object-fit: cover;
}
.deck-name {
  font-family: var(--font-body); font-weight: 600;
  color: var(--color-ink);
  padding: var(--sp-3) var(--sp-3) var(--sp-1);
  margin: 0;
}
.deck-blurb {
  font-family: var(--font-body); font-size: 0.9rem; line-height: 1.5;
  color: var(--color-plum-mauve);
  padding: 0 var(--sp-3) var(--sp-3);
  margin: 0;
}
/* Mobile: swipeable "flashcard" carousel (Elena's note — quizlet-style swipe cards, NOT the
   tilted Fine-Art pile). One deck per view via CSS scroll-snap; the ~14% peek of the next
   card signals there's more to swipe to. No JS needed — native touch scrolling drives it. */
@media (max-width: 760px) {
  .deck-row {
    display: flex;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    gap: var(--sp-3);
    padding: 0 var(--sp-2) var(--sp-2);
    scrollbar-width: none;
  }
  .deck-row::-webkit-scrollbar { display: none; }
  .deck-card {
    flex: 0 0 86%;
    scroll-snap-align: center;
  }
  .deck-card:hover { transform: none; box-shadow: var(--shadow-soft); }  /* no hover-lift on touch */
}

/* =========================================================
   STELLINA MAE
   ========================================================= */
.stellina-wordmark {
  max-width: 480px;
  width: 80%;
  margin: 0 auto var(--sp-4);
}
.section-intro--stellina { margin-left: auto; margin-right: auto; margin-bottom: var(--sp-3); text-align: center; }
.stellina-cta { margin-bottom: var(--sp-8); } /* button sits under the paragraph, with room before the scene */

.stellina-preview {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: var(--sp-6);
  align-items: center;
  text-align: left;
  max-width: 960px;
  margin: var(--sp-6) auto 0;
}
.stellina-preview-frame {
  border-radius: var(--radius-art);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  background: #000;
}
.stellina-preview-frame video { width: 100%; display: block; }
.stellina-preview-copy h3 {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 1.6rem;
  margin-bottom: var(--sp-1);
}
@media (max-width: 760px) {
  .stellina-preview { grid-template-columns: 1fr; text-align: center; }
  .stellina-preview-copy { text-align: center; }
}

/* =========================================================
   STELLINA immersive scene — 12 stacked layers (same 1366×768
   canvas): per-layer scroll parallax, a looping Dream Train,
   drifting campfire smoke, and a top fade into the warm white.
   ========================================================= */
.stellina-scene {
  position: relative;
  width: 100vw;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  height: 100vh; /* full-screen immersive (was a cropped 16:9 band) */
  overflow: hidden;
  margin-top: var(--sp-6);
  background: #140a26; /* deep night behind the layers while they load */
}
/* no will-change here: 12 promoted full-size layers was the scroll-jank cause */
.stel-layer { position: absolute; inset: 0; }
.stel-layer img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* top edge dissolves into the warm-white section instead of a hard cut */
.stellina-scene-fade {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 24%;
  z-index: 7; /* sits BETWEEN the sky layers (z≤6) and the castle/trees (z≥8) so only the sky fades — castle + pine stay crisp */
  pointer-events: none;
  background: linear-gradient(to bottom, var(--color-bg) 0%, rgba(251,250,246,0.55) 42%, transparent 100%);
}
.stellina-viewer {
  position: absolute;
  z-index: 25;
  left: 44%;
  top: 56%;
  transform: translate(-50%, -50%);
  width: min(40%, 480px);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 26px 60px rgba(0,0,0,0.5);
  border: 3px solid rgba(255,255,255,0.16);
  background: #000;
}
.stellina-viewer video { width: 100%; display: block; }
.stellina-cta-row { margin-top: var(--sp-6); }

/* Dream Train — sweeps the track and loops; off-screen at both ends so the
   reset is unseen, and it's layer 4 so the forest/trees occlude the edges. */
@keyframes stellinaTrain {
  from { transform: translateX(-55vw); }
  to   { transform: translateX(75vw); }
}
.stel-train { animation: stellinaTrain 24s linear -12s infinite; will-change: transform; } /* -12s delay = train already mid-crossing on load */
.stel-train-b { animation-delay: 0s; } /* 2nd train, offset half a cycle from the first → one is always crossing (seamless) */

/* Campfire smoke — gentle billowing rise, pivoting near its base. */
@keyframes stellinaSmoke {
  0%, 100% { transform: translateX(0) skewX(0deg) scaleY(1); opacity: 0.75; }
  50%      { transform: translateX(7px) skewX(6deg) scaleY(1.06); opacity: 1; }
}
.stel-smoke { transform-origin: 84% 78%; animation: stellinaSmoke 7.5s ease-in-out infinite; will-change: transform; }

/* Clouds — slow, calm horizontal drift. On the <img>, so it composes with the
   wrap's scroll parallax (translateY) rather than fighting it on one element.
   The wrap is scale(1.12) (≈6% bleed each side) and .stellina-scene clips
   overflow, so ±2% never exposes an edge. Scroll-tied site elsewhere, but a
   sky needs its own quiet life — this stays gentle (40s, eased, alternating). */
@keyframes stellinaClouds {
  from { transform: translateX(-2%); }
  to   { transform: translateX(2%); }
}
.stel-clouds { animation: stellinaClouds 40s ease-in-out infinite alternate; will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  .stel-train, .stel-smoke, .stel-clouds { animation: none !important; }
}
@media (max-width: 700px) {
  .stellina-viewer { width: 62%; left: 50%; }
}
/* Mobile crop (Elena's reference image): the 100vh full-screen scene cover-crops the
   16:9 art hard on a portrait phone, cutting off the castle on the right. Drop the scene
   to roughly the art's own aspect so the WHOLE composition — castle, train, sky — shows
   full width. (~16:9 = 56vw; a touch taller keeps it immersive with only a sliver of crop
   top/bottom, and the parallax's scale(1.12) bleed stays hidden.) */
@media (max-width: 760px) {
  .stellina-scene { height: 66vw; min-height: 260px; }
}

/* =========================================================
   CONTACT
   ========================================================= */
/* Connect: single column now (form removed) — big serif heading, invite line,
   and a clickable email, with the grass/flower meadow rooted along the bottom. */
/* overflow:hidden clips the meadow at the section/footer seam so its downward
   dip can't add scrollable white space below the footer (that was the "pull up
   white space" bug). Flowers still reach the bottom, rooting at the soil line.
   min-height gives the section room to breathe (was feeling cramped). */
.section--contact { position: relative; overflow: hidden; min-height: 88vh; padding: var(--sp-16) var(--sp-4); }
/* mahogany "soil" band at the section bottom so the flowers root IN the soil
   (the layered look) while overflow:hidden still prevents the white-space bug.
   The footer below is the same mahogany, so it reads as one continuous ground. */
.section--contact::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: clamp(80px, 9vw, 140px); /* scales with the meadow so the soil always reaches the flower body — no gap */
  background: var(--color-plum-deep); /* dark amethyst footer soil (Elena) — was mahogany ink */
  z-index: 0; /* behind the flowers (z-index 1) and the copy (z-index 2) */
}
.section-inner--contact {
  position: relative;
  z-index: 2; /* copy sits above the meadow */
  max-width: none; /* let the oversized heading fill the width, per mockup */
  text-align: center; /* centered, per Elena's mockup */
}
.contact-title {
  font-family: var(--font-display); /* Bebas Neue, per the mockup */
  font-weight: 400;
  -webkit-text-stroke: 0.014em currentColor; /* bolded */
  color: var(--color-lilac);
  font-size: clamp(4rem, 19vw, 18rem); /* big & screen-filling */
  line-height: 0.85;
  letter-spacing: 0.01em;
  margin: 0 0 var(--sp-4);
  will-change: transform; /* scroll parallax (JS) */
}
.contact-invite {
  color: rgba(251, 250, 246, 0.82);
  font-size: 1.05rem;
  max-width: 46ch;
  margin: 0 auto var(--sp-3); /* centered */
}
.contact-email {
  display: inline-block;
  font-size: clamp(1.1rem, 2.6vw, 1.7rem);
  color: var(--color-bg);
  text-decoration: none;
  border-bottom: 2px solid var(--color-copper);
  padding-bottom: 3px;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.contact-email:hover { color: var(--color-lilac); border-color: var(--color-lilac); }

/* Meadow: the 9 grass/flower layers are all the SAME canvas, stacked, so
   together they compose one cluster (bottom-left, per Elena's mockup). Each
   layer is injected as its own <img> so JS can rustle it independently. */
.contact-meadow {
  position: absolute;
  left: clamp(-2rem, 1vw, 2rem);
  bottom: 0; /* section is now the last element, so bottom:0 = page base → stems flush to the base */
  width: clamp(420px, 60vw, 920px); /* bigger — fills more of the negative space */
  aspect-ratio: 865 / 812; /* exact shared layer canvas so the stack lines up perfectly */
  z-index: 1; /* behind the copy (z-index 2) */
  pointer-events: none; /* reacts to the cursor but never blocks clicks */
}
/* footer credit, merged into this section so the section reaches the page base */
/* footer legal block (attribution legend + copyright) in the soil band */
.contact-footer-legal {
  position: absolute;
  bottom: var(--sp-2);
  left: 0; right: 0;
  z-index: 3; /* above the flowers so it stays readable */
  text-align: center;
  padding: 0 var(--sp-4);
}
.attr-legend {
  max-width: 68ch; margin: 0 auto var(--sp-1);
  color: rgba(250, 245, 236, 0.62);
  font-size: 0.72rem; line-height: 1.5;
}
.attr-legend .attr-mark { color: var(--color-copper); font-size: 1em; vertical-align: baseline; margin: 0 0.15em 0 0; }
.contact-credit {
  margin: 0;
  text-align: center;
  color: rgba(250, 245, 236, 0.6);
  font-size: 0.8rem;
}
.meadow-blade {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom center;
  transform-origin: bottom center; /* rooted at the base — blades pivot from the ground */
  will-change: transform;
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.contact-form label {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.75;
  margin-top: var(--sp-2);
}
.contact-form input, .contact-form textarea {
  background: rgba(250,245,236,0.08);
  border: 1px solid rgba(250,245,236,0.25);
  border-radius: 10px;
  padding: 0.8em 1em;
  color: var(--color-bg);
  font-family: var(--font-body);
  font-size: 1rem;
}
.contact-form input:focus, .contact-form textarea:focus {
  outline: 2px solid var(--color-accent-gold);
  border-color: transparent;
}
.contact-form .btn { margin-top: var(--sp-3); align-self: flex-start; }
.contact-form-note { font-size: 0.8rem; opacity: 0.6; margin-top: var(--sp-2); }

@media (max-width: 760px) {
  .section-inner--contact { grid-template-columns: 1fr; }
  /* Smaller on mobile so it reads as a proportioned pair of stretched words (like the
     desktop) instead of two oversized lines crowding the screen (Elena's note). */
  .contact-title { font-size: clamp(3.2rem, 17vw, 6rem); line-height: 0.9; }
  /* Drop the flower meadow on mobile entirely (Elena, session 7): with no cursor it can't be
     interactive, and it was crowding the fine-print. */
  .contact-meadow { display: none; }
  /* Taller mahogany "soil" footer so the attribution legend + credit fit fully inside it
     (they were spilling above the band). No flowers now, so it's a clean footer strip. */
  .section--contact::after { height: clamp(155px, 44vw, 210px); }
  .contact-footer-legal { bottom: var(--sp-3); }
  .attr-legend { max-width: 40ch; }
}

/* =========================================================
   FOOTER
   ========================================================= */
.site-footer {
  background: var(--color-ink);
  color: rgba(250,245,236,0.6);
  text-align: center;
  padding: var(--sp-3);
  font-size: 0.8rem;
}

/* =========================================================
   RESPONSIVE NAV (mobile)
   ========================================================= */
@media (max-width: 860px) {
  .nav-toggle { display: flex; }
  .primary-nav {
    position: fixed;
    top: 0; right: 0;
    height: 100vh;
    width: min(78vw, 320px);
    background: var(--color-plum); /* plum slide-in panel to match the bar */
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: var(--sp-4);
    padding: var(--sp-8) var(--sp-4);
    transform: translateX(100%);
    transition: transform var(--dur-med) var(--ease-out);
    box-shadow: -10px 0 30px rgba(0,0,0,0.1);
  }
  .primary-nav.is-open { transform: translateX(0); }
  .nav-cta { align-self: flex-start; }
}

/* =========================================================
   COLOR — the site is full-colour throughout. The grayscale-
   to-colour narrative was retired (it depended on the film reel
   + crossword, both being reworked). The hero sketch colour-
   reveal wipe stays — it stands on its own. Do NOT reintroduce
   a global filter: grayscale().
   ========================================================= */

/* BRAINBOW REVEAL section removed 2026-08-02 — see index.html note. */

/* =========================================================
   AMBIENT RAINBOW-LIGHT REFLECTION
   Fixed decorative layer behind all content. Fades in gradually
   starting at Fine Art (JS toggles .is-visible via IntersectionObserver),
   nudges slightly warmer/more visible through the Personal Studio wheel
   (.is-vivid), then stays restrained for the rest of the page. Never
   applied directly with any real opacity over the black-and-white film
   reel or over text — it's the cream space around content that carries
   the color, per Elena's spec. Slow independent drift per blob plus a
   gentle scroll-linked parallax (~15% of scroll speed) via JS.
   ========================================================= */
/* #main needs position:relative (but no z-index of its own — that caused
   an earlier bug, see below) so it can serve as the containing block for
   .ambient-light via normal position:absolute — see AMBIENT RAINBOW-LIGHT
   REFLECTION below for why this replaced position:fixed entirely. */
#main { position: relative; }
.ambient-light {
  position: absolute;
  left: 0;
  width: 100%;
  height: 100vh;
  top: 0; /* set dynamically via JS on scroll — see setupAmbientLight in main.js */
  z-index: 1; /* explicitly above section backgrounds (which are z-index:auto) — see .section-inner below for how content stays above this in turn */
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.8s var(--ease-out);
}
.ambient-light.is-visible { opacity: 1; }
.light-blob {
  position: absolute;
  width: 55vw;
  height: 55vw;
  max-width: 780px;
  max-height: 780px;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.32; /* bumped up again — 0.22 read as too faint once actually visible */
  transition: opacity 2.2s var(--ease-out);
  will-change: transform;
}
.ambient-light.is-vivid .light-blob { opacity: 0.46; } /* slightly warmer through the colorful painting section */
.light-blob--pink {
  top: 8%;
  left: 4%;
  background: radial-gradient(circle, rgba(230,140,150,0.9) 0%, rgba(230,140,150,0) 70%);
  animation: driftA 70s ease-in-out infinite;
}
.light-blob--yellow {
  top: 12%;
  right: 2%;
  background: radial-gradient(circle, rgba(230,190,110,0.9) 0%, rgba(230,190,110,0) 70%);
  animation: driftB 85s ease-in-out infinite;
  animation-delay: -20s;
}
.light-blob--cyan {
  bottom: 10%;
  left: 15%;
  background: radial-gradient(circle, rgba(90,170,180,0.85) 0%, rgba(90,170,180,0) 70%);
  animation: driftC 95s ease-in-out infinite;
  animation-delay: -45s;
}
.light-blob--lavender {
  bottom: 14%;
  right: 8%;
  background: radial-gradient(circle, rgba(150,130,180,0.85) 0%, rgba(150,130,180,0) 70%);
  animation: driftA 78s ease-in-out infinite;
  animation-delay: -30s;
}
@keyframes driftA {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(3%, -2.5%) scale(1.06); }
}
@keyframes driftB {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-2.5%, 3%) scale(1.05); }
}
@keyframes driftC {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(2%, 2%) scale(1.07); }
}
@media (prefers-reduced-motion: reduce) {
  .light-blob { animation: none !important; }
}
