/* ==========================================================================
   Chu Chu — portfolio
   Art direction: Sable, ART-DIRECTION.md v1 (tokens + treatment lifted from
   style-plate.html). Structure/content: BRIEF.md.
   One typeface, two backgrounds, one accent, numbers instead of ornament.
   ========================================================================== */

/* ---- fonts (self-hosted per ART-DIRECTION.md §2) ------------------------ */
@font-face {
  font-family: "Work Sans";
  src: url("fonts/work-sans-var.woff2") format("woff2");
  font-weight: 300 600;
  font-style: normal;
  font-display: swap;
}
/* two glyphs only — 中文 — so the EN/中文 mark never renders tofu (§2) */
@font-face {
  font-family: "Noto Sans TC Subset";
  src: url("fonts/noto-tc-name-subset.woff2") format("woff2");
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
  /* 中文 (the EN/中文 mark) + 陶竺君 (the wordmark — her name, every language) */
  unicode-range: U+4E2D, U+6587, U+9676, U+7AFA, U+541B;
}

/* ---- tokens (ART-DIRECTION.md §1 — lifted verbatim) ---------------------- */
:root {
  --dark-1: #16100D;
  --dark-2: #1E1611;
  --cream-bg: #EDE4D3;
  --cream-text: #F0E6D8;
  --ink: #2A2018;
  --ochre: #C17F3E;
  --ochre-bright: #D99458;

  --cream-dim: rgba(240,230,216,0.62);
  --cream-faint: rgba(240,230,216,0.34);
  --cream-hairline: rgba(240,230,216,0.14);
  --ink-dim: rgba(42,32,24,0.66);
  --ink-hairline: rgba(42,32,24,0.16);
  --thumb-1: #14100D;
  --thumb-2: #2B211A;

  --hero-grad: linear-gradient(160deg, var(--dark-1) 0%, var(--dark-2) 100%);
  --font: "Work Sans", "Helvetica Neue", Arial, "Noto Sans TC Subset",
          "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", sans-serif;

  --gutter: clamp(20px, 5vw, 64px);
  --section-pad: clamp(96px, 14vh, 176px);
}

/* ---- base ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-weight: 400;
  line-height: 1.65;
  /* the screening room is ONE paint: hero/context/work sit transparent on this
     single gradient so no seams can exist between the dark beats; the cream
     sections opaquely cover their range (AD §0/§3).
     The opalescent washes themselves live on the fixed .opal layer (end of
     file) so they can breathe; the body keeps only the dark base. */
  background: linear-gradient(170deg, var(--dark-1) 0%, var(--dark-2) 100%) var(--dark-1);
  background-attachment: fixed;
  color: var(--cream-text);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, ul, figure { margin: 0; }
ul { padding: 0; list-style: none; }
a { color: inherit; }
img { max-width: 100%; display: block; }
button { font: inherit; color: inherit; }

.col { max-width: 1120px; margin: 0 auto; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute;
  left: 1rem; top: -3rem;
  z-index: 100;
  padding: 10px 18px;
  background: var(--ochre);
  color: var(--dark-1);
  text-decoration: none;
  font-weight: 500;
  transition: top 150ms ease-out;
}
.skip-link:focus { top: 1rem; }

:focus-visible {
  outline: 2px solid var(--ochre-bright);
  outline-offset: 3px;
}

::selection { background: var(--ochre); color: var(--dark-1); }

/* shared type roles (§2) */
.eyebrow {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ochre-bright);
}
.cream-section .eyebrow { color: var(--ochre); }

.section-title {
  font-size: clamp(1.6rem, 3vw, 2.4rem);
  font-weight: 500;
  line-height: 1.15;
  margin-top: 16px;
}

.body-copy {
  font-size: 17px;
  line-height: 1.65;
  max-width: 62ch;
  margin-top: 16px;
  color: var(--cream-dim);
}
.cream-section .body-copy { color: var(--ink-dim); }
.body-copy p + p { margin-top: 1.3em; }

