/* /css/memorial-v2.css — DESIGN SANDBOX: "Editorial memorial" redesign for
   /memorial-v2/<slug>. A parallel restyle of the live memorial front door
   (memorial.css) used to trial the editorial layout before promotion.

   Editorial language (from the approved mockup):
     - centered serif masthead: "In loving memory" eyebrow → name → lifetime
     - a sage hero band with the circular portrait overlapping its bottom edge
     - the life story as a large-serif drop-cap lede (hidden when no description)
     - Roman-numeraled "chapter" section heads (I — Photographs & videos, II — …)
     - a quiet resting-place strip, a centered candle moment, flowing-serif
       tributes (in the tributes modal)

   ⚠️ Preserves 100% of the live JS wiring (memorial-v2.js == memorial.js): every
   element id / JS-created class from memorial.js is styled here. The modals,
   lightbox, media cells, tributes wall, candle/flame animations, photo-teaser
   drift constraints, .reveal/.is-visible, dropzone, and dark mode all carry over.
   Design-system tokens from CLAUDE.md. */

:root {
    --bg: #FAF8F5;
    --surface: #ffffff;
    --text: #1c1917;
    --muted: #78716c;
    --border: rgba(0, 0, 0, 0.09);
    --accent: #4a7c59;
    --accent-dark: #3a6147;
    --accent-bg: #f0f5f1;
    --ink: #1a3325;
    --serif: Georgia, "Times New Roman", serif;
    --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --hero-grad: linear-gradient(150deg, #2f5740 0%, #1a3325 100%);
    --radius-sm: 8px;
    --radius-md: 14px;
    --radius-lg: 20px;
    /* journey-band thread hairline (rest / hover) — the #197 journey chapter */
    --jb-thread: rgba(58, 97, 71, 0.30);
    --jb-thread-strong: rgba(58, 97, 71, 0.62);
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--sans);
    line-height: 1.7;
    font-size: 17px;
    -webkit-font-smoothing: antialiased;
}
html { -webkit-text-size-adjust: 100%; }
::selection { background: #dce8df; }

h1, h2, h3 {
    font-family: var(--serif);
    font-weight: 400;
    letter-spacing: -0.01em;
}
.muted { color: var(--muted); font-weight: 400; }

.wordmark {
    font-family: 'Great Vibes', cursive;
    color: var(--accent-dark);
    letter-spacing: 0.01em;
}
/* logo.css (the embedded Great Vibes font) is async-loaded off the render-
   blocking path (memorial.html head script). Hide the wordmark until it lands
   so it can never flash the heavy bold-cursive system fallback; the script's
   load/error/4 s-timeout handlers remove .gv-wait. */
html.gv-wait .wordmark { visibility: hidden; }

/* ── Topbar ──────────────────────────────────────────────────────────── */
.topbar {
    position: sticky;
    top: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px clamp(20px, 5vw, 48px);
    background: rgba(250, 248, 245, 0.86);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--border);
}
.topbar .wordmark { font-size: 1.9rem; line-height: 1; text-decoration: none; }
/* ── Collapsing-header rules (REUSABLE — keep in lockstep with the dashboard's inline copy).
   Size + speed/easing of the collapse; reuse these tokens on any page that adopts a
   collapsing header so they all feel identical. ── */
:root {
  --ch-dur: 0.8s;                              /* transition duration — peaceful */
  --ch-ease: cubic-bezier(0.37, 0, 0.63, 1);   /* ease-in-out sine (gentle start + end) */
  --ch-portrait: 46px;                         /* condensed circular portrait size */
  --ch-name-size: 1.26rem;                     /* condensed name font-size */
}
/* Collapsing header: once the hero scrolls out of view, the sticky bar's
   wordmark crossfades to the person's portrait + name, so their identity merges
   into the bar as one designed unit. Crossfade handoff (IntersectionObserver) —
   NOT scroll-linked per-frame work, which would starve the iOS hero video. */
.topbar-lead { position: relative; display: flex; align-items: center; }
.topbar-brand { transition: opacity var(--ch-dur) var(--ch-ease); }
.topbar.condensed .topbar-brand { opacity: 0; pointer-events: none; }
.topbar-identity {
    position: absolute;
    left: 0;
    top: 50%;
    display: flex;
    align-items: center;
    gap: 12px;
    max-width: min(60vw, 460px);
    transform: translateY(-50%) translateX(-8px);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ch-dur) var(--ch-ease), transform var(--ch-dur) var(--ch-ease);
}
.topbar.condensed .topbar-identity { opacity: 1; transform: translateY(-50%); pointer-events: auto; }
.ti-portrait {
    width: var(--ch-portrait); height: var(--ch-portrait); flex: 0 0 auto;
    border-radius: 50%;
    object-fit: cover; object-position: 50% 50%;
    border: 2px solid var(--bg);
    box-shadow: 0 2px 9px rgba(26, 51, 37, 0.22);
}
.ti-name {
    font-family: var(--serif);
    font-size: var(--ch-name-size);
    line-height: 1.1;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (max-width: 640px) {
    .ti-portrait { width: 34px; height: 34px; }
    .ti-name { font-size: 1rem; }
    .topbar-identity { gap: 9px; max-width: 52vw; }
}
@media (prefers-reduced-motion: reduce) {
    .topbar-identity { transition: opacity 0.25s ease; transform: translateY(-50%); }
    .topbar.condensed .topbar-identity { transform: translateY(-50%); }
}
.topbar nav { display: flex; gap: clamp(16px, 3vw, 30px); align-items: center; }
.topbar nav a {
    font-size: 0.82rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
    text-decoration: none;
    transition: color 0.25s ease;
}
.topbar nav a:hover { color: var(--accent-dark); }

/* ── Page frame ──────────────────────────────────────────────────────── */
.page { max-width: 1080px; margin: 0 auto; padding: 0 clamp(20px, 5vw, 48px); }
.rule { height: 1px; background: var(--border); border: 0; margin: 0; }

/* ── Masthead / hero ─────────────────────────────────────────────────── */
.masthead { padding: clamp(40px, 7vw, 64px) 0 0; text-align: center; }
.eyebrow {
    font-size: 0.74rem;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 26px;
}
.eyebrow::before, .eyebrow::after {
    content: "";
    display: inline-block;
    width: clamp(18px, 4vw, 34px);
    height: 1px;
    background: var(--accent);
    vertical-align: middle;
    margin: 0 16px 3px;
    opacity: 0.5;
}
.masthead h1 {
    margin: 0;
    font-family: var(--serif);
    font-weight: 400;
    font-size: clamp(2.4rem, 5.2vw, 4.2rem);
    line-height: 1.05;
    letter-spacing: -0.01em;
    color: var(--ink);
}
.lifetime {
    margin: 18px 0 0;
    font-family: var(--serif);
    font-style: italic;
    font-size: clamp(1.05rem, 1.8vw, 1.4rem);
    color: var(--muted);
    letter-spacing: 0.01em;
}
/* Joint memorial: the second person's lifetime sits tight under the first. */
.lifetime-2 { margin-top: 4px; }
.lifetime[hidden], .lifetime-2[hidden] { display: none; }

/* Hero band + overlapping circular portrait. The band carries the owner's
   cover IMAGE/VIDEO; when neither loads, the sage gradient + portrait monogram
   are the graceful fallback. */
.portrait-frame-wrap { position: relative; margin: clamp(38px, 6vw, 46px) 0 0; }
.hero {
    position: relative;
    height: clamp(260px, 42vw, 420px);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--hero-grad);   /* fallback when no cover is chosen */
}
.hero-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
}
/* Both the <img> and the <video> background share .hero-bg; JS toggles between
   them with the [hidden] attribute. .hero-bg sets display:block, which would
   override the UA [hidden] rule, so restore it explicitly. */