/* timecodes: the editor's tell (§2) */
.scroll-cue {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ==========================================================================
   NAV — minimal, fixed; ink swap after the hard cut
   ========================================================================== */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding: 20px var(--gutter) 26px;
  color: var(--cream-text);
  /* soft dark scrim so content scrolling under the fixed nav never collides
     with the wordmark/links; .nav--light's solid cream replaces it */
  background: linear-gradient(to bottom, rgba(22,16,13,0.92) 0%, rgba(22,16,13,0) 100%);
  transition: background 200ms ease-out, color 200ms ease-out;
}
.nav__wordmark {
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.nav__links {
  display: flex;
  gap: clamp(18px, 3vw, 40px);
}
.nav__links a {
  text-decoration: none;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream-dim);
  transition: color 200ms ease-out;
}
.nav__links a:hover { color: var(--ochre-bright); }

/* after the cut: house-lights nav */
.nav--light {
  background: var(--cream-bg);
  color: var(--ink);
  border-bottom: 1px solid var(--ink-hairline);
}
.nav--light .nav__links a { color: var(--ink-dim); }
.nav--light .nav__links a:hover { color: var(--ochre); }

/* ==========================================================================
   BEAT 1 · COLD OPEN — full-bleed background video (AD §8 v2, lifted verbatim)
   ========================================================================== */
.hero {
  position: relative;
  overflow: hidden; /* contains the full-bleed video */
  background: transparent;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: 96px var(--gutter);
}
.hero .col { width: 100%; position: relative; z-index: 2; }

/* no src yet: the poster carries the frame. Her footage = swap src + poster,
   zero layout change (the element always fills the hero via object-fit) */

/* the Showreel CTA, re-homed to the hero (§8 v2 — lifted verbatim; +cursor,
   since ours is a real <button>, not an anchor) */
.reel-cta {
  display: inline-flex; align-items: center; gap: 12px; margin-top: 36px; padding: 13px 24px;
  border: 1px solid rgba(240,230,216,0.28); border-radius: 2px; background: rgba(22,16,13,0.25);
  font-size: 12px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--cream-text); text-decoration: none;
  transition: border-color 200ms ease-out, color 200ms ease-out;
  cursor: pointer;
}
.reel-cta::before {   /* the ochre play tick */
  content: ""; width: 0; height: 0; border-left: 9px solid var(--ochre);
  border-top: 6px solid transparent; border-bottom: 6px solid transparent;
}
.reel-cta:hover, .reel-cta:focus-visible { border-color: var(--ochre); color: var(--ochre-bright); }

.hero__title {
  font-size: clamp(2.6rem, 5.5vw, 4.4rem);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.015em;
  margin: 20px 0 24px;
  max-width: 18ch;
}
.hero__sub {
  font-size: 17px;
  line-height: 1.65;
  color: var(--cream-dim);
  max-width: 52ch;
}

.scroll-cue {
  position: absolute;
  z-index: 2; /* above the hero scrim, or it can't be clicked */
  left: var(--gutter);
  bottom: 36px;
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--cream-faint);
  transition: color 200ms ease-out;
}
.scroll-cue::after {
  content: "";
  width: 56px;
  height: 1px;
  background: var(--cream-hairline);
}
.scroll-cue:hover { color: var(--ochre-bright); }

/* ==========================================================================
   BEAT 2 · CONTEXT — dark, continuous with the hero
   ========================================================================== */
.context {
  background: transparent;
  padding: var(--section-pad) var(--gutter);
}
.context__text {
  font-size: clamp(1.25rem, 2.2vw, 1.6rem);
  font-weight: 300;
  line-height: 1.55;
  max-width: 44ch;
  color: var(--cream-text);
}

/* ==========================================================================
   BEAT 3 · SELECTED WORK — slates + monitor-frame thumbs
   ========================================================================== */
.work {
  background: transparent;
  padding: var(--section-pad) var(--gutter);
}

/* ---- editorial category headers (§4 v2 — timecode motif retired per Chu Chu) ----
   the title carries the display voice; the hairline rule stays (structural, quiet);
   piece-count = the editorial metadata at the rule's far end */
.cat-head {
  display: flex;
  align-items: baseline;
  gap: 24px;
  margin-bottom: 40px;
}
.cat-title {
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--cream-text);
  margin: 0;
}
.cat-head::after {
  content: "";
  flex: 1;
  order: 2;
  height: 1px;
  background: var(--cream-hairline);
  align-self: center;
}
.cat-count {
  order: 3;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream-faint);
  white-space: nowrap;
}

/* ---- grids (§5) ---- */
.grid {
  display: grid;
  gap: 20px;
  margin-bottom: 72px;
}
.grid--last { margin-bottom: 0; }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cols-2 { grid-template-columns: repeat(2, 1fr); }

/* ---- the placeholder thumb: a dark monitor frame (§5) ----
   Swappability contract: drop <img src="…" alt=""> as the thumb's first child.
   The field (with safe-area guides) hides via :has(img); the lower-third,
   timecode, and hover treatment persist on top of the real still. */
.thumb {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 0;
  border-radius: 2px;
  background: none;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
}
.thumb:focus-visible {
  outline: 1px solid var(--ochre);
  outline-offset: 4px;
}

.thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: filter 280ms ease-out;
}
.thumb:has(img) .thumb-field { display: none; }

/* hover preview (mounted by script.js on hover/focus) — sits over the still,
   under the grade + lower-third */
.thumb-preview {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* layer 1 — the field: gradient + per-category key light */
.thumb-field {
  position: absolute;
  inset: 0;
  display: block;
  border-radius: 2px;
  background:
    radial-gradient(120% 90% at var(--key-x, 22%) var(--key-y, 18%),
      var(--key-tint, rgba(217,148,88,0.10)) 0%, transparent 55%),
    linear-gradient(150deg, var(--thumb-1) 0%, var(--thumb-2) 100%);
  transition: filter 280ms ease-out;
}
/* per-category key light: the only variable that changes (§5) */
.cat-brand .thumb-field { --key-x: 22%; --key-y: 18%; --key-tint: rgba(217,148,88,0.10); }
.cat-film  .thumb-field { --key-x: 18%; --key-y: 86%; --key-tint: rgba(193,127,62,0.16); }
.cat-doc   .thumb-field { --key-x: 50%; --key-y: 4%;  --key-tint: rgba(240,230,216,0.07); }

/* layer 5 — grain: shared feTurbulence data-URI */
.thumb-field::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* layer 2 — safe-area guides: action-safe 4%, title-safe 9% */
.safe-a, .safe-t {
  position: absolute;
  display: block;
  border: 1px solid var(--cream-hairline);
  border-radius: 1px;
  transition: border-color 280ms ease-out;
  pointer-events: none;
}
.safe-a { inset: 4%; }
.safe-t { inset: 9%; }

/* layer 2.5 — legibility grade under the lower-third, only when a real still
   is present (placeholder fields are already dark; light frames — white text
   cards, bright product shots — need the grade or the cream type drowns) */
.thumb:has(img)::after {
  content: "";
  position: absolute;
  inset: 55% 0 0 0;
  background: linear-gradient(180deg, rgba(20, 16, 13, 0) 0%, rgba(20, 16, 13, 0.72) 78%);
  pointer-events: none;
}

/* layer 3 — title as lower-third */
.lower-third {
  z-index: 1;
  position: absolute;
  left: 11%;
  bottom: 12%;
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-width: 78%;
}
.lt-title {
  font-size: 15px;
  font-weight: 500;
  color: var(--cream-text);
  padding-left: 10px;
  border-left: 2px solid var(--ochre);
  line-height: 1.3;
}
.lt-cred {
  font-size: 11px;
  color: var(--cream-dim);
  padding-left: 12px;
}

/* layer 6 — play affordance + hover (no scale-zoom; restraint) */
.play {
  z-index: 1;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 0;
  height: 0;
  border-left: 18px solid var(--ochre);
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  opacity: 0;
  transition: opacity 280ms ease-out;
}
.play--on { opacity: 0.9; }

.thumb:hover .thumb-field, .thumb:focus-visible .thumb-field,
.thumb:hover img, .thumb:focus-visible img { filter: brightness(1.04); }
.thumb:hover .play, .thumb:focus-visible .play { opacity: 1; }
.thumb:hover .safe-a, .thumb:hover .safe-t,
.thumb:focus-visible .safe-a, .thumb:focus-visible .safe-t {
  border-color: rgba(240,230,216,0.22);
}

/* ==========================================================================
   THE HARD CUT → BEATS 4/5 · CREAM
   ========================================================================== */
.cream-section {
  background: var(--cream-bg);
  color: var(--ink);
}

.about { padding: var(--section-pad) var(--gutter); }

.about__cols {
  display: grid;
  grid-template-columns: minmax(0, 62ch) minmax(200px, 280px);
  gap: clamp(40px, 6vw, 96px);
  align-items: start;
}
.body-copy em { font-style: italic; }

/* portrait slot — same monitor-frame treatment, portrait ratio (§3 beat 4) */
.portrait {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: 2px;
  overflow: hidden;
  margin-top: 8px;
}
.portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* on cream the monitor-frame flips to a "mounted print": a dark slab right
   after the house-lights cut re-installs a screen and fights the beat (AD §0);
   warm paper + ink guides reads as a frame waiting for her portrait instead.
   The swap contract is unchanged — a real photo replaces all of it. */
.portrait .thumb-field {
  background:
    radial-gradient(120% 90% at 50% 20%, rgba(193,127,62,0.07) 0%, transparent 55%),
    linear-gradient(rgba(42,32,24,0.055), rgba(42,32,24,0.055));
  border: 1px solid var(--ink-hairline);
}
.portrait .thumb-field::before { opacity: 0.03; }
.portrait .safe-a, .portrait .safe-t { border-color: var(--ink-hairline); }
.portrait img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.portrait:has(img) .thumb-field { display: none; }

.contact {
  padding: var(--section-pad) var(--gutter);
  border-top: 1px solid var(--ink-hairline);
}
.contact .body-copy { margin-top: 20px; }
.sep { color: var(--ochre); padding: 0 6px; }

.text-link {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--ochre);
  transition: border-color 200ms ease-out, color 200ms ease-out;
  overflow-wrap: anywhere;
}
.text-link:hover { color: var(--ochre); }