.hero-bg[hidden] { display: none; }
/* Gentle bottom scrim so the overlapping portrait reads against any photo.
   Two layers: (1) a focused sage "breath" pooled at the bottom-centre — a soft
   halo of guaranteed contrast behind the portrait so the PERSON always wins over
   even the loudest cover image/video (owners choose backgrounds with love, not
   design judgement); (2) the original wide bottom gradient. The breath is
   anchored at 50% 100% and clipped by .hero's overflow, so it reads as a pool
   under the circle at every width (the portrait always overlaps bottom-centre). */
.hero-scrim {
    position: absolute;
    inset: 0;
    background:
        radial-gradient(ellipse 34% 64% at 50% 100%, rgba(26, 51, 37, 0.52) 0%, rgba(26, 51, 37, 0.26) 42%, rgba(26, 51, 37, 0) 76%),
        linear-gradient(180deg, rgba(26, 51, 37, 0) 40%, rgba(26, 51, 37, 0.34) 100%);
}
.portrait-frame {
    position: absolute;
    left: 50%;
    bottom: clamp(-58px, -9vw, -66px);
    transform: translateX(-50%);
    z-index: 2;
    /* Larger circle so the PERSON — not the background — is the hero's subject.
       Desktop max 172 → 216 (mobile stays at the 128 min per the review). */
    width: clamp(128px, 22vw, 216px);
    height: clamp(128px, 22vw, 216px);
    border-radius: 50%;
    padding: 6px;
    background: var(--bg);             /* white-ish ring matched to the page bg */
    box-shadow: 0 14px 38px rgba(26, 51, 37, 0.28);
    display: flex;
    align-items: center;
    justify-content: center;
}
.portrait-frame img {
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;                 /* fill the circle */
    display: block;
    background: var(--hero-grad);
}
/* Monogram fallback sits under the <img>; if the cover never loads, JS removes
   the img's src (so it's transparent/empty) and the sage circle + mark show. */
.portrait-mono {
    position: absolute;
    inset: 6px;
    border-radius: 50%;
    background: var(--hero-grad);
    color: rgba(255, 255, 255, 0.85);
    font-family: 'Great Vibes', cursive;
    font-size: 2.6rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── "At a glance" activity strip ──────────────────────────────────────
   Spacer above accounts for the portrait that overlaps the hero band. */
.stat-strip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px 34px;
    margin-top: clamp(78px, 12vw, 92px);
    padding: 18px 22px;
    background: var(--accent-bg);
    border-radius: 999px;
    border: 1px solid rgba(74, 124, 89, 0.14);
}
.stat-strip[hidden] { display: none; }
/* When the strip is hidden, the portrait still needs clearance above the lede. */
.stat-strip[hidden] + .lede { margin-top: clamp(78px, 12vw, 92px); }
.stat { display: inline-flex; align-items: baseline; gap: 8px; }
.stat[hidden] { display: none; }
.stat-icon { align-self: center; line-height: 1; }
.stat-num {
    font-family: var(--serif);
    font-size: 1.4rem;
    font-weight: 400;
    color: var(--accent-dark);
    line-height: 1;
}
.stat-label { font-size: 0.85rem; color: var(--muted); letter-spacing: 0.01em; }

/* ── Lede / their story ──────────────────────────────────────────────── */
.lede { padding: clamp(40px, 7vw, 64px) 0 8px; }
.lede .card {
    max-width: 680px;
    margin: 0 auto;
    background: none;
    border: none;
    padding: 0;
    text-align: center;
}
.lede .card[hidden] { display: none; }
.lede-body {
    font-family: var(--serif);
    font-size: clamp(1.28rem, 2.1vw, 1.5rem);
    line-height: 1.62;
    color: var(--text);
    font-style: italic;        /* the family's tribute, set in a quiet italic serif */
    white-space: pre-line;     /* preserves the family's line breaks */
}
/* (Drop-cap removed by request — the story renders as a quiet italic serif.) */

/* ── Long story (rec #1) — a short tribute reads beautifully as the centered
   italic quote above; a long obituary does NOT (centered ragged italic tires
   the eye). Past STORY_QUOTE_MAX chars, JS adds .is-long: the card becomes a
   centered COLUMN of LEFT-aligned, upright serif at a comfortable reading
   measure, under a quiet "Their story" heading. ── */
.lede-title { font-family: var(--serif); font-weight: 400; font-size: 1.5rem; color: var(--ink); margin: 0 0 16px; letter-spacing: -0.01em; }
.lede-title[hidden] { display: none; }
.lede .card.is-long { max-width: 64ch; text-align: left; }
.lede .card.is-long .lede-body {
    font-style: normal;
    font-size: clamp(1.05rem, 1.5vw, 1.18rem);
    line-height: 1.78;
}