.cta {
  display: inline-block;
  margin-top: 28px;
  padding: 14px 28px;
  background: var(--ochre);
  color: var(--dark-1);
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: 2px;
  transition: background 200ms ease-out;
}
.cta:hover { background: var(--ochre-bright); }

/* ---- footer (cream, quiet) ---- */
.footer { padding: 0 var(--gutter) 48px; }
.footer__mark {
  padding-top: 72px;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
.footer__copy {
  margin-top: 8px;
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
}

/* ==========================================================================
   LIGHTBOX (§6) — dark scrim, larger placeholder, title + tc beneath
   ========================================================================== */
.lightbox {
  border: 0;
  padding: 0;
  background: transparent;
  /* cinema-sized: as wide as the screen allows while the 16:9 player + text
     strip still fit the viewport (the svh term backs the width out of the
     media's height + ~12rem of text/padding). */
  max-width: min(96rem, calc(100vw - 2rem), calc((100svh - 12rem) * 16 / 9));
  width: 100%;
  max-height: calc(100svh - 2rem);
  overflow-y: auto;
}
.lightbox::backdrop { background: rgba(20, 16, 13, 0.92); }

.lightbox__frame {
  position: relative;
  background: var(--dark-2);
  border: 1px solid var(--cream-hairline);
  border-radius: 2px;
  color: var(--cream-text);
  /* taller top pad = a header strip the close button owns; it sits attached
     in the corner, fully above the video */
  padding: 52px clamp(16px, 2.5vw, 28px) clamp(16px, 2.5vw, 28px);
}

.lightbox__close {
  position: absolute;
  top: 6px; right: 8px;
  z-index: 5;
  width: 40px; height: 40px;
  border: 0;
  background: none;
  color: var(--cream-dim);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition: color 200ms ease-out;
}
.lightbox__close:hover { color: var(--ochre-bright); }

/* the media slot reuses the monitor-frame treatment; a category class
   (cat-brand / cat-film / cat-doc) is applied on open so the key light matches */
.lightbox__media {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 2px;
  overflow: hidden;
  background-color: var(--thumb-1);
}
.lightbox__media iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* self-hosted player (mounted by script.js) — same frame as the iframe path;
   letterboxing over the monitor-frame dark, never a stretched crop */
.lightbox__media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--thumb-1);
}
.lightbox__media.has-video .thumb-field,
.lightbox__media.has-video .play { display: none; }

.lightbox__text { padding: 22px 4px 8px; }
.lightbox__kicker {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ochre-bright);
}
.lightbox__title {
  margin-top: 8px;
  font-size: clamp(1.3rem, 3vw, 1.7rem);
  font-weight: 500;
  line-height: 1.2;
}
.lightbox__credits {
  margin-top: 10px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--cream-faint);
  max-width: 62ch;
}
.lightbox__desc {
  margin-top: 10px;
  color: var(--cream-dim);
  max-width: 62ch;
}
.lightbox__note {
  margin-top: 18px;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream-faint);
}

/* text-link sits on dark inside a dialog (default .text-link is ink-on-cream) */
.lightbox .text-link { color: var(--cream-text); }
.lightbox .text-link:hover { color: var(--ochre-bright); }

/* ==========================================================================
   MOTION (§6) — one pattern: fade-up 12px, 300ms ease-out, 60ms grid stagger
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }

  .js [data-reveal] {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 300ms ease-out, transform 300ms ease-out;
  }
  .js [data-reveal].is-visible {
    opacity: 1;
    transform: none;
  }

  /* stagger within a grid */
  .js [data-stagger] > li {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 300ms ease-out, transform 300ms ease-out;
  }
  .js [data-stagger].is-visible > li {
    opacity: 1;
    transform: none;
  }
  .js [data-stagger].is-visible > li:nth-child(2) { transition-delay: 60ms; }
  .js [data-stagger].is-visible > li:nth-child(3) { transition-delay: 120ms; }
  .js [data-stagger].is-visible > li:nth-child(4) { transition-delay: 180ms; }
  .js [data-stagger].is-visible > li:nth-child(5) { transition-delay: 240ms; }
  .js [data-stagger].is-visible > li:nth-child(6) { transition-delay: 300ms; }

  .lightbox[open] .lightbox__frame { animation: lb-in 250ms ease-out both; }
  @keyframes lb-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
  html { scroll-behavior: auto; }
}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 900px) {
  .cols-3 { grid-template-columns: repeat(2, 1fr); }

  .about__cols { grid-template-columns: 1fr; }
  .portrait { max-width: 280px; }
}

@media (max-width: 640px) {
  .cols-3, .cols-2 { grid-template-columns: 1fr; }

  .nav { padding: 16px var(--gutter); }
  .nav__links { gap: 16px; }

  .scroll-cue::after { width: 36px; }

  .lightbox__text { padding-top: 16px; }

}

/* ==========================================================================
   LIVING OPALESCENCE — six independent colour blobs, wandering.
   Each blob is one soft radial gradient on its own multi-waypoint drift with
   a co-prime cycle length (23-47s), so the field slowly flows, merges and
   splits and never visibly repeats — lava in the emulsion, not two plates.
   transform/opacity only (gradients pre-softened, no filters) = composited.
   Cream sections opaquely cover it; reduced-motion holds a still field.
   ========================================================================== */
.opal {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}
.ob {
  position: absolute;
  border-radius: 50%;
  will-change: transform, opacity;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}