/* ── Primary actions ─────────────────────────────────────────────────── */
.actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: clamp(28px, 4vw, 40px) 0 clamp(40px, 6vw, 56px);
}
.btn {
    font-family: var(--sans);
    font-size: 0.95rem;
    font-weight: 600;
    border-radius: 999px;
    padding: 14px 30px;
    cursor: pointer;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 9px;
    transition: transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
    border: 1px solid transparent;
}
.btn .ic { width: 18px; height: 18px; display: inline-flex; align-items: center; }
.btn .ic svg, .btn > svg.ic { width: 18px; height: 18px; }
.btn-primary {
    background: var(--accent);
    color: #fff;
    box-shadow: 0 6px 18px rgba(74, 124, 89, 0.26);
}
.btn-primary:hover { background: var(--accent-dark); transform: translateY(-1px); }
.btn-primary:disabled { opacity: 0.6; cursor: default; transform: none; }
.btn-primary svg { fill: currentColor; }
.btn-soft { background: var(--accent-bg); color: var(--accent-dark); border-color: var(--border); }
.btn-soft:hover { background: #e6efe9; border-color: var(--accent); transform: translateY(-1px); }

/* ── Section heads (Roman-numeral chapters) ──────────────────────────── */
.sec { padding: clamp(40px, 6vw, 60px) 0; }
.sec-resting { padding-bottom: clamp(20px, 3vw, 32px); }
.sec-head { display: flex; align-items: baseline; gap: 18px; margin-bottom: 34px; }
/* Chapter numerals — RE-ENABLED 2026-07-12 for the chapter-grammar redesign
   (owner-approved via the journey-staging review). This REVERSES the earlier
   "chapter numbers removed by request" decision — do not hide them again
   without a new owner decision. Values in the markup are only initial text:
   memorial.js renumberChapters() re-assigns I/II/III… across the VISIBLE
   chapters at runtime, so conditional chapters (journey, places) never leave
   numbering gaps on sparse galleries. */
.sec-num {
    display: inline-block;
    font-family: var(--serif);
    font-size: 1.02rem;
    color: var(--accent);
    letter-spacing: 0.05em;
    flex: 0 0 auto;
}
.sec-title {
    font-family: var(--serif);
    font-weight: 400;
    font-size: clamp(1.55rem, 3vw, 1.9rem);
    color: var(--ink);
    letter-spacing: -0.01em;
    flex: 0 0 auto;
    margin: 0;
}
/* The count whisper inside a chapter title ("Photos & videos · 214") —
   subordinate by design: it replaced the old "(N)" on the View-all button. */
.sec-count {
    font-size: 0.58em;
    font-weight: 400;
    color: var(--muted);
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    margin-left: 2px;
}
.sec-line { flex: 1; height: 1px; background: var(--border); }

/* ── Resting place (quiet strip) ─────────────────────────────────────── */
.resting {
    display: flex;
    align-items: center;
    gap: 22px;
    background: var(--accent-bg);
    border-radius: var(--radius-md);
    padding: 26px 32px;
    border: 1px solid rgba(74, 124, 89, 0.14);
}
.resting[hidden] { display: none; }
.resting .pin {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: var(--surface);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    color: var(--accent);
    box-shadow: 0 2px 8px rgba(74, 124, 89, 0.16);
}
.resting .where { min-width: 0; }
.resting .where small {
    display: block;
    font-size: 0.74rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: 4px;
}
.resting .where p { margin: 0; font-family: var(--serif); font-size: 1.25rem; color: var(--ink); }
.resting .where p.resting-together { margin: 8px 0 0; font-size: 0.95rem; font-style: italic; color: var(--accent); }  /* joint memorial: both rest here together */
.resting-map {
    margin-left: auto;
    font-size: 0.9rem;
    color: var(--accent-dark);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.resting-map[hidden] { display: none; }
.resting-map:hover { text-decoration: underline; }
/* #125: resting-place photo — thumbnail sized to the card, with a hover-to-enlarge popover */
.grave-photo { position: relative; flex: 0 0 auto; display: inline-block; line-height: 0; }
.grave-photo-thumb {
    width: 68px; height: 68px; object-fit: cover; border-radius: 12px;
    border: 3px solid var(--surface); box-shadow: 0 2px 10px rgba(74, 124, 89, 0.22);
    cursor: zoom-in; display: block;
}
.grave-photo-zoom {
    position: absolute; left: 50%; bottom: calc(100% + 12px);
    transform: translateX(-50%) scale(0.94); transform-origin: bottom center;
    opacity: 0; pointer-events: none; z-index: 40;
    transition: opacity 0.18s ease, transform 0.18s ease;
}
.grave-photo-zoom img {
    display: block; width: 260px; max-width: 72vw; max-height: 320px; object-fit: cover;
    border-radius: 14px; border: 4px solid #fff; box-shadow: 0 18px 44px rgba(0, 0, 0, 0.30);
}
.grave-photo:hover .grave-photo-zoom,
.grave-photo:focus-visible .grave-photo-zoom,
.grave-photo.is-open .grave-photo-zoom { opacity: 1; transform: translateX(-50%) scale(1); }
@media (prefers-reduced-motion: reduce) { .grave-photo-zoom { transition: none; } }

/* ── Chapter: photo teaser ───────────────────────────────────────────── */
.photo-teaser[hidden] { display: none; }
/* RETIRED 2026-07-12 (chapter-grammar redesign): the "View all photos & videos"
   button markup was removed — the +N tile (.t-more, last strip cell) is the one
   doorway now. Rules kept ONLY so edge-cached pre-redesign HTML (≤5-min page
   cache + old ?v=39 CSS possibly evicted) still renders styled; prune on a
   future ?v bump. */
.gallery-foot { margin-top: 22px; text-align: center; }
.teaser-viewall {
    background: none;
    border: none;
    font: inherit;
    font-size: 0.86rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted);
    cursor: pointer;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
}
.teaser-viewall:hover { color: var(--accent-dark); }
.teaser-viewall #teaser-total { letter-spacing: 0; text-transform: none; }
/* Timeline fallback link (#197) — the quiet one-liner inside the journey
   chapter (#journey-sec); memorial.js shows it only when the timeline gate
   passes but fewer than 4 band thumbnails resolve.
   NB: the explicit [hidden] rules below are LOAD-BEARING — .timeline-link and
   .journey-band set display:flex, which overrides the UA [hidden] rule (same
   convention as .hero-bg[hidden] etc. above). Without them the fallback link
   rendered (href="#") on every gallery — a latent pre-redesign bug, fixed
   2026-07-12. */
.timeline-link[hidden] { display: none; }
.journey-band[hidden] { display: none; }
.timeline-link {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    margin: 26px auto 4px; color: var(--accent-dark);
    font-family: var(--serif); font-style: italic; font-size: 1.08rem;
    text-decoration: none; transition: gap 0.2s ease, color 0.15s ease;
}
.timeline-link:hover { gap: 12px; color: var(--ink); }
.timeline-link svg { flex: 0 0 auto; }

/* ── Journey band (#197 chapter) — the whole band is ONE <a>: a thread of
   4–5 year-labelled bead photos + one italic affordance line. The chapter
   head above it owns the title, so the band carries no heading of its own.
   Calm card idiom (surface + border + radius-lg, cf. .candle-invite). ── */
.journey-band {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
    padding: clamp(22px, 3vw, 30px) clamp(20px, 3vw, 34px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
    text-decoration: none;
    color: inherit;
    transition: border-color 0.35s ease, box-shadow 0.35s ease;
    /* thread geometry (consumed by .jb-thread::before); JS sets --n per bead count */
    --disc: 64px;
    --n: 5;
}
.journey-band:hover,
.journey-band:focus-visible {
    border-color: rgba(74, 124, 89, 0.42);
    box-shadow: 0 8px 24px rgba(58, 97, 71, 0.10);
}
.journey-band:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.jb-sub {
    display: inline-flex; align-items: center; gap: 8px;
    font-family: var(--serif); font-style: italic; font-size: 1.06rem;
    color: var(--accent-dark);
    transition: gap 0.2s ease, color 0.15s ease;
}
.jb-sub svg { flex: 0 0 auto; }
.jb-thread {
    position: relative;
    width: min(100%, 620px);
    display: flex;
    align-items: flex-start;
    transition: transform 0.35s ease;
}
/* the hairline "thread": runs first disc-centre → last disc-centre BEHIND the
   opaque discs, so it shows only in the gaps → beads-on-a-string */
.jb-thread::before {
    content: "";
    position: absolute;
    top: calc(var(--disc) / 2 - 0.5px);
    left: calc(100% / (var(--n) * 2));
    right: calc(100% / (var(--n) * 2));
    height: 1px;
    background: var(--jb-thread);
    z-index: 0;
    transition: background 0.35s ease, height 0.35s ease;
}
.jb-bead {
    position: relative; z-index: 1;
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; text-align: center;
}
.jb-photo {
    width: var(--disc); height: var(--disc);
    border-radius: 50%;                /* circles — echo the circular-portrait motif */
    object-fit: cover; display: block;
    background: var(--accent-bg);      /* soft sage fallback while a thumb loads */
    border: 2px solid var(--surface);
    box-shadow: 0 2px 8px rgba(26, 51, 37, 0.16);
}
.jb-year {
    margin-top: 9px;
    font-size: 0.72rem; letter-spacing: 0.06em;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}
/* HOVER — one colour move (thread deepens/thickens; kept under reduced motion)
   + one motion move (thread lifts 2px, arrow reaches right). Restrained. */
.journey-band:hover .jb-thread::before,
.journey-band:focus-visible .jb-thread::before { background: var(--jb-thread-strong); height: 2px; }
.journey-band:hover .jb-thread,
.journey-band:focus-visible .jb-thread { transform: translateY(-2px); }
.journey-band:hover .jb-sub,
.journey-band:focus-visible .jb-sub { gap: 12px; color: var(--ink); }
@media (prefers-reduced-motion: reduce) {
    .journey-band, .jb-thread, .jb-thread::before, .jb-sub { transition: none; }
    .journey-band:hover .jb-thread,
    .journey-band:focus-visible .jb-thread { transform: none; }   /* no movement */
    .journey-band:hover .jb-sub,
    .journey-band:focus-visible .jb-sub { gap: 8px; }             /* no movement */
    /* colour shifts (thread deepen, border warm, arrow ink) are kept */
}
@media (max-width: 640px) {
    .journey-band { --disc: 54px; --n: 4; }
    .jb-thread { max-width: 340px; }
    .jb-hide-mobile { display: none; }   /* JS tags the 2nd of 5 beads → mobile shows 4 */
}
/* Horizontal drift scroller — a calm auto-scrolling row that yields to touch
   (drift logic + reduced-motion/hidden-tab handling live in memorial-v2.js).
   NB: do NOT add -webkit-overflow-scrolling:touch — modern iOS has momentum by
   default and it fights the programmatic drift (landmine, keep intact). */
.teaser-grid {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    overflow-x: auto;
    scrollbar-width: none;          /* Firefox: hide scrollbar */
    scroll-behavior: auto;
}
.teaser-grid::-webkit-scrollbar { display: none; }   /* WebKit: hide scrollbar */
/* EXPLICIT square cells — the .media-cell `padding-bottom:100%` square trick
   resolves against the flex CONTAINER width here (not the cell), which made the
   cells tall rectangles. Set width AND height + cancel the padding hack
   (landmine, keep intact). */
.teaser-grid .media-cell {
    flex: 0 0 auto;
    width: clamp(150px, 30vw, 220px);
    height: clamp(150px, 30vw, 220px);
    padding-bottom: 0;
    border-radius: var(--radius-md);
}
/* ── "+N more" tile — the strip's last cell (JS-built in buildTeaser); the ONE
   doorway into the media browser since the View-all button was retired
   (2026-07-12). Shares .media-cell sizing; declared AFTER .teaser-grid
   .media-cell so its equal-specificity overrides win the cascade tie. ── */
.teaser-grid .t-more {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    font: inherit;
    background: var(--accent-bg);
    border: 1px solid rgba(74, 124, 89, 0.16);
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease;
}
.teaser-grid .t-more:hover,
.teaser-grid .t-more:focus-visible { background: #e6efe9; border-color: rgba(74, 124, 89, 0.42); }
.teaser-grid .t-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.t-more-num {
    font-family: var(--serif);
    font-size: clamp(1.35rem, 2vw, 1.6rem);
    line-height: 1.15;
    color: var(--accent-dark);
    font-variant-numeric: tabular-nums;
}
.t-more-lbl {
    font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--accent-dark); opacity: 0.75;
}

/* ── A candle moment (centered) ──────────────────────────────────────── */
.candle-invite {
    text-align: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: clamp(40px, 6vw, 54px) clamp(24px, 4vw, 40px);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
}
.candle-invite .flame {
    width: 64px;
    height: 64px;
    margin: 0 auto 22px;
    border-radius: 50%;
    background: var(--accent-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #e8843a;
}
.candle-invite .flame svg { width: 30px; height: 30px; }
.candle-invite .flame .mk-flame { fill: currentColor; }
.candle-invite h3 { margin: 0 0 10px; font-size: clamp(1.45rem, 3vw, 1.7rem); color: var(--ink); }
.candle-invite p { color: var(--muted); font-size: 1rem; max-width: 440px; margin: 0 auto 26px; }

/* ── Chapter II: tributes invite ─────────────────────────────────────── */
.tributes-invite { text-align: center; max-width: 600px; margin: 0 auto; }
.tributes-invite-lead {
    font-family: var(--serif);
    font-size: clamp(1.18rem, 2.2vw, 1.4rem);
    line-height: 1.6;
    color: #2c2a27;
    margin: 0 0 26px;
}

/* ── Closing ─────────────────────────────────────────────────────────── */
.closing { text-align: center; padding: clamp(56px, 8vw, 80px) 0 30px; }
.closing .wordmark { font-size: 2.5rem; display: inline-block; text-decoration: none; }
.closing .quote {
    font-family: var(--serif);
    font-style: italic;
    font-size: 1.2rem;
    color: var(--muted);
    max-width: 480px;
    margin: 26px auto 0;
    line-height: 1.6;
}

/* ════════════════════════════════════════════════════════════════════════
   The following blocks (modals, tributes wall, form, dropzone, media browser,
   lightbox, icons, candle animations, reveal, footer, dark mode) carry over
   the live memorial.css styling so the JS-driven modals look + behave the same.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Tributes wall (inside the tributes modal) ───────────────────────── */
.tributes-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.tributes-wall-title {
    margin: 22px 0 0;
    border-top: 1px solid var(--border);
    padding-top: 18px;
    font-family: var(--serif);
    font-weight: 400;
    font-size: 1.05rem;
    color: var(--text);
}
.tributes-wall-title[hidden] { display: none; }
.tributes-wall-title .tributes-count { color: var(--muted); font-size: 0.9rem; }
.tributes-wall-title:not([hidden]) + .tributes-list { margin-top: 12px; border-top: none; padding-top: 0; }
.tributes-list:not(:empty) { margin-top: 22px; border-top: 1px solid var(--border); padding-top: 18px; }

/* ── Inline tributes (Chapter II, editorial layout) ──────────────────────
   The wall + leave-a-tribute form render inline now (no modal), so constrain
   them to the calm reading column and card the form as a gentle panel. */
#tributes .tributes-invite-lead { max-width: 680px; margin-left: auto; margin-right: auto; text-align: center; }
#tributes #tribute-form {
    max-width: 680px;
    margin: 0 auto 6px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: clamp(22px, 4vw, 32px);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
}
#tributes .tributes-empty,
#tributes .tributes-wall-title,
#tributes .tributes-list { max-width: 680px; margin-left: auto; margin-right: auto; }

/* ── Tributes wall — masonry for 3+ cards (toggled from JS); ≤2 stay single ──
   CSS multi-column flows the varying-height cards into 1/2/3 columns by width
   (Pinterest-style). Widens past the 680px reading column so it can hold 2-3. */
#tributes .tributes-list--wall {
    max-width: 1040px;
    display: block;              /* columns need a block container (overrides the flex) */
    columns: 320px;
    column-gap: 18px;
}
.tributes-list--wall > li {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    margin: 0 0 18px;            /* replaces the flex gap in wall mode */
    width: 100%;
}
/* Semantic card tints — a gentle colour language in both layouts: photo cards
   stay white (image-led), word-only cards get a calm light sage (--accent-bg),
   and "lit a candle" cards get a warm candlelight cream that echoes the flame
   (#e8843a). All low-saturation so the wall stays calm. */
.tributes-list > li.trib-text-only {
    background: var(--accent-bg);
    border-color: #d8e6dc;
}
.tributes-list > li.trib-candle {
    background: #fdf5dd;
    border-color: #eee2bd;
}
.tributes-list li {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 18px 20px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    animation: trib-in 0.5s ease both;
}
.tributes-list li:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08); }
@keyframes trib-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}
.tributes-list .trib-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 9px; }
.tributes-list .trib-namecol { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.tributes-list .trib-namecol strong { color: var(--text); font-weight: 600; font-size: 0.98rem; }
.tributes-list .trib-when { font-size: 0.78rem; color: var(--muted); }
.tributes-list .trib-avatar {
    flex: 0 0 auto;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 1rem;
    font-weight: 600;
    line-height: 46px;
    text-align: center;
    text-transform: uppercase;
    user-select: none;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}
/* Flowing-serif tribute body — the editorial voice of the wall. */
.tributes-list .trib-body {
    margin: 0;
    font-family: var(--serif);
    font-size: 1.16rem;
    line-height: 1.6;
    color: #2c2a27;
}
.tributes-list p { margin: 0; }
.tributes-list .trib-photo { margin-top: 12px; }
.tributes-list .trib-photo img {
    display: block;
    max-width: 100%;
    max-height: 320px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
}
/* A "lit a candle" entry on the tributes wall — compact, warm flame. */
.tributes-list li.trib-candle { display: flex; align-items: baseline; gap: 10px; }
.tributes-list .trib-candle-flame { color: #e8843a; flex: 0 0 auto; line-height: 1; }
.tributes-list .trib-candle-flame .mk-flame { width: 1.7em; height: 1.7em; vertical-align: -0.42em; }
.tributes-list .trib-candle-text { color: var(--text); font-size: 0.98rem; }
.tributes-list .trib-candle-text strong { font-weight: 600; }
.tributes-list li.trib-candle .trib-when { margin-left: auto; white-space: nowrap; }
.tributes-empty {
    color: var(--muted);
    font-style: italic;
    margin: 22px 0 0;
    border-top: 1px solid var(--border);
    padding-top: 18px;
}
.tributes-empty[hidden] { display: none; }

/* ── Tribute form ────────────────────────────────────────────────────── */
#tribute-form { margin-top: 4px; }
.tribute-form-sub { margin: 0 0 6px; font-size: 0.9rem; }
.trib-field-label { display: block; font-size: 0.88rem; color: var(--muted); margin: 14px 0 6px; }
.trib-counter { float: right; font-size: 0.8rem; transition: color 0.2s ease; }
.trib-counter.is-near { color: #b5703a; }
.trib-counter.is-full { color: #c0392b; }
#tribute-form label { display: block; font-size: 0.88rem; color: var(--muted); margin: 12px 0 4px; }
#tribute-form input[type="text"],
#tribute-form textarea {
    width: 100%;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font: inherit;
    background: #fff;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
#tribute-form input:focus, #tribute-form textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(74, 124, 89, 0.12);
}
#tribute-form textarea { resize: vertical; min-height: 104px; line-height: 1.6; }

/* ── "Not sure what to write?" tribute prompts (issue #123) ── */
.trib-prompts { margin: 8px 0 2px; }
.trib-prompts-toggle {
    display: inline-flex; align-items: center; gap: 7px;
    background: none; border: 0; padding: 3px 0; cursor: pointer;
    font: inherit; font-size: 0.82rem; color: var(--accent);
    transition: color 0.15s ease;
}
.trib-prompts-toggle:hover { color: var(--accent-dark); }
.trib-prompts-toggle svg { width: 15px; height: 15px; fill: currentColor; opacity: 0.9; flex: none; }
.trib-prompts-toggle span { border-bottom: 1px dashed currentColor; padding-bottom: 1px; }
.trib-prompts-toggle.is-open span { border-bottom-color: transparent; }
.trib-prompts-panel { margin-top: 11px; }
.trib-prompts-lead { font-size: 0.78rem; color: var(--muted); margin: 0 0 9px; }
.trib-prompts-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.trib-prompt {
    font: inherit; font-size: 0.8rem; line-height: 1.3; text-align: left;
    color: var(--ink); background: var(--accent-bg);
    border: 1px solid var(--border); border-radius: 999px;
    padding: 7px 14px; cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.trib-prompt:hover, .trib-prompt.is-active {
    background: var(--accent); color: #fff; border-color: var(--accent);
}
.trib-prompts-more {
    display: inline-flex; align-items: center; gap: 6px; margin-top: 12px;
    background: none; border: 0; padding: 3px 0; cursor: pointer;
    font: inherit; font-size: 0.78rem; color: var(--muted);
    transition: color 0.15s ease;
}
.trib-prompts-more:hover { color: var(--accent); }
.trib-prompts-more svg { width: 13px; height: 13px; fill: currentColor; }
/* AI assist button (issue #123 follow-on) */
.trib-ai { margin: 10px 0 2px; }
.trib-ai-btn {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 8px 15px; border-radius: 999px; cursor: pointer;
    font: inherit; font-size: 0.82rem; font-weight: 600;
    color: #fff; background: var(--accent); border: 1px solid var(--accent);
    transition: background 0.15s ease, opacity 0.15s ease;
}
.trib-ai-btn:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
.trib-ai-btn:disabled, .trib-ai-btn.is-busy { opacity: 0.65; cursor: default; }
.trib-ai-btn svg { width: 15px; height: 15px; fill: currentColor; }
.trib-ai-msg { margin: 8px 0 0; font-size: 0.8rem; color: var(--muted); }
.trib-ai-actions { margin: 9px 0 0; display: flex; align-items: center; gap: 9px; }
.trib-ai-link { background: none; border: 0; padding: 0; cursor: pointer; font: inherit; font-size: 0.8rem; color: var(--accent); }
.trib-ai-link:hover { color: var(--accent-dark); text-decoration: underline; }
.trib-ai-sep { color: var(--border); }

#tribute-form > button[type="submit"] { margin-top: 16px; width: 100%; justify-content: center; }
.dz-input {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0);
    white-space: nowrap; border: 0;
}
.photo-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 5px;
    text-align: center;
    padding: 22px 16px;
    border: 1.5px dashed var(--border);
    border-radius: var(--radius-md);
    background: var(--bg);
    color: var(--accent-dark);
    cursor: pointer;
    transition: border-color 0.2s ease, background 0.2s ease;
}
.photo-dropzone[hidden] { display: none; }
.photo-dropzone:hover,
.photo-dropzone.is-drag { border-color: var(--accent); background: var(--accent-bg); }
.photo-dropzone.is-drag { border-style: solid; }
.photo-dropzone .dz-icon { width: 26px; height: 26px; fill: var(--accent); opacity: 0.85; }
.photo-dropzone .dz-text { font-weight: 600; font-size: 0.95rem; }
.photo-dropzone .dz-hint { font-size: 0.78rem; color: var(--muted); }
.dz-input:focus-visible + .photo-dropzone { outline: 2px solid var(--accent); outline-offset: 2px; }
#trib-photo-field[hidden] { display: none; }
.trib-photo-preview { position: relative; margin-top: 12px; display: inline-block; }
.trib-photo-preview[hidden] { display: none; }
.trib-photo-preview img {
    display: block;
    max-width: 100%;
    max-height: 220px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
}
.trib-photo-clear {
    position: absolute;
    top: -8px;
    right: -8px;
    width: 28px;
    height: 28px;
    margin: 0;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--text);
    color: #fff;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
#trib-website { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; opacity: 0; }
.form-error   { color: #c0392b; font-size: 0.9rem; margin: 10px 0 0; }
.form-error[hidden], .form-success[hidden] { display: none; }
.form-success { color: var(--accent-dark); font-size: 0.95rem; margin: 10px 0 0; }

/* ── Modals (shared shell) ───────────────────────────────────────────── */
.modal {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    z-index: 50;
}
.modal[hidden] { display: none; }
.modal-card {
    position: relative;
    background: var(--surface);
    border-radius: var(--radius-lg);
    padding: 28px 26px;
    max-width: 380px;
    width: 100%;
    text-align: center;
    box-shadow: 0 12px 48px rgba(0, 0, 0, 0.25);
}
.modal-card h3 { margin: 8px 0 14px; font-size: 1.2rem; }
.modal-close {
    position: absolute;
    top: 10px;
    right: 12px;
    background: none;
    border: none;
    font-size: 1.7rem;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
}
.modal-close:hover { color: var(--text); background: var(--accent-bg); }
.modal-card label { display: block; font-size: 0.85rem; margin-bottom: 4px; }
.modal-card input {
    width: 100%;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font: inherit;
    background: var(--bg);
    text-align: center;
}
.modal-actions { display: flex; gap: 10px; justify-content: center; margin-top: 18px; }

/* ── Tributes modal (award-pass header) ──────────────────────────────── */
.modal-card-large {
    max-width: 560px;
    max-height: 85vh;
    overflow-y: auto;
    text-align: left;
}
.modal-card-large h3 { text-align: center; }
.modal-card-large #tribute-form h4 { margin: 0 0 4px; font-size: 1.3rem; color: var(--accent-dark); }
.tributes-modal-header { text-align: center; margin: -28px -26px 14px; }
.tributes-modal-header h3 { margin: 0; }
.tm-banner {
    height: 112px;
    background-color: #2f5740;
    background-image: var(--hero-grad);
    background-size: cover;
    background-position: center;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    position: relative;
}
.tm-banner::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.22) 100%);
    border-radius: inherit;
}
.tm-portrait {
    position: relative;
    z-index: 1;
    width: 86px;
    height: 86px;
    margin: -46px auto 10px;
    border-radius: 50%;
    padding: 4px;
    background: var(--surface);
    box-shadow: 0 6px 22px rgba(0, 0, 0, 0.28);
}
.tm-portrait[hidden] { display: none; }
.tm-portrait img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    background: var(--accent-bg);
}
.modal-card-large .modal-close {
    z-index: 2;
    color: #fff;
    background: rgba(0, 0, 0, 0.30);
    width: 34px;
    height: 34px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}