/* amber — the big top-left presence */
.ob1 {
  width: 72vmax; height: 58vmax; top: -24vmax; left: -18vmax;
  background: radial-gradient(40% 34% at 44% 42%, rgba(232,150,78,0.3) 0%, rgba(232,150,78,0.135) 34%, rgba(232,150,78,0.054) 52%, rgba(232,150,78,0.021) 68%, rgba(232,150,78,0.006) 82%, rgba(232,150,78,0) 96%),
    radial-gradient(32% 38% at 62% 58%, rgba(232,150,78,0.22) 0%, rgba(232,150,78,0.099) 34%, rgba(232,150,78,0.040) 52%, rgba(232,150,78,0.015) 68%, rgba(232,150,78,0.004) 82%, rgba(232,150,78,0) 96%);
  animation: ob-drift-1 44s infinite alternate ease-in-out;
}
/* teal — mid-field wanderer */
.ob2 {
  width: 54vmax; height: 48vmax; top: 4vmax; left: 28vmax;
  background: radial-gradient(38% 30% at 42% 38%, rgba(66,172,166,0.22) 0%, rgba(66,172,166,0.099) 34%, rgba(66,172,166,0.040) 52%, rgba(66,172,166,0.015) 68%, rgba(66,172,166,0.004) 82%, rgba(66,172,166,0) 96%),
    radial-gradient(30% 36% at 58% 62%, rgba(66,172,166,0.17) 0%, rgba(66,172,166,0.077) 34%, rgba(66,172,166,0.031) 52%, rgba(66,172,166,0.012) 68%, rgba(66,172,166,0.003) 82%, rgba(66,172,166,0) 96%);
  animation: ob-drift-2 56s infinite alternate ease-in-out;
  animation-delay: -20s;
}
/* violet — top-right counterweight */
.ob3 {
  width: 60vmax; height: 50vmax; top: -20vmax; right: -16vmax;
  background: radial-gradient(38% 32% at 40% 44%, rgba(164,112,224,0.27) 0%, rgba(164,112,224,0.122) 34%, rgba(164,112,224,0.049) 52%, rgba(164,112,224,0.019) 68%, rgba(164,112,224,0.005) 82%, rgba(164,112,224,0) 96%),
    radial-gradient(32% 40% at 64% 54%, rgba(164,112,224,0.2) 0%, rgba(164,112,224,0.090) 34%, rgba(164,112,224,0.036) 52%, rgba(164,112,224,0.014) 68%, rgba(164,112,224,0.004) 82%, rgba(164,112,224,0) 96%);
  animation: ob-drift-3 47s infinite alternate ease-in-out;
  animation-delay: -14s;
}
/* Waypoint paths — irregular loops, not orbits; scale breathes so blobs
   swell into each other and thin back out. */
@keyframes ob-drift-1 {
  0%   { transform: translate(0, 0) scale(1) rotate(0deg);            opacity: 0.9;  }
  30%  { transform: translate(16vw, 10vh) scale(1.18) rotate(14deg);   opacity: 1;    }
  55%  { transform: translate(6vw, 26vh) scale(0.92) rotate(26deg);    opacity: 0.75; }
  80%  { transform: translate(-8vw, 14vh) scale(1.24) rotate(9deg);   opacity: 0.95; }
  100% { transform: translate(-14vw, -4vh) scale(1.05) rotate(-8deg);  opacity: 0.85; }
}
@keyframes ob-drift-2 {
  0%   { transform: translate(0, 0) scale(1.1) rotate(0deg);          opacity: 0.7;  }
  25%  { transform: translate(-18vw, 12vh) scale(0.9) rotate(-12deg);   opacity: 1;    }
  50%  { transform: translate(-6vw, 30vh) scale(1.3) rotate(-24deg);    opacity: 0.8;  }
  75%  { transform: translate(14vw, 18vh) scale(0.95) rotate(-10deg);   opacity: 1;    }
  100% { transform: translate(20vw, -8vh) scale(1.15) rotate(6deg);   opacity: 0.75; }
}
@keyframes ob-drift-3 {
  0%   { transform: translate(0, 0) scale(1) rotate(0deg);            opacity: 0.85; }
  35%  { transform: translate(-14vw, 16vh) scale(1.22) rotate(10deg);  opacity: 1;    }
  60%  { transform: translate(-24vw, 4vh) scale(0.88) rotate(-14deg);   opacity: 0.7;  }
  85%  { transform: translate(-10vw, 28vh) scale(1.12) rotate(22deg);  opacity: 0.95; }
  100% { transform: translate(4vw, 12vh) scale(1) rotate(12deg);       opacity: 0.8;  }
}
/* dither veil — ~4% fractal noise over the blobs breaks up 8-bit banding on
   the long dark falloffs (the "ribbing"); static, tiled, no filters animate */