.modal-card-large .modal-close:hover { color: #fff; background: rgba(0, 0, 0, 0.5); }

/* ── Inline SVG icons + candle animations ────────────────────────────── */
.mk-flame {
    width: 1.1em;
    height: 1.1em;
    fill: currentColor;
    display: inline-block;
    vertical-align: -0.18em;
    transform-origin: 50% 85%;
}
.mk-ico {
    width: 1.1em;
    height: 1.1em;
    fill: currentColor;
    display: inline-block;
    vertical-align: -0.18em;
}
.stat-icon .mk-ico, .stat-icon .mk-flame { width: 1.25em; height: 1.25em; }
.stat-icon .mk-ico { fill: var(--accent); }
@keyframes mk-flame-flicker {
    0%   { transform: scale(1)    rotate(-1.5deg) translateX(0);      opacity: 1; }
    25%  { transform: scale(1.08) rotate(1deg)    translateX(0.4px);  opacity: 0.92; }
    50%  { transform: scale(1.16) rotate(1.6deg)  translateX(-0.3px); opacity: 0.84; }
    75%  { transform: scale(1.06) rotate(-1deg)   translateX(0.3px);  opacity: 0.95; }
    100% { transform: scale(1)    rotate(-1.5deg) translateX(0);      opacity: 1; }
}
/* Button flame: sage by default; warm + flickering once this visitor lit one. */
#candle-btn-icon.is-lit .mk-flame {
    fill: #f0a020;
    filter: drop-shadow(0 0 4px rgba(240, 160, 32, 0.75));
    animation: mk-flame-flicker 1.9s ease-in-out infinite;
}
#stat-candles .mk-flame { fill: #e0911f; }
/* Modal candle: a glow halo blooms on ignite, then a gentle flicker. */
.candle-flame { position: relative; display: inline-block; }
.candle-flame::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 52%;
    width: 96px;
    height: 96px;
    transform: translate(-50%, -50%) scale(0.5);
    border-radius: 50%;
    background: radial-gradient(circle,
        rgba(245, 184, 92, 0.55) 0%, rgba(245, 184, 92, 0.20) 42%, rgba(245, 184, 92, 0) 70%);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.5s ease;
}
.candle-flame .mk-flame { position: relative; width: 2.4rem; height: 2.4rem; fill: var(--accent); }
.candle-lit .candle-flame::before { opacity: 1; animation: candle-glow-pulse 2.4s ease-in-out infinite; }
.candle-lit .candle-flame .mk-flame {
    fill: #f0a020;
    animation: candle-ignite 0.6s ease-out, mk-flame-flicker 1.6s ease-in-out 0.6s infinite;
}
@keyframes candle-glow-pulse {
    0%, 100% { transform: translate(-50%, -50%) scale(1);    opacity: 0.85; }
    50%      { transform: translate(-50%, -50%) scale(1.14); opacity: 1; }
}
@keyframes candle-ignite {
    0%   { transform: scale(0.2) translateY(7px);  opacity: 0; }
    55%  { transform: scale(1.28) translateY(-2px); opacity: 1; }
    100% { transform: scale(1)    translateY(0);    opacity: 1; }
}

/* ── Share modal ─────────────────────────────────────────────────────── */
.share-options { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.share-option {
    display: flex;
    align-items: center;
    gap: 12px;
    background: var(--accent-bg);
    color: var(--accent-dark);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 16px;
    font-weight: 600;
    font-size: 0.95rem;
    text-decoration: none;
    cursor: pointer;
    transition: border-color 0.2s ease, background 0.2s ease;
}
.share-option:hover { border-color: var(--accent); background: #e3ede7; }
.share-icon { font-size: 1.2rem; line-height: 1; }

/* ── Media browser ───────────────────────────────────────────────────── */
.modal-card-media {
    max-width: 920px;
    height: 88vh;
    display: flex;
    flex-direction: column;
    text-align: left;
}
.modal-card-media h3 { text-align: center; }
.media-tabs { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-bottom: 16px; }
.media-tab {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 6px 16px;
    font: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.media-tab span { font-size: 0.78rem; opacity: 0.7; margin-left: 2px; }
.media-tab:hover { border-color: var(--accent); }
.media-tab.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
.media-grid {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 10px;
    overflow-y: auto;
    padding: 2px;
    align-content: start;
    -webkit-overflow-scrolling: touch;
}
/* Bulletproof square: height:0 + padding-bottom:100% (landmine, keep intact). */
.media-cell {
    position: relative;
    width: 100%;
    height: 0;
    padding: 0;
    padding-bottom: 100%;
    display: block;
    border: none;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--accent-bg);
    cursor: pointer;
}
.media-cell img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.25s ease;
}
.media-cell:hover img { transform: scale(1.06); }
.media-play {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 1.5rem;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
    background: linear-gradient(0deg, rgba(0,0,0,0.28), rgba(0,0,0,0.05));
    pointer-events: none;
}
.media-note {
    position: absolute;
    right: 6px;
    bottom: 6px;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.5);
    color: #fff;
    pointer-events: none;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
.media-note svg { width: 13px; height: 13px; display: block; }
.media-empty { color: var(--muted); font-style: italic; text-align: center; padding: 28px 0; }
.media-empty[hidden] { display: none; }
@media (min-width: 600px) {
    .media-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}

/* ── Lightbox ────────────────────────────────────────────────────────── */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: rgba(0, 0, 0, 0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}
.lightbox[hidden] { display: none; }
.lightbox-stage {
    max-width: 100%;
    max-height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
}
.lightbox-stage img,
.lightbox-stage video {
    max-width: min(96vw, 1400px);
    max-height: 88vh;
    border-radius: var(--radius-sm);
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5);
}
.lightbox-stage.has-caption img,
.lightbox-stage.has-caption video { max-height: 80vh; }
.lb-caption {
    max-width: min(92vw, 880px);
    color: #f0eee9;
    font-size: 0.95rem;
    line-height: 1.5;
    text-align: center;
    text-shadow: 0 1px 8px rgba(0, 0, 0, 0.7);
    padding: 0 12px;
}
.lightbox-close,
.lightbox-nav {
    position: absolute;
    background: rgba(0, 0, 0, 0.4);
    border: none;
    color: #fff;
    cursor: pointer;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.lightbox-close { top: 16px; right: 16px; width: 44px; height: 44px; font-size: 1.8rem; line-height: 1; }
.lightbox-nav { top: 50%; transform: translateY(-50%); width: 52px; height: 52px; font-size: 2.2rem; line-height: 1; }
.lightbox-nav[hidden] { display: none; }
.lightbox-prev { left: 16px; }
.lightbox-next { right: 16px; }
.lightbox-close:hover,
.lightbox-nav:hover { background: rgba(0, 0, 0, 0.7); }
@media (max-width: 560px) {
    .lightbox-nav { width: 44px; height: 44px; font-size: 1.8rem; }
    .lightbox-prev { left: 8px; }
    .lightbox-next { right: 8px; }
}

/* ── Footer + logo badge signature underline ─────────────────────────── */
.site-footer { text-align: center; padding: 34px 20px 60px; color: var(--muted); }
.site-footer .logo-badge { font-size: 1.3rem; color: var(--muted); }
.site-footer p { margin: 10px 0 0; font-size: 0.84rem; }
.logo-badge {
    font-family: 'Great Vibes', cursive;
    color: #3a6147;
    letter-spacing: 0.005em;
    text-decoration: none;
    display: inline-block;
    position: relative;
    transition: color 0.2s ease;
}
.logo-badge::after {
    content: ""; position: absolute; left: 50%; bottom: 1px;
    width: 0; height: 1.5px; transform: translateX(-50%);
    background: linear-gradient(90deg, transparent, #4a7c59 22%, #4a7c59 78%, transparent);
    transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.logo-badge:focus-visible::after,
.logo-badge:active::after { width: 104%; }
@media (hover: hover) { .logo-badge:hover::after { width: 104%; } }
.topbar .wordmark, .closing .wordmark, .brand, .logo-badge { -webkit-tap-highlight-color: transparent; }
@media (prefers-reduced-motion: reduce) { .logo-badge::after { transition: none; } }

/* ── Gentle scroll-in reveal (JS-driven progressive enhancement) ─────── */
.reveal {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.6s ease, transform 0.6s ease;
    will-change: opacity, transform;
}
.reveal.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
    .reveal { opacity: 1 !important; transform: none !important; }
}

/* ── Mobile ──────────────────────────────────────────────────────────── */
@media (max-width: 680px) {
    body { font-size: 16px; }
    .topbar { padding: 14px 20px; }
    .topbar .wordmark { font-size: 1.6rem; }
    .topbar nav a { font-size: 0.7rem; letter-spacing: 0.1em; }
    .topbar nav .nav-hide { display: none; }
    .actions .btn-primary { width: 100%; justify-content: center; }
    .sec-head { gap: 12px; flex-wrap: wrap; }
    .resting { flex-direction: column; align-items: flex-start; gap: 16px; padding: 22px 22px; }
    .resting-map { margin-left: 0; }
    .lede-body { font-size: 1.22rem; }
    .tributes-list .trib-body { font-size: 1.06rem; }
    .closing .wordmark { font-size: 2.1rem; }
}

/* ── Dark mode (automatic, follows the OS setting) ───────────────────── */
@media (prefers-color-scheme: dark) {
    :root {
        --bg: #14110f;
        --surface: #1f1b18;
        --text: #ece7e1;
        --muted: #a79d93;
        --border: rgba(255, 255, 255, 0.12);
        --accent: #4a7c59;
        --accent-dark: #3a6147;
        --accent-bg: #242b26;
        --ink: #cfe6d7;
        --jb-thread: rgba(159, 212, 177, 0.28);
        --jb-thread-strong: rgba(159, 212, 177, 0.60);
    }
    .topbar { background: rgba(20, 17, 15, 0.86); }
    .masthead h1 { color: var(--ink); }
    .sec-title, .resting .where p { color: var(--ink); }
    .candle-invite h3 { color: var(--ink); }
    .lede-body { color: var(--text); }
    .tributes-invite-lead, .tributes-list .trib-body { color: var(--text); }
    .sec-num, .eyebrow, .resting .where small, .resting .where p.resting-together { color: #9fd4b1; }
    /* Accent-as-text/icon → lighter sage so it reads on dark surfaces. */
    .stat-num,
    .btn-soft,
    .teaser-viewall:hover,
    .resting-map,
    .form-success,
    .modal-card-large #tribute-form h4 { color: #9fd4b1; }
    .stat-icon .mk-ico,
    .photo-dropzone .dz-icon { fill: #9fd4b1; }
    /* Journey chapter + strip tile — accent-as-text reads too dark on dark
       surfaces, so lift to the lighter sages (same convention as above). */
    .timeline-link, .jb-sub { color: #7fb894; }
    .timeline-link:hover,
    .journey-band:hover .jb-sub,
    .journey-band:focus-visible .jb-sub { color: var(--ink); }
    .teaser-grid .t-more { border-color: rgba(159, 212, 177, 0.18); }
    .teaser-grid .t-more:hover,
    .teaser-grid .t-more:focus-visible { background: #2c352e; }
    .t-more-num, .t-more-lbl { color: #9fd4b1; }
    .places-lead { color: var(--text); }
    /* Hardcoded light hovers → dark equivalents. */
    .btn-soft:hover,
    .share-option:hover { background: #2c352e; }
    /* Inputs, textarea, dropzone, fields. */
    #tribute-form input[type="text"],
    #tribute-form textarea,
    .modal-card input,
    .photo-dropzone { background: #15120f; }
    /* Brighten destructive / near-limit colours for dark contrast. */
    .form-error,
    .trib-counter.is-full { color: #e8796b; }
    .trib-counter.is-near { color: #d6a05c; }
    /* Footer wordmark off the near-black background. */
    .logo-badge { color: #8ecba4; }
    .logo-badge::after { background: linear-gradient(90deg, transparent, #8ecba4 22%, #8ecba4 78%, transparent); }
}


/* ── Places (lived & loved) — grouped index beneath the map, issue #109 ──────
   2026-07-12: the old centered .places-head/.places-title heading idiom was
   RETIRED — the chapter now uses the standard .sec-head grammar (numeral +
   .sec-title + hairline) with one soft serif lead line beneath it. */
.places-lead {
    font-family: var(--serif);
    font-size: clamp(1.18rem, 2.2vw, 1.4rem);
    line-height: 1.6;
    color: #2c2a27;
    text-align: center;
    max-width: 680px;
    margin: 0 auto;
}
.places-list { max-width: 540px; margin: 22px auto 0; }
.place { display: flex; align-items: center; gap: 10px; padding: 9px 10px; margin: 0 -10px; border-radius: 9px; transition: background 0.15s ease; }
.place-body { flex: 1 1 auto; min-width: 0; }   /* text column; the map icon (rec #4) sits to its right */
.place-type { display: inline-block; margin-bottom: 4px; font-size: 0.62rem; letter-spacing: 0.07em; text-transform: uppercase; color: var(--accent-dark); background: var(--accent-bg); padding: 2px 9px; border-radius: 10px; }
.place-type-fav { color: #8a5a1a; background: rgba(182, 132, 58, 0.14); }
.places-showall-ctrl button { display: flex; align-items: center; justify-content: center; color: var(--accent-dark); }
.place-tappable { cursor: pointer; }
.place-tappable:hover, .place-tappable:focus-visible { background: var(--accent-bg); outline: none; }
.place-name { display: block; font-family: var(--serif); font-size: 1.05rem; color: var(--ink); }
.place-note { font-family: var(--serif); font-style: italic; font-size: 0.88rem; color: var(--muted); margin: 3px 0 0; line-height: 1.5; }
/* rec #4 — the repeated "View on map ↗" text link is now one small pin icon at
   the row's right edge (the whole row already flies the in-page map on tap). */
.place-map { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; color: var(--muted); text-decoration: none; transition: background 0.15s, color 0.15s; }
.place-map svg { width: 17px; height: 17px; }
.place-map:hover, .place-map:focus-visible { background: var(--accent-bg); color: var(--accent-dark); text-decoration: none; outline: none; }
.place-collapsed { display: none; }
.places-showall { display: block; margin: 14px auto 0; padding: 0; background: none; border: 0; cursor: pointer; font-family: inherit; font-size: 0.8rem; color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.places-showall:hover { color: var(--accent-dark); }

/* ── Places map (#109 Phase 2) — OpenFreeMap (MapLibre GL), soft pins ──────── */
.places-map-wrap { max-width: 540px; margin: 26px auto 0; }
.places-map {
    height: 320px; border-radius: 14px; overflow: hidden;
    border: 1px solid rgba(26, 51, 37, 0.10);
    box-shadow: 0 1px 4px rgba(26, 51, 37, 0.06);
    background: var(--accent-bg);
}
/* A touch of warmth over the calm Positron basemap. */
.places-map .maplibregl-canvas { filter: sepia(0.08) saturate(0.96); }
.places-map .maplibregl-map { font: inherit; }
.places-map-hint { text-align: center; font-size: 0.72rem; color: var(--muted); margin: 7px 0 0; }
/* Soft dot pin matching the timeline (sage; amber for favourites). */
.place-pin-dot {
    display: block; width: 15px; height: 15px; border-radius: 50%;
    background: var(--accent); border: 2.5px solid #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35); cursor: pointer;
}
.place-pin-dot.place-pin-fav-dot { background: #b6843a; }
/* Resting place — a distinct, reverent slate teardrop (set apart from the dots). */
.place-pin-rest { display: block; cursor: pointer; filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.35)); }
/* Popup — light, on-brand. */
.places-map .maplibregl-popup-content { border-radius: 11px; box-shadow: 0 6px 20px rgba(26, 51, 37, 0.16); padding: 0.6rem 0.85rem; font: inherit; }
.place-pop-type { display: block; font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-dark); margin-bottom: 2px; }
.place-pop strong { font-family: var(--serif); font-weight: 400; font-size: 1.05rem; color: var(--ink); }
.places-map .maplibregl-ctrl-attrib { font-size: 10px; background: rgba(255, 255, 255, 0.72); }
/* Dark mode uses OpenFreeMap's "dark" style (picked in JS); soften the filter + ring. */
@media (prefers-color-scheme: dark) {
    .places-map { border-color: rgba(207, 230, 215, 0.12); }
    .places-map .maplibregl-canvas { filter: saturate(0.9); }
    .place-pin-dot { border-color: #14110f; }
    .places-map .maplibregl-ctrl-attrib { background: rgba(20, 17, 15, 0.6); }
}