.opal::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.07;
}

@media (prefers-reduced-motion: reduce) {
  .ob { animation: none; }
}

/* ==========================================================================
   SHARE — per-card corner button + the copy-link dialog.
   The button sits above the thumb (sibling, never nested); quiet until the
   card is hovered/focused, ochre on its own hover. Dialog follows the
   lightbox family: dark frame, hairlines, one accent.
   ========================================================================== */
.card { position: relative; }
.share {
  position: absolute;
  top: 10px; right: 10px;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  padding: 0;
  border: 1px solid rgba(240,230,216,0.28);
  border-radius: 50%;
  background: rgba(22,16,13,0.55);
  color: var(--cream-dim);
  cursor: pointer;
  opacity: 0;
  transition: opacity 160ms ease-out, border-color 160ms ease-out, color 160ms ease-out;
}
.card:hover .share, .card:focus-within .share { opacity: 1; }
.share:hover, .share:focus-visible { border-color: var(--ochre); color: var(--ochre-bright); }
/* touch devices have no hover: keep the button always present, slightly quieter */
@media (hover: none) { .share { opacity: 0.8; } }

.sharebox {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: min(30rem, calc(100vw - 2rem));
  width: 100%;
}
.sharebox::backdrop { background: rgba(20, 16, 13, 0.86); }
.sharebox__frame {
  position: relative;
  background: var(--dark-2);
  border: 1px solid rgba(240,230,216,0.14);
  padding: 28px 28px 24px;
}
.sharebox__close {
  position: absolute;
  top: 8px; right: 10px;
  border: 0; background: none;
  font-size: 22px; line-height: 1;
  color: var(--cream-dim);
  cursor: pointer;
}
.sharebox__close:hover { color: var(--ochre-bright); }
.sharebox__title {
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1.25;
  margin-top: 10px;
  color: var(--cream-text);
}
.sharebox__row {
  display: flex;
  gap: 10px;
  margin-top: 18px;
}
.sharebox__url {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  font: inherit;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--cream-text);
  background: rgba(22,16,13,0.7);
  border: 1px solid rgba(240,230,216,0.2);
  border-radius: 2px;
}
.sharebox__copy {
  padding: 10px 18px;
  border: 1px solid var(--ochre);
  border-radius: 2px;
  background: none;
  font-size: 12px; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ochre-bright);
  cursor: pointer;
  white-space: nowrap;
  transition: background 160ms ease-out, color 160ms ease-out;
}
.sharebox__copy:hover, .sharebox__copy:focus-visible { background: var(--ochre); color: var(--dark-1); }
.sharebox__hint {
  margin-top: 12px;
  font-size: 13px;
  color: var(--cream-faint);
}


/* share, lightbox variant: static corner of the text block, always visible */
.lightbox__text { position: relative; }
.share--lightbox {
  position: absolute;
  top: 18px; right: 20px;
  opacity: 1;
}
.lightbox__text .share--lightbox { /* the card rule hides .share until hover; this one never hides */
  opacity: 1;
}

/* credits in the detail view — the quiet line under the description */
.lightbox__credits {
  margin-top: 10px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--cream-faint);
}
.lightbox__credits[hidden] { display: none; }
