/* ===================================================================
   THE DIDOLKAR FAMILY LEGACY — styles.css
   Every appearance and every animation on the site. app.js decides when
   things happen; this file decides what they look like while they do.

   HOW THIS FILE IS ORDERED
     :root            the palette, the type scale and the easings, in
                      OKLCH with hex fallbacks on the line before
     base + layout    typography, .screen sections, .section-inner
     components       cards, tiles, buttons, forms, the timeline, the
                      gallery, the admin panel
     the two screens  the opening (.threshold) and the closing
                      (.farewell) — black and marigold, and the only
                      places the mandala glow is allowed
     later overrides  the sections at the end of the file refine what
                      came before, in the order the family asked for
                      them, each under a heading that says why

   WHY THE END OF THE FILE MATTERS
     CSS resolves ties by whichever rule comes last, so the blocks near
     the bottom deliberately override earlier ones. If a change here
     seems to do nothing, something further down is almost certainly
     winning — search for the selector before adding another rule.

   TWO RULES OF THUMB USED THROUGHOUT
     - Nothing that loops animates a layout property (width, height,
       padding, border-width, top/left). Those force the browser to
       re-lay out the page on every frame, which is what made the gold
       borders flicker. Loops animate transform, opacity, colour,
       box-shadow and clip-path instead — paint and compositor work.
     - A line held on one line by white-space: nowrap needs its type
       capped in vw, and needs its container wide enough to hold it;
       otherwise it overflows to the right and merely looks off-centre.
   =================================================================== */
/* ===== DIDOLKAR FAMILY LEGACY — Enhanced Design System ===== */
:root {
  /* ---------------------------------------------------------------
     Palette in OKLCH. Perceptually uniform, and reaches into Display-P3
     on capable screens so the coppers and golds render at full strength
     instead of being clipped to sRGB. Hex fallbacks precede each value
     for older browsers, which simply ignore the oklch() line.
     Chroma raised roughly 20% over the previous hex palette.
     --------------------------------------------------------------- */
  --ink: #1a0f05;
  --ink: oklch(0.19 0.045 52);
  --text: #3d2814;
  --text: oklch(0.32 0.062 55);
  --muted: #7a6650;
  --muted: oklch(0.55 0.048 68);
  --heading: #5c2d0a;
  --heading: oklch(0.38 0.115 48);
  --heading2: #8b4513;
  --heading2: oklch(0.48 0.135 47);
  --sacred-gold: #c4880f;
  --sacred-gold: oklch(0.71 0.165 78);
  --sacred-copper: #a04f0a;
  --sacred-copper: oklch(0.53 0.165 48);
  --sacred-teal: #1a6b6f;
  --sacred-teal: oklch(0.48 0.095 196);
  --plum: #5c2d0a;
  --plum: oklch(0.38 0.115 48);
  --copper: #b8650e;
  --copper: oklch(0.60 0.175 52);
  --gold: #d4a03a;
  --gold: oklch(0.76 0.155 82);
  --teal: #1a6b6f;
  --teal: oklch(0.48 0.095 196);
  --rose: #7a3020;
  --rose: oklch(0.43 0.135 32);
  --peacock: #005f7f;
  --peacock: oklch(0.47 0.115 226);
  --peacock-dark: #004a63;
  --peacock-dark: oklch(0.39 0.105 228);

  /* Darker tone for the unified section headings. Deeper than --heading,
     so the titles sit firmly against the warm background instead of
     glowing off it the way the old #D9820F orange did. */
  --heading-strong: #43200a;
  --heading-strong: oklch(0.33 0.105 46);

  /* The single vivid accent. Reserved for interactive elements only -
     focus rings, active states, links on hover, the sound toggle when on.
     Nothing decorative uses it, so it always means "you can act here". */
  --accent: #0090b8;
  --accent: oklch(0.66 0.155 218);
  --accent-strong: #00718f;
  --accent-strong: oklch(0.55 0.145 220);
  --accent-soft: rgba(0, 144, 184, 0.14);
  --accent-soft: oklch(0.66 0.155 218 / 0.14);

  --cream: #fffdf6;
  --cream: oklch(0.99 0.018 92);
  --paper: #fffaf0;
  --paper: oklch(0.98 0.028 88);
  --panel: rgba(255, 252, 243, 0.86);
  --panel: oklch(0.99 0.022 88 / 0.86);
  --panel2: rgba(255, 247, 228, 0.94);
  --panel2: oklch(0.97 0.045 86 / 0.94);
  --line: rgba(140, 85, 20, 0.16);
  --line: oklch(0.46 0.09 55 / 0.18);
  --shadow: 0 32px 80px rgba(30, 15, 0, 0.14);
  --soft: 0 16px 40px rgba(30, 15, 0, 0.08);
  --radius: 24px;
  --radius-sm: 14px;
  --speed: 0.5s;
  --ease: cubic-bezier(0.22, 0.85, 0.22, 1);
  /* Motion curves. --ease-spring overshoots slightly then settles, which
     is what reads as "bubbly" - things arrive with a little life rather
     than sliding to a stop. --ease-soft is for anything carrying text,
     where an overshoot would make words wobble while being read. */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.44, 1);
  --ease-soft: cubic-bezier(0.33, 1, 0.68, 1);
  --ease-out-back: cubic-bezier(0.18, 1.42, 0.4, 1);
  --font-display: 'Cormorant Garamond', Georgia, serif;
  --font-body: 'Jost', system-ui, sans-serif;
}

/* One vivid accent, used only where the visitor can act */
a:focus-visible, button:focus-visible, [tabindex]:focus-visible,
input:focus-visible, textarea:focus-visible, select:focus-visible,
summary:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}
input:focus, textarea:focus, select:focus { border-color: var(--accent) !important; }

/* ===== RESET & BASE ===== */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--text);
  /* --- Festive Marigold, restored ----------------------------------
     Warm throughout: ivory into marigold cream. Four stops so the ramp
     stays smooth down a long page, and fixed so all fourteen sections
     sit on one identical wash. */
  background:
    radial-gradient(ellipse at 20% 0%,   rgba(255, 193, 66, 0.34), transparent 42%),
    radial-gradient(ellipse at 82% 8%,   rgba(255, 214, 140, 0.28), transparent 38%),
    radial-gradient(ellipse at 50% 100%, rgba(255, 205, 110, 0.16), transparent 45%),
    linear-gradient(160deg, #fffdf7 0%, #fef6e6 28%, #fdefd2 58%, #fff4e0 100%);
  background-color: #fdf3de;
  background-attachment: fixed, fixed, fixed, fixed;
  min-height: 100vh;
  overflow-x: hidden;
  line-height: 1.7;
  font-size: 19px;
}
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -4;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(140, 85, 20, 0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(140, 85, 20, 0.03) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: linear-gradient(to bottom, black, transparent 80%);
}
a { color: var(--copper); text-decoration: none; transition: color 0.2s; }
a:hover { color: var(--heading); }
img { max-width: 100%; display: block; }
.hidden { display: none !important; }

.skip-link { position: absolute; left: -999px; top: 10px; background: #fff; padding: 10px; z-index: 99; border-radius: 8px; }
.skip-link:focus { left: 10px; }

/* ===== PROGRESS BAR ===== */
.page-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 100;
  background: transparent;
}
.page-progress span {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
  transition: width 0.08s linear;
  border-radius: 0 2px 2px 0;
}

/* ===== AMBIENT PARTICLES ===== */
.living-bg {
  display: none; /* floating background bubbles removed */
  position: fixed; inset: 0; z-index: -3; pointer-events: none; overflow: hidden;
}
.living-bg i {
  position: absolute; width: 6px; height: 6px; border-radius: 50%;
  background: rgba(212, 160, 58, 0.5);
  box-shadow: 0 0 20px rgba(212, 160, 58, 0.4);
  animation: floatDust 16s ease-in-out infinite;
}
.living-bg i:nth-child(1) { left: 6%; top: 15%; animation-delay: -1s; }
.living-bg i:nth-child(2) { left: 15%; top: 70%; animation-delay: -4s; }
.living-bg i:nth-child(3) { left: 28%; top: 35%; animation-delay: -8s; }
.living-bg i:nth-child(4) { left: 40%; top: 80%; animation-delay: -3s; }
.living-bg i:nth-child(5) { left: 52%; top: 15%; animation-delay: -6s; }
.living-bg i:nth-child(6) { left: 65%; top: 58%; animation-delay: -10s; }
.living-bg i:nth-child(7) { left: 75%; top: 25%; animation-delay: -5s; }
.living-bg i:nth-child(8) { left: 85%; top: 72%; animation-delay: -2s; }
.living-bg i:nth-child(9) { left: 92%; top: 40%; animation-delay: -7s; }
.living-bg i:nth-child(10) { left: 10%; top: 50%; background: rgba(26, 107, 111, 0.3); animation-delay: -9s; }
.living-bg i:nth-child(11) { left: 60%; top: 44%; background: rgba(184, 101, 14, 0.35); animation-delay: -12s; }
.living-bg i:nth-child(12) { left: 35%; top: 8%; background: rgba(92, 45, 10, 0.25); animation-delay: -11s; }
@keyframes floatDust {
  0%, 100% { transform: translate3d(0, 0, 0) scale(0.7); opacity: 0.15; }
  50% { transform: translate3d(20px, -60px, 0) scale(1.3); opacity: 0.6; }
}

/* Living pulse */
.legacy-pulse {
  display: none; /* pulsing bubble circle removed */
  position: fixed; right: 24px; bottom: 24px; width: 12px; height: 12px;
  border-radius: 50%; z-index: 30; background: var(--gold);
  box-shadow: 0 0 0 0 rgba(212, 160, 58, 0.5);
  animation: pulse 3s infinite;
}
@keyframes pulse {
  70% { box-shadow: 0 0 0 16px rgba(212, 160, 58, 0); }
  100% { box-shadow: 0 0 0 0 rgba(212, 160, 58, 0); }
}

/* ===== TOPBAR / NAV ===== */
.topbar {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  padding: 12px clamp(16px, 4vw, 56px);
  background: linear-gradient(120deg,
    rgba(255, 250, 240, 0.88) 0%,
    rgba(255, 250, 240, 0.88) 42%,
    rgba(212, 160, 58, 0.32) 50%,
    rgba(255, 250, 240, 0.88) 58%,
    rgba(255, 250, 240, 0.88) 100%);
  background-size: 260% 100%;
  animation: topbarShine 9s ease-in-out infinite;
  backdrop-filter: blur(20px) saturate(1.2);
  border-bottom: 1px solid var(--line);
  transition: box-shadow 0.3s;
}
.topbar.scrolled { box-shadow: 0 4px 30px rgba(30, 15, 0, 0.08); }
@keyframes topbarShine {
  0%, 100% { background-position: 160% 0; }
  50% { background-position: -60% 0; }
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.brand-text { display: grid; line-height: 1.1; }
.brand strong { font-family: var(--font-display); font-size: 25px; color: var(--heading); font-weight: 700; }
.brand em { font-size: 14px; font-style: normal; color: var(--muted); letter-spacing: 0.18em; text-transform: uppercase; font-weight: 500; }
.nav-links { display: flex; align-items: center; gap: 4px; }
.nav-links a {
  padding: 8px 14px; text-decoration: none; border-radius: 999px;
  font-size: 16px; font-weight: 700; color: var(--text);
  text-shadow: 0 0 8px rgba(212, 160, 58, 0.16);
  transition: all 0.2s var(--ease), text-shadow 0.3s var(--ease);
}
.nav-links a:hover, .nav-links a.active {
  background: rgba(212, 160, 58, 0.16); color: var(--heading);
  transform: translateY(-1px);
  text-shadow: 0 0 12px rgba(212, 160, 58, 0.65), 0 0 3px rgba(212, 160, 58, 0.5);
}
.admin-link { border: 1px solid var(--line); }
.nav-exit-btn {
  border: 1px solid var(--line); border-radius: 999px; padding: 8px 14px;
  font-size: 16px; font-weight: 700; background: rgba(255, 255, 255, 0.7);
  color: var(--rose); cursor: pointer; transition: all 0.2s;
}
.nav-exit-btn:hover { background: var(--rose); color: #fff; }
.menu-toggle {
  display: none; border: 1px solid var(--line); border-radius: 12px;
  background: #fff; padding: 10px; cursor: pointer; flex-direction: column; gap: 4px;
  width: 42px; height: 42px; align-items: center; justify-content: center;
}
.menu-toggle span {
  display: block; width: 20px; height: 2px; background: var(--heading);
  border-radius: 2px; transition: all 0.3s;
}


/* ===== TYPOGRAPHY ===== */
h1, h2, h3 {
  font-family: var(--font-display); color: var(--heading);
  line-height: 1.05; letter-spacing: -0.02em; margin: 0;
}
h1 { font-size: clamp(51px, 8vw, 113px); margin-bottom: 16px; }
h2 { font-size: clamp(39px, 5.5vw, 75px); margin-bottom: 14px; }
.heading-accent { color: var(--heading-strong); font-weight: 700; text-align: center; }
h3 { font-size: clamp(29px, 3.5vw, 51px); margin-bottom: 10px; }
h4 { font-size: 21px; margin: 0 0 10px; color: var(--ink); font-weight: 700; font-family: var(--font-body); }
.eyebrow {
  color: var(--copper); font-size: 15px; font-weight: 900;
  text-transform: uppercase; letter-spacing: 0.22em; margin: 0 0 8px;
  font-family: var(--font-body);
}
.lede, .section-heading p, .screen > p, .hero-layout p, .card p {
  color: var(--muted); font-size: 22px; line-height: 1.75;
}
.lede-italic { font-style: italic; font-family: var(--font-display); font-size: clamp(23px, 2.4vw, 27px); line-height: 1.8; }
.section-quote { font-style: italic; color: var(--text); opacity: 0.85; }
.name-accent { color: var(--heading); font-weight: 600; }

/* ===== BUTTONS ===== */
.button-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
.button-row.center { justify-content: center; }
.btn {
  border: 1px solid var(--line); border-radius: 999px;
  padding: 12px 24px; font-weight: 700; font-size: 17px;
  text-decoration: none; cursor: pointer; font-family: var(--font-body);
  display: inline-flex; align-items: center; justify-content: center;
  transition: all 0.25s var(--ease); position: relative; overflow: hidden;
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--soft); }
.btn:active { transform: translateY(0) scale(0.98); }
.btn.primary {
  background: linear-gradient(135deg, var(--heading), var(--copper));
  color: #fff; border-color: transparent;
}
.btn.primary:hover { box-shadow: 0 12px 36px rgba(92, 45, 10, 0.3); }
.btn.secondary {
  background: linear-gradient(135deg, var(--copper), var(--gold));
  color: #fff; border-color: transparent;
  text-shadow: 0 1px 2px rgba(60, 30, 0, 0.35);
}
.btn.secondary:hover { box-shadow: 0 12px 36px rgba(184, 101, 14, 0.35); }
.btn.ghost { background: var(--peacock); color: #fff; border-color: transparent; }
.btn.ghost:hover { background: var(--peacock-dark); color: #fff; box-shadow: 0 12px 36px rgba(0, 95, 127, 0.3); }
.btn.full { width: 100%; margin-top: 14px; }

/* ===== SHARED SOFT-GOLD SHINE + GLOW (every text-labelled button, site-wide) ===== */
.btn, .virtue-btn, .tab, .help-toggle, .tour-actions button, .nav-exit-btn, .filter-chip {
  position: relative; overflow: hidden;
  text-shadow: 0 1px 0 rgba(255, 240, 196, 0.7);
}
.btn::after, .virtue-btn::after, .tab::after, .help-toggle::after,
.tour-actions button::after, .nav-exit-btn::after, .filter-chip::after {
  content: ""; position: absolute; inset: -50% auto -50% -30%;
  width: 30%; background: linear-gradient(90deg, transparent, rgba(212, 160, 58, 0.5), transparent);
  transform: translateX(0) rotate(25deg);
  animation: sweep 5.4s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: transform;
  pointer-events: none;
}
/* This shine sits on every button on the site, so how it moves matters.
   It used to animate `left`, a layout property, which made the browser
   re-lay out each button on every frame - with this many buttons on
   screen that showed up as a constant shimmer. Translating instead is
   handled by the compositor: same travel, no layout work.
   The element is 30% of the button wide and starts 30% to its left, so
   crossing to 130% is 160% of the button = 533% of the shine's own
   width. Translate comes before rotate so it still travels level. */
@keyframes sweep {
  0%, 60% { transform: translateX(0) rotate(25deg); }
  100%    { transform: translateX(533%) rotate(25deg); }
}
.btn:hover, .btn.active, .virtue-btn:hover, .virtue-btn.active,
.tab:hover, .tab.active, .help-toggle:hover, .tour-actions button:hover,
.nav-exit-btn:hover, .filter-chip:hover, .filter-chip.active {
  text-shadow: 0 0 12px rgba(212, 160, 58, 0.6), 0 0 3px rgba(212, 160, 58, 0.45);
}

/* ===== SECTIONS ===== */
.screen {
  min-height: 100vh; padding: clamp(60px, 8vw, 100px) clamp(16px, 5vw, 72px);
  scroll-margin-top: var(--topbar-h, 70px);
  position: relative;
}
.section-panel { border-bottom: 1px solid rgba(140, 85, 20, 0.06); }
#guest-tribute.screen { min-height: auto; }

/* Section inner wrapper for full-page sections */
.section-inner {
  max-width: 1200px; margin: 0 auto; width: 100%;
}
.section-inner.subsection-style {
  max-width: 1200px; margin: 0 auto; width: 100%;
}

.section-heading { text-align: center; max-width: 900px; margin: 0 auto 36px; }
.animated-heading h2::after, .animated-heading h3::after {
  content: ""; display: block; width: 0; height: 3px;
  border-radius: 99px; background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
  margin: 14px auto 0; transition: width 0.8s var(--ease);
}
.in-view .animated-heading h2::after, .in-view .animated-heading h3::after,
.animated-heading.in-view h2::after, .animated-heading.in-view h3::after { width: 100px; }
.subsection { max-width: 1200px; margin: 80px auto 0; }
.subsection-style .hero-layout,
.subsection-style .timeline,
.subsection-style .virtue-controls,
.subsection-style .parent-grid,
.subsection-style .dossier-shell,
.subsection-style .filter-bar,
.subsection-style .gallery-filters,
.subsection-style .approved-grid,
.subsection-style .gallery-grid,
.subsection-style .tribute-form { max-width: 1200px; margin-left: auto; margin-right: auto; }

/* ===== SACRED SCREEN ===== */
.sacred-screen, .thanks-screen, .exit-screen {
  display: grid; place-items: center; text-align: center;
  align-content: center; position: relative; overflow: hidden;
}
.sacred-content { position: relative; z-index: 2; }
.shree-emblem-wrap {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: clamp(64px, 9vw, 104px); height: clamp(64px, 9vw, 104px); margin: 0 auto 16px;
}
.shree-emblem {
  display: block; width: 100%; height: 100%; object-fit: contain;
  animation: shreeGlow 3.2s ease-in-out infinite;
}
.shree-emblem-wrap::before, .shree-emblem-wrap::after {
  content: ""; position: absolute; width: 10px; height: 10px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 244, 210, 0.95) 0%, rgba(212, 160, 58, 0.45) 45%, transparent 72%);
  pointer-events: none; animation: shreeSparkle 2.6s ease-in-out infinite;
}
.shree-emblem-wrap::before { top: -2px; right: 4px; animation-delay: 0s; }
.shree-emblem-wrap::after { bottom: 2px; left: -4px; width: 7px; height: 7px; animation-delay: 1.3s; }
@keyframes shreeGlow {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(212, 160, 58, 0.35)) drop-shadow(0 0 10px rgba(212, 160, 58, 0.15)); }
  50% { filter: drop-shadow(0 0 16px rgba(212, 160, 58, 0.85)) drop-shadow(0 0 30px rgba(255, 220, 150, 0.45)); }
}
@keyframes shreeSparkle {
  0%, 100% { opacity: 0; transform: scale(0.35); }
  50% { opacity: 1; transform: scale(1); }
}
.split-title { position: relative; z-index: 1; }
/* Sacred/Thanks/Exit titles match the standard section heading (same face, style, size, color as Help) */
.sacred-content h1, .sacred-content h2 {
  font-size: clamp(39px, 5.5vw, 75px);
}
/* Same colourful underline as the other section headings */
.sacred-content h1::after, .sacred-content h2::after {
  content: ""; display: block; width: 0; height: 3px;
  border-radius: 99px; background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
  margin: 14px auto 0; transition: width 0.8s var(--ease);
}
.in-view .sacred-content h1::after, .in-view .sacred-content h2::after { width: 100px; }
/* Sacred/Thanks/Exit labels match section-heading labels like "Foundational Legacy" (17px via .section-heading p) */
.sacred-content .eyebrow {
  font-size: 20px;
  color: var(--muted);
}
/* Unified lede style for all section subtitle sentences (matches the hero tribute sentence) */
.section-heading p.lede-italic {
  font-size: clamp(23px, 2.4vw, 27px);
  color: var(--muted);
}
/* Keep key headings on one line on larger screens */
@media (min-width: 760px) {
  .split-title { white-space: normal; }
}
@media (min-width: 900px) {
  #academic .section-heading { max-width: 100%; }
  #academic .section-heading h3 { white-space: nowrap; }
}
/* Foundation & Help titles match "The Didolkar Legacy" (h3) size, on one line on larger screens */
#foundation .section-heading h2, #help .section-heading h2 {
  font-size: clamp(29px, 3.5vw, 51px);
}
@media (min-width: 900px) {
  #foundation .section-heading, #help .section-heading { max-width: 1000px; }
  #foundation .section-heading h2, #help .section-heading h2 { white-space: nowrap; }
}

/* ===== CONCENTRIC RINGS (opposite revolving with sparkles) ===== */
.concentric-rings {
  position: absolute; inset: 0; display: grid; place-items: center;
  pointer-events: none; z-index: 0;
}
.ring {
  position: absolute; border-radius: 50%;
  border: 1.5px solid transparent; opacity: 0.6;
}
.ring-1 {
  width: min(90vw, 800px); height: min(90vw, 800px);
  border-color: rgba(212, 160, 58, 0.25);
  animation: spinCW 28s linear infinite;
  box-shadow: 0 0 20px rgba(212, 160, 58, 0.08) inset;
}
.ring-2 {
  width: min(72vw, 640px); height: min(72vw, 640px);
  border-color: rgba(26, 107, 111, 0.3);
  animation: spinCCW 22s linear infinite;
  box-shadow: 0 0 16px rgba(26, 107, 111, 0.08) inset;
}
.ring-3 {
  width: min(54vw, 480px); height: min(54vw, 480px);
  border-color: rgba(184, 101, 14, 0.28);
  animation: spinCW 18s linear infinite;
  box-shadow: 0 0 14px rgba(184, 101, 14, 0.06) inset;
}
.ring-4 {
  width: min(36vw, 320px); height: min(36vw, 320px);
  border-color: rgba(212, 160, 58, 0.35);
  animation: spinCCW 14s linear infinite;
  box-shadow: 0 0 12px rgba(212, 160, 58, 0.1) inset;
}
.ring-5 {
  width: min(18vw, 160px); height: min(18vw, 160px);
  border-color: rgba(26, 107, 111, 0.4);
  animation: spinCW 10s linear infinite;
  box-shadow: 0 0 10px rgba(26, 107, 111, 0.12) inset;
}
/* Dashed/dotted variation for visual richness */
.ring-1 { border-style: solid; }
.ring-2 { border-style: dashed; border-width: 1px; }
.ring-3 { border-style: solid; border-width: 2px; }
.ring-4 { border-style: dotted; border-width: 1.5px; }
.ring-5 { border-style: solid; border-width: 2px; }

@keyframes spinCW { to { transform: rotate(360deg); } }
@keyframes spinCCW { to { transform: rotate(-360deg); } }

/* Sparkle field within rings */
.sparkle-field {
  display: none; /* colourful sparkle bubble dots removed */
  position: absolute; inset: 0; pointer-events: none;
}
.sparkle-field i {
  position: absolute; width: 5px; height: 5px; border-radius: 50%;
  animation: sparkleFloat 4s ease-in-out infinite;
}
.sparkle-field i:nth-child(1) { left: 15%; top: 20%; background: rgba(212, 160, 58, 0.8); animation-delay: 0s; }
.sparkle-field i:nth-child(2) { left: 80%; top: 15%; background: rgba(26, 107, 111, 0.8); animation-delay: 0.3s; }
.sparkle-field i:nth-child(3) { left: 45%; top: 10%; background: rgba(184, 101, 14, 0.7); animation-delay: 0.6s; }
.sparkle-field i:nth-child(4) { left: 25%; top: 75%; background: rgba(212, 160, 58, 0.9); animation-delay: 0.9s; }
.sparkle-field i:nth-child(5) { left: 70%; top: 80%; background: rgba(26, 107, 111, 0.7); animation-delay: 1.2s; }
.sparkle-field i:nth-child(6) { left: 55%; top: 85%; background: rgba(184, 101, 14, 0.8); animation-delay: 1.5s; }
.sparkle-field i:nth-child(7) { left: 10%; top: 50%; background: rgba(212, 160, 58, 0.6); animation-delay: 1.8s; width: 4px; height: 4px; }
.sparkle-field i:nth-child(8) { left: 90%; top: 45%; background: rgba(26, 107, 111, 0.6); animation-delay: 2.1s; width: 4px; height: 4px; }
.sparkle-field i:nth-child(9) { left: 35%; top: 30%; background: rgba(255, 215, 0, 0.7); animation-delay: 2.4s; width: 3px; height: 3px; }
.sparkle-field i:nth-child(10) { left: 60%; top: 65%; background: rgba(255, 140, 0, 0.6); animation-delay: 2.7s; width: 3px; height: 3px; }
.sparkle-field i:nth-child(11) { left: 20%; top: 40%; background: rgba(0, 206, 209, 0.5); animation-delay: 3.0s; width: 6px; height: 6px; }
.sparkle-field i:nth-child(12) { left: 75%; top: 35%; background: rgba(255, 165, 0, 0.6); animation-delay: 3.3s; width: 6px; height: 6px; }

@keyframes sparkleFloat {
  0%, 100% { opacity: 0.2; transform: scale(0.5) translateY(0); }
  25% { opacity: 1; transform: scale(1.4) translateY(-8px); box-shadow: 0 0 8px currentColor; }
  50% { opacity: 0.6; transform: scale(0.8) translateY(4px); }
  75% { opacity: 1; transform: scale(1.2) translateY(-4px); box-shadow: 0 0 12px currentColor; }
}

/* ===== CARDS ===== */
.card, .dossier-shell, .dedication-strip, .preview-state, .tribute-form {
  background: var(--panel);
  border: 1px solid rgba(255, 245, 220, 0.8);
  border-radius: var(--radius);
  box-shadow: var(--soft);
  padding: 28px;
  backdrop-filter: blur(12px);
  position: relative; overflow: hidden;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.card::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 3px;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
  opacity: 0.8;
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow); }

/* ===== FOUNDATION SECTION ===== */
.foundation-shell {
  display: grid; grid-template-columns: 220px 1fr; gap: 32px;
  align-items: start; background: var(--panel); border: 1px solid rgba(255, 245, 220, 0.8);
  border-radius: var(--radius); box-shadow: var(--soft); padding: 32px;
  position: relative; overflow: hidden;
}
.foundation-shell::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 3px;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
}
.foundation-photo {
  width: 100%; border-radius: 18px;
  box-shadow: 0 12px 30px rgba(30, 15, 0, 0.15);
}
.foundation-role { color: var(--copper); font-weight: 700; font-size: 17px; text-transform: uppercase; letter-spacing: 0.1em; }
.foundation-highlights {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-top: 20px;
}
.foundation-highlights span {
  padding: 12px 14px; border-radius: var(--radius-sm);
  background: rgba(26, 107, 111, 0.06); border: 1px solid rgba(26, 107, 111, 0.12);
}
.foundation-highlights b { display: block; color: var(--heading); font-size: 17px; }
.foundation-highlights small { color: var(--muted); font-size: 15px; }

/* ===== HERO LAYOUT ===== */
.hero-layout {
  display: grid; grid-template-columns: 1.1fr 0.9fr; gap: 40px;
  align-items: center; max-width: 1200px; margin: 0 auto;
}
.legacy-narrative { font-size: 20px; line-height: 1.8; color: var(--text); }
.portrait-card {
  min-height: 400px; border-radius: 32px;
  background: linear-gradient(150deg, #2a1508 0%, #4a2510 45%, #1a6b6f 100%);
  color: var(--cream); display: grid; place-items: center;
  padding: 32px; box-shadow: 0 34px 80px rgba(30, 15, 0, 0.25);
  position: relative; overflow: hidden;
}
.portrait-card::before {
  content: ""; position: absolute; inset: 20px;
  border: 1px solid rgba(255, 250, 240, 0.2); border-radius: 24px;
}
.portrait-card::after {
  content: ""; position: absolute; width: 200px; height: 200px;
  border-radius: 50%; border: 1px solid rgba(255, 250, 240, 0.12);
  animation: orbital 14s linear infinite;
}
@keyframes orbital { to { transform: rotate(1turn); } }

/* Full-frame mandir image — wearing the photo gallery's own frame:
   the beaded brown mat with its gold hairline, the dark inset stage,
   and the four cut-gold corner ornaments. Same values as .gv-mat,
   .gv-stage and .gv-corner so the two frames are identical. */
.portrait-card.portrait-card-fullimg {
  /* this element is the MAT */
  padding: 9px;
  min-height: 0;
  border-radius: 12px;
  display: block;
  position: relative;
  overflow: visible;
  background:
    repeating-linear-gradient(90deg, rgba(92,45,10,0.30) 0 2px, transparent 2px 8px),
    linear-gradient(160deg, #5c2d0a, #8b4513 55%, #5c2d0a);
  box-shadow:
    inset 0 0 0 1px rgba(255, 226, 160, 0.55),
    0 0 0 1px rgba(120, 70, 12, 0.55);
}
.portrait-card.portrait-card-fullimg picture {
  /* this element is the STAGE */
  display: block; position: relative; overflow: hidden;
  border-radius: 7px; background: #24120a;
  box-shadow: inset 0 0 26px rgba(0, 0, 0, 0.55);
}
.temple-img-full {
  position: static; inset: auto;
  display: block; width: 100%; height: 100%;
  aspect-ratio: 3 / 2; object-fit: cover;
  border-radius: 0;
}
/* the four corner ornaments, two off each element so no extra markup */
.portrait-card.portrait-card-fullimg::before,
.portrait-card.portrait-card-fullimg::after,
.portrait-card.portrait-card-fullimg picture::before,
.portrait-card.portrait-card-fullimg picture::after {
  content: ""; display: block; position: absolute;
  width: 26px; height: 26px; pointer-events: none; z-index: 2;
  border-color: rgba(255, 245, 210, 0.9); border-style: solid; border-width: 0;
}
.portrait-card.portrait-card-fullimg picture::before {
  top: 5px; left: 5px;  border-top-width: 3px; border-left-width: 3px;  border-top-left-radius: 14px;
}
.portrait-card.portrait-card-fullimg picture::after {
  top: 5px; right: 5px; border-top-width: 3px; border-right-width: 3px; border-top-right-radius: 14px;
}
.portrait-card.portrait-card-fullimg::before {
  bottom: 14px; left: 14px;  border-bottom-width: 3px; border-left-width: 3px;  border-bottom-left-radius: 14px;
}
.portrait-card.portrait-card-fullimg::after {
  bottom: 14px; right: 14px; border-bottom-width: 3px; border-right-width: 3px; border-bottom-right-radius: 14px;
  width: 26px; height: 26px; border-radius: 0 0 14px 0;
  animation: none;
}

.temple-img {
  width: min(340px, 80%); border-radius: 22px; opacity: 0.92;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
}

/* ===== TIMELINE ===== */
.timeline {
  position: relative; display: grid; gap: 16px; counter-reset: story;
  padding-left: 40px;
}
.timeline::before {
  content: ""; position: absolute; left: 14px; top: 0; bottom: 0;
  width: 3px; background: linear-gradient(var(--heading), var(--gold), var(--teal));
  opacity: 0.2; border-radius: 99px;
}
.timeline::after {
  content: ""; position: absolute; left: 14px; top: 0; width: 3px;
  height: var(--timeline-progress, 0%);
  background: linear-gradient(var(--heading), var(--gold), var(--teal));
  box-shadow: 0 0 14px rgba(184, 101, 14, 0.5);
  border-radius: 99px; transition: height 0.4s var(--ease);
}
.timeline-item {
  counter-increment: story; position: relative;
  padding: 20px 24px; background: var(--panel);
  border: 1px solid rgba(255, 245, 220, 0.8);
  border-radius: 20px; box-shadow: var(--soft);
  transition: all 0.3s var(--ease);
}
.tl-marker {
  position: absolute; left: -40px; top: 22px; width: 28px; height: 28px;
  border-radius: 50%; background: linear-gradient(135deg, var(--heading), var(--gold));
  display: grid; place-items: center; box-shadow: 0 0 0 5px rgba(212, 160, 58, 0.15);
}
.tl-marker::after {
  content: counter(story); color: #fff; font-size: 15px; font-weight: 900;
}
.tl-content strong { color: var(--heading); font-size: 21px; font-family: var(--font-display); }
.tl-content p { margin: 6px 0; }
.timeline-item button {
  border: 1px solid var(--line); border-radius: 999px; padding: 8px 16px;
  background: rgba(255, 255, 255, 0.7); color: var(--teal); font-weight: 700;
  cursor: pointer; font-size: 16px; transition: all 0.2s;
}
.timeline-item button:hover {
  background: var(--teal); color: #fff; border-color: transparent;
}
.timeline-item.active, .timeline-item:hover {
  transform: translateX(4px); border-color: rgba(212, 160, 58, 0.3);
  box-shadow: var(--shadow);
}
.story-copy {
  block-size: 0; overflow: hidden; opacity: 0;
  transition: block-size 0.5s var(--ease), opacity 0.4s, margin 0.4s;
  margin-top: 0; color: var(--text); font-size: 18px; line-height: 1.7;
  padding: 18px 26px !important; box-sizing: border-box !important;
  max-width: 100%; overflow-wrap: break-word; word-break: break-word;
}
.timeline-item.story-open .story-copy {
  block-size: auto; opacity: 1; margin-top: 12px;
}
.tl-reflection-strong {
  color: var(--heading);
}
.tl-reflection-strong strong {
  font-weight: 700;
}
.tl-edu-columns {
  display: flex; flex-wrap: wrap; gap: 20px; margin-top: 12px;
}
.tl-edu-col {
  flex: 1 1 200px; min-width: 180px;
}
@media (max-width: 480px) {
  .tl-edu-columns { flex-direction: column; gap: 10px; }
}

/* Enriched timeline card: two-column layout with photo, meta, quote, people */
.tl-content { display: flex; gap: 20px; align-items: flex-start; }
.tl-main { flex: 1 1 auto; min-width: 0; }
.tl-head {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: 6px 12px; margin-bottom: 8px;
}
.tl-quote {
  margin: 8px 0 10px; padding: 4px 0 4px 14px;
  border-left: 3px solid var(--gold);
  font-family: var(--font-display); font-style: italic;
  font-size: 21px; line-height: 1.4; color: var(--heading);
}
.tl-people {
  block-size: 0; overflow: hidden; opacity: 0; margin: 0;
  font-size: 17px; line-height: 1.6; color: var(--muted);
  transition: block-size 0.4s var(--ease), opacity 0.35s, margin 0.35s;
}
.tl-people strong { color: var(--text); font-weight: 700; font-family: var(--font-body); }
.timeline-item.story-open .tl-people {
  block-size: auto; opacity: 1; margin: 0 0 12px;
}
.tl-photo, .tl-photo-cluster, .tl-photo-stack, .tl-photo-grid {
  display: none;
}
.timeline-item.story-open .tl-photo,
.timeline-item.story-open .tl-photo-cluster {
  display: block;
}
.timeline-item.story-open .tl-photo-stack {
  display: flex;
}
.timeline-item.story-open .tl-photo-grid {
  display: grid;
}
.tl-photo {
  flex: 0 0 132px; width: 132px; margin: 0; text-align: center;
}
.tl-photo img {
  width: 132px; height: 132px; object-fit: cover; object-position: center;
  border-radius: 14px; border: 2px solid rgba(212, 160, 58, 0.45);
  background: linear-gradient(135deg, rgba(255,248,232,0.9), rgba(240,220,170,0.5));
  padding: 6px; box-shadow: var(--soft);
}
.tl-photo figcaption {
  margin-top: 6px; font-size: 14px; line-height: 1.35; color: var(--muted);
  font-style: italic;
}
@media (max-width: 720px) {
  .tl-content { flex-direction: column-reverse; }
  .tl-photo { flex: 0 0 auto; width: 100%; }
  .tl-photo img { width: 108px; height: 108px; }
}

/* Photo cluster: several small photos linked by a curved gold thread (Childhood tile) */
.tl-photo-cluster {
  position: relative;
  flex: 0 0 150px;
  width: 150px;
  min-height: 400px;
  margin: 0 auto;
}
.tl-photo-cluster--pair {
  min-height: 270px;
}
.tl-photo-cluster--marriage {
  /* two photographs now, not three - the column is shortened to match
     so the thread does not run on past the last frame */
  width: 240px; flex: 0 0 240px; min-height: 290px;
}
.tl-photo-cluster--career {
  min-height: 830px;
}
.tl-photo-stack {
  flex: 0 0 260px; width: 260px;
  display: flex; flex-direction: column; gap: 12px;
}
.tl-photo-fill {
  width: 260px; aspect-ratio: 260 / 185; margin: 0; flex: none;
  border-radius: 16px; overflow: hidden; position: relative;
  border: 2px solid rgba(212, 160, 58, 0.5); box-shadow: var(--soft);
}
.tl-photo-fill img {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: center;
}
.tl-photo-fill figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 6px 12px; font-size: 14px; font-style: italic; color: #fff;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.55));
}
@media (max-width: 720px) {
  .tl-photo-stack { width: 100%; }
  .tl-photo-fill { width: 100%; height: auto; aspect-ratio: 260 / 185; }
}
.tl-thread {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}
.tl-thread-line {
  fill: none;
  stroke: var(--gold);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 1 5;
  opacity: 0.85;
  filter: drop-shadow(0 0 2px rgba(212, 160, 58, 0.5));
}
.tl-thread-bead {
  fill: var(--gold);
  filter: drop-shadow(0 0 2px rgba(212, 160, 58, 0.65));
}
.tl-thread-tip {
  position: absolute; left: 50%; transform: translateX(-50%);
  color: var(--gold); font-size: 18px; line-height: 1; z-index: 2;
  text-shadow: 0 0 5px rgba(212, 160, 58, 0.55);
}
.tl-thread-tip--top { top: -16px; }
.tl-photo-item {
  position: absolute;
  left: calc(50% + var(--x));
  top: var(--t);
  margin: 0;
  z-index: 1;
  transform: translateX(-50%) rotate(var(--r));
  transition: transform 0.3s ease, z-index 0.3s ease;
}
.tl-photo-item:hover { transform: translateX(-50%) rotate(0deg) scale(1.1); z-index: 3; }
.tl-photo-item img {
  width: var(--s, 100px); height: var(--s, 100px); object-fit: cover; object-position: center;
  border-radius: 12px; border: 2px solid rgba(212, 160, 58, 0.5);
  background: linear-gradient(135deg, rgba(255,248,232,0.9), rgba(240,220,170,0.5));
  padding: 5px; box-shadow: var(--soft);
}
.tl-photo-item--round img { border-radius: 50%; padding: 4px; }
.tl-photo-item--landscape img { width: var(--w); height: var(--h); border-radius: 14px; }

/* Stacked layout: text full width on top, photo section full width below (Career, Service tile) */
.tl-content--stacked {
  flex-direction: column;
}
.tl-content--stacked .tl-main {
  flex: 1 1 auto; width: 100%;
}

/* Dense photo grid with zoom-navigable thumbnails */
.tl-photo-grid {
  grid-template-columns: repeat(5, 1fr);
  gap: 10px; width: 100%; margin-top: 16px;
}
.tl-grid-thumb {
  padding: 0; border: 2px solid rgba(212, 160, 58, 0.4); border-radius: 10px;
  overflow: hidden; cursor: zoom-in; background: none; aspect-ratio: 1;
  transition: all 0.2s var(--ease);
}
.tl-grid-thumb:hover {
  border-color: var(--gold); transform: translateY(-3px); box-shadow: var(--soft);
}
.tl-grid-thumb img {
  width: 100%; height: 100%; object-fit: cover; object-position: center; display: block;
}
@media (max-width: 900px) {
  .tl-photo-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 720px) {
  .tl-photo-grid { grid-template-columns: repeat(3, 1fr); gap: 8px; }
}
@media (max-width: 480px) {
  .tl-photo-grid { grid-template-columns: repeat(2, 1fr); }
}
.tl-cluster-caption {
  position: absolute; bottom: -22px; left: 0; right: 0;
  text-align: center; font-size: 14px; font-style: italic; color: var(--muted);
}
@media (max-width: 720px) {
  .tl-photo-cluster {
    width: 100%; min-height: auto; margin: 0 0 26px;
    justify-content: center; align-items: flex-end; gap: 14px;
  }
  .timeline-item.story-open .tl-photo-cluster { display: flex; }
  .tl-thread, .tl-thread-tip { display: none; }
  .tl-photo-item { position: static; transform: rotate(var(--r)); }
  .tl-photo-item:hover { transform: rotate(0deg) scale(1.06); }
  .tl-photo-item img { width: 84px !important; height: 84px !important; }
  .tl-cluster-caption { display: none; }
}

/* ===== VIRTUES & WISDOM ===== */
.virtue-controls {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-bottom: 24px;
}
.virtue-btn {
  border: 1px solid var(--line); border-radius: 999px; padding: 10px 20px;
  background: rgba(255, 255, 255, 0.7); color: var(--heading);
  font-weight: 700; cursor: pointer; font-size: 17px; font-family: var(--font-body);
  transition: all 0.25s var(--ease);
}
.virtue-btn:hover { background: rgba(212, 160, 58, 0.12); transform: translateY(-2px); }
.virtue-btn.active {
  background: linear-gradient(135deg, var(--heading), var(--copper));
  color: #fff; border-color: transparent;
  box-shadow: 0 8px 24px rgba(92, 45, 10, 0.2);
}
.parent-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; max-width: 1100px; margin: 0 auto; }
.parent-top { display: flex; gap: 14px; align-items: center; margin-bottom: 14px; }
.avatar-frame {
  width: 52px; height: 52px; border-radius: 16px; display: grid; place-items: center;
  background: linear-gradient(145deg, var(--heading), var(--copper));
  color: #fff; font: 700 24px var(--font-display);
  box-shadow: 0 8px 20px rgba(92, 45, 10, 0.2);
}
.virtue-panel {
  min-height: 60px; padding: 16px 0; color: var(--text); font-size: 18px; line-height: 1.7;
  transition: all 0.4s var(--ease);
}
.virtue-panel strong { display: block; color: var(--heading); margin-bottom: 8px; font-size: 19px; }
.virtue-placeholder { color: var(--muted); font-style: italic; }

/* ===== ACADEMIC TABS ===== */
.dossier-shell {
  max-width: 1100px; margin: 0 auto;
  /* No background panel — tabs and cards sit directly on the page */
  background: none; border: none; box-shadow: none;
  padding: 0; backdrop-filter: none; overflow: visible;
}
.tabs { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-bottom: 18px; }
.tab {
  border: 1px solid var(--line); background: rgba(255, 255, 255, 0.65);
  border-radius: 999px; padding: 9px 16px; font-weight: 700; font-size: 16px;
  color: var(--heading); cursor: pointer; font-family: var(--font-body);
  transition: all 0.2s var(--ease);
}
.tab:hover, .tab.active {
  background: linear-gradient(135deg, var(--heading), var(--copper));
  color: #fff; border-color: transparent; transform: translateY(-1px);
}
.tab-panel { transition: opacity 0.2s, transform 0.2s; }
.tab-panel.switching { opacity: 0; transform: translateY(8px); }
.academic-card .academic-detail { margin-top: 12px; }
.academic-card .academic-detail ul { padding-left: 18px; margin: 8px 0; }
.academic-card .academic-detail li { margin-bottom: 6px; color: var(--text); font-size: 18px; }

/* ===== PUBLICATION TABLE ===== */
.expand-pub-table-btn {
  margin-top: 12px; font-size: 16px !important; padding: 8px 16px !important;
}
.pub-table-container { margin-top: 14px; }
.pub-table-scroll { overflow-x: auto; border-radius: 12px; border: 1px solid var(--line); }
.pub-table {
  width: 100%; border-collapse: collapse; font-size: 16px; min-width: 600px;
}
.pub-table th, .pub-table td {
  padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.pub-table th {
  background: rgba(92, 45, 10, 0.06); color: var(--heading);
  font-weight: 700; font-size: 15px; text-transform: uppercase; letter-spacing: 0.05em;
  position: sticky; top: 0;
}
.pub-table tr:hover td { background: rgba(212, 160, 58, 0.05); }
.pub-table td { color: var(--text); }

/* ===== GUEST BOOK ===== */
.filter-bar, .gallery-filters {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 10px; margin: 0 auto 24px;
}
.filter-chip {
  border: 1px solid transparent; background: var(--peacock);
  border-radius: 999px; padding: 9px 16px; font-weight: 700; font-size: 16px;
  color: #fff; cursor: pointer; font-family: var(--font-body);
  transition: all 0.2s var(--ease);
}
.filter-chip:hover, .filter-chip.active {
  background: var(--peacock-dark); color: #fff;
  border-color: var(--gold);
}
.filter-bar input {
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255, 255, 255, 0.8); padding: 10px 16px;
  min-width: 200px; font-family: var(--font-body); font-size: 17px;
  outline: none; transition: border 0.2s, box-shadow 0.2s;
}
.filter-bar input:focus {
  border-color: var(--teal); box-shadow: 0 0 0 4px rgba(26, 107, 111, 0.1);
}
.approved-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
}
.approved-grid article, .tribute-placeholder-card {
  background: var(--panel); border: 1px solid rgba(255, 245, 220, 0.8);
  border-radius: 20px; padding: 22px; box-shadow: var(--soft);
  position: relative; overflow: hidden; transition: transform 0.25s var(--ease);
}
.approved-grid article:hover { transform: translateY(-3px); }
.approved-grid article::before {
  content: "\201C"; position: absolute; right: 14px; top: -8px;
  font: 700 80px var(--font-display); color: rgba(212, 160, 58, 0.12);
}

/* ===== GALLERY ===== */
.gallery-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px;
}
.gallery-card {
  position: relative; overflow: hidden; border-radius: 18px;
  border: 0; cursor: pointer; aspect-ratio: 1;
  background: linear-gradient(145deg, #2a1508, #1a6b6f);
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.gallery-card:hover { transform: translateY(-4px) scale(1.02); box-shadow: var(--shadow); }
.gallery-card img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 0.4s var(--ease), opacity 0.3s;
  opacity: 0.88;
}
.gallery-card:hover img { transform: scale(1.08); opacity: 1; }
.gallery-card span {
  position: absolute; bottom: 0; left: 0; right: 0; padding: 12px 14px;
  background: linear-gradient(transparent, rgba(0,0,0,0.7));
  color: #fff; font-weight: 700; font-size: 16px;
}

/* ===== TRIBUTE FORM ===== */
.tribute-form { max-width: 1100px; margin: 0 auto; }
.dedication-strip {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin-bottom: 20px; padding: 20px; background: var(--panel);
  border: 1px solid rgba(255, 245, 220, 0.8); border-radius: var(--radius);
}
.dedication-strip strong { color: var(--heading); font-size: 17px; }
.radio-pill {
  display: flex; gap: 8px; align-items: center; padding: 8px 14px;
  border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255, 255, 255, 0.6); font-weight: 600; font-size: 17px;
  cursor: pointer; transition: all 0.2s;
}
.radio-pill:has(input:checked) {
  background: rgba(212, 160, 58, 0.15); border-color: rgba(212, 160, 58, 0.4);
  box-shadow: 0 0 0 4px rgba(212, 160, 58, 0.06);
}
.radio-pill input { width: 16px; height: 16px; accent-color: var(--copper); }
.tribute-grid { display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 20px; }
label { display: grid; gap: 6px; font-weight: 700; color: var(--heading); font-size: 17px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
input, textarea, select {
  width: 100%; border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 12px 14px; background: rgba(255, 255, 255, 0.88); font: inherit;
  color: var(--text); outline: none; transition: border 0.2s, box-shadow 0.2s;
}
input:focus, textarea:focus, select:focus {
  border-color: var(--teal); box-shadow: 0 0 0 4px rgba(26, 107, 111, 0.08);
}
textarea { resize: vertical; }
.char-meter { height: 5px; border-radius: 99px; background: rgba(0,0,0,0.06); overflow: hidden; margin-top: 6px; }
.char-meter span { display: block; height: 100%; width: 0%; background: linear-gradient(90deg, var(--teal), var(--copper)); transition: width 0.2s; }
.upload-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
.upload-tile {
  min-height: 90px; border: 1px dashed rgba(140, 85, 20, 0.25);
  border-radius: 16px; background: rgba(255, 255, 255, 0.55);
  display: grid; place-items: center; text-align: center; cursor: pointer;
  transition: all 0.25s var(--ease); padding: 12px;
}
.upload-tile input { display: none; }
.upload-icon { font-size: 31px; }
.upload-tile em { font-style: normal; color: var(--muted); font-size: 15px; }
.upload-tile:hover, .upload-tile.selected {
  border-color: var(--teal); background: rgba(26, 107, 111, 0.06);
  transform: translateY(-2px);
}
.tracker-state { margin-bottom: 14px; }
.tracker-line { display: grid; grid-template-columns: 40px 1fr 60px; gap: 10px; align-items: center; margin: 8px 0; }
.tracker-line span { font-weight: 700; color: var(--heading); font-size: 16px; }
.tracker-line em { font-style: normal; color: var(--muted); font-weight: 600; font-size: 15px; }
progress { width: 100%; height: 10px; accent-color: var(--teal); border-radius: 99px; }
.preview-state {
  margin-top: 20px; padding: 24px;
  background: linear-gradient(135deg, rgba(255, 250, 240, 0.9), rgba(255, 245, 220, 0.8));
}
.preview-state blockquote {
  margin: 12px 0; padding: 16px; border-left: 3px solid var(--copper);
  background: rgba(255, 255, 255, 0.6); border-radius: 14px; color: var(--text);
  font-style: italic;
}
.preview-files { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 12px; }
.preview-files span {
  padding: 10px; border-radius: 12px; background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--line); text-align: center; font-size: 16px; font-weight: 600;
}
.moderation-note { font-size: 16px; color: var(--muted); margin-top: 12px; }
.form-message { font-weight: 700; color: var(--teal); min-height: 24px; margin-top: 12px; }
.form-message:empty { display: none; min-height: 0; margin-top: 0; }

/* ===== HELP SECTION ===== */
.help-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 18px; max-width: 1100px; margin: 0 auto; }
.help-head {
  display: flex; gap: 12px; align-items: center;
}
.card-icon {
  width: 38px; height: 38px; border-radius: 12px; display: grid; place-items: center;
  background: rgba(26, 107, 111, 0.1); color: var(--teal); font-weight: 900; flex-shrink: 0;
}
.help-toggle {
  margin-left: auto; border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 14px; background: rgba(255, 255, 255, 0.7);
  font-weight: 700; font-size: 15px; cursor: pointer; color: var(--heading);
  transition: all 0.2s;
}
.help-toggle:hover { background: var(--heading); color: #fff; border-color: transparent; }
.help-body {
  block-size: 0; overflow: hidden; opacity: 0;
  transition: block-size 0.5s var(--ease), opacity 0.4s, padding 0.4s;
  padding-top: 0;
}
.help-card.is-open .help-body { block-size: auto; opacity: 1; padding-top: 16px; }
.help-body ol { padding-left: 18px; }
.help-body li { margin-bottom: 6px; font-size: 17px; }
details { padding: 10px 0; border-bottom: 1px solid var(--line); }
summary { cursor: pointer; color: var(--heading); font-weight: 700; font-size: 18px; }
details p { font-size: 17px; margin-top: 6px; }
.tour-map { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.tour-map span {
  padding: 6px 10px; border-radius: 999px; background: rgba(212, 160, 58, 0.1);
  border: 1px solid rgba(212, 160, 58, 0.2); font-size: 15px; font-weight: 700;
}
.support-note { margin-top: 12px; padding: 14px; background: rgba(26, 107, 111, 0.06); border-radius: 12px; font-size: 17px; }

/* ===== ADMIN ===== */
.admin-login { max-width: 440px; margin: 0 auto; display: grid; gap: 14px; }
.admin-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; max-width: 900px; margin: 0 auto 20px; }
@media (max-width: 620px) {
  .admin-stats { grid-template-columns: repeat(2, 1fr); }
}
.admin-stats article {
  padding: 16px; border-radius: 18px; background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--line); text-align: center;
}
.admin-stats strong { display: block; font: 700 30px var(--font-display); color: var(--heading); }
.admin-filter-bar { max-width: 1100px; }
.admin-tributes { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; max-width: 1100px; margin: 0 auto; }
.admin-tribute {
  background: var(--panel); border: 1px solid rgba(255, 245, 220, 0.8);
  border-radius: 20px; padding: 20px; transition: transform 0.2s;
}
.admin-tribute:hover { transform: translateY(-2px); }
.admin-tribute .actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.admin-tribute .actions button {
  border: 1px solid var(--line); border-radius: 999px; padding: 6px 12px;
  background: #fff; cursor: pointer; font-weight: 700; font-size: 15px;
  transition: all 0.2s;
}
.admin-tribute .actions button[data-action="approved"] { color: var(--teal); }
.admin-tribute .actions button[data-action="approved"]:hover { background: var(--teal); color: #fff; }
.admin-tribute .actions button[data-action="rejected"] { color: var(--rose); }
.admin-tribute .actions button[data-action="rejected"]:hover { background: var(--rose); color: #fff; }
.admin-tribute .actions button[data-action="delete"]:hover { background: #333; color: #fff; }
/* Shown / hidden on the website - a display state, not an approval state,
   so it reads as a quiet note rather than another status word. */
.admin-vis { display: block; margin-top: 3px; font-size: 13px; font-weight: 700; }
.admin-vis.is-shown  { color: var(--teal); }
.admin-vis.is-hidden { color: var(--rose); }
.admin-tribute .actions button[data-action="hide"] { color: var(--rose); }
.admin-tribute .actions button[data-action="hide"]:hover { background: var(--rose); color: #fff; }
.admin-tribute .actions button[data-action="show"] { color: var(--teal); }
.admin-tribute .actions button[data-action="show"]:hover { background: var(--teal); color: #fff; }

/* ===== GALLERY MODAL ===== */
.modal {
  border: 0; border-radius: 24px; padding: 28px;
  box-shadow: 0 40px 120px rgba(0,0,0,0.35); background: var(--cream);
  max-width: min(700px, 92vw); color: var(--text);
}
.modal::backdrop { background: rgba(20, 10, 0, 0.6); backdrop-filter: blur(10px); }
.modal-close {
  position: absolute; top: 14px; right: 14px; border: 0; border-radius: 50%;
  width: 36px; height: 36px; background: #fff; box-shadow: var(--soft);
  font-size: 25px; cursor: pointer; display: grid; place-items: center;
  transition: all 0.2s;
}
.modal-close:hover { background: var(--heading); color: #fff; }
.gallery-modal img { width: 100%; border-radius: 16px; margin-bottom: 16px; }
.gallery-modal h3 { font-family: var(--font-display); color: var(--heading); }

.timeline-photo-modal {
  max-width: min(1200px, 94vw); padding: 16px; position: relative;
}
.timeline-photo-modal img {
  display: block; width: 100%; max-height: 85vh;
  object-fit: contain; border-radius: 16px; cursor: zoom-out;
}
.tl-photo-item img, .tl-photo-fill img, .tl-photo img, .tl-grid-thumb img {
  cursor: zoom-in;
}
.tl-photo-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%; border: 0;
  background: rgba(255, 255, 255, 0.85); color: var(--heading);
  font-size: 21px; cursor: pointer; box-shadow: var(--soft);
  display: grid; place-items: center; transition: all 0.2s;
  z-index: 2;
}
.tl-photo-nav:hover { background: var(--heading); color: #fff; }
.tl-photo-nav--prev { left: 24px; }
.tl-photo-nav--next { right: 24px; }
.tl-photo-nav[hidden] { display: none; }
.tl-photo-counter {
  text-align: center; margin: 10px 0 0; font-size: 16px; color: var(--muted);
}

/* ===== TOUR BUBBLE ===== */
.tour-bubble {
  position: fixed; right: 20px; bottom: 50px; max-width: 320px;
  background: #fff; border: 1px solid var(--line); box-shadow: var(--shadow);
  border-radius: 20px; padding: 18px; z-index: 80;
}
.tour-bubble strong { color: var(--heading); font-size: 17px; }
.tour-bubble p { font-size: 17px; color: var(--text); margin: 8px 0; }
.tour-actions { display: flex; gap: 8px; }
.tour-actions button {
  border: 0; border-radius: 999px; padding: 8px 16px; font-weight: 700;
  font-size: 16px; cursor: pointer; transition: all 0.2s;
}
#tourNext { background: var(--heading); color: #fff; }
#tourClose { background: rgba(0,0,0,0.06); color: var(--text); }

/* ===== TOAST ===== */
.toast {
  position: fixed; left: 50%; bottom: 24px;
  transform: translate(-50%, 20px); opacity: 0;
  background: var(--ink); color: #fff; border-radius: 999px;
  padding: 12px 20px; z-index: 90; box-shadow: var(--shadow);
  font-weight: 600; font-size: 17px;
  transition: opacity 0.3s, transform 0.3s;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* (exit sparkles replaced by concentric-rings above) */

/* ===== SCROLL REVEAL ===== */
.observe, .observe-card {
  opacity: 0; transform: translateY(28px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.observe.in-view, .observe-card.in-view { opacity: 1; transform: translateY(0); }

/* ===== EXIT OVERLAY ===== */
.exit-countdown-overlay {
  position: fixed; inset: 0; z-index: 999; display: grid; place-items: center;
  background: rgba(20, 10, 0, 0.85); backdrop-filter: blur(12px);
}
.exit-countdown-card {
  text-align: center; padding: 48px; background: var(--cream);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.exit-countdown-kicker { color: var(--copper); font-weight: 700; text-transform: uppercase; letter-spacing: 0.2em; font-size: 15px; }
#exitCountdownNumber { display: block; font: 700 80px var(--font-display); color: var(--heading); margin: 10px 0; }

/* ===== TOUR HIGHLIGHT ===== */
.tour-highlight { outline: 3px solid var(--gold); outline-offset: 8px; border-radius: var(--radius); }

/* ===== FILE LINKS ===== */
.file-links { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.file-links a {
  padding: 6px 10px; border-radius: 999px; background: rgba(26, 107, 111, 0.08);
  color: var(--teal); font-weight: 700; font-size: 15px;
}

/* ===== RESPONSIVE ===== */
@media (max-width: 980px) {
  .menu-toggle { display: flex; }
  .nav-links {
    position: absolute; top: 100%; left: 14px; right: 14px;
    display: none; flex-direction: column; align-items: stretch;
    padding: 14px; background: rgba(255, 250, 240, 0.97);
    border: 1px solid var(--line); border-radius: 18px; box-shadow: var(--shadow);
  }
  .nav-links.open { display: flex; }
  .hero-layout, .parent-grid, .tribute-grid, .help-grid { grid-template-columns: 1fr; }
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
  .approved-grid, .admin-tributes { grid-template-columns: repeat(2, 1fr); }
  .foundation-shell { grid-template-columns: 1fr; }
  .foundation-photo { max-width: 200px; margin: 0 auto; }
}
@media (max-width: 620px) {
  .screen { padding: 48px 14px; }
  .brand em { display: none; }
  .button-row { flex-direction: column; }
  .btn { width: 100%; }
  .gallery-grid, .approved-grid, .admin-tributes, .admin-stats,
  .upload-grid, .preview-files, .field-row { grid-template-columns: 1fr; }
  .foundation-highlights { grid-template-columns: 1fr; }
  .dedication-strip { display: grid; }
  .tour-bubble { left: 12px; right: 12px; bottom: 16px; max-width: none; }
  .portrait-card { min-height: 320px; }
}

/* ===== REDUCED MOTION ===== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important; transition: none !important;
    scroll-behavior: auto !important;
  }
  .observe, .observe-card { opacity: 1 !important; transform: none !important; }
}

/* ===== PRINT ===== */
@media print {
  body { background: #fff; }
  .topbar, .living-bg, .page-progress, .button-row, .legacy-pulse,
  .dedication-strip, .tracker-state, .tour-bubble, .toast { display: none !important; }
  .screen, .subsection { min-height: auto; padding: 20px 0; }
}

/* ===================================================================
   CONTINUOUS MANDALA FLOW - 13 mandalas, nine designs, sparkling gold
   Each mandala cycles through nine colour stops over 25-50 seconds:
   out of the page background, up through marigold and burnished gold to
   a bright sparkle at the crest, then back down and into the background
   again - so it blooms out of the page and dissolves into it.
   No halo: the sparkle comes entirely from the colour journey.
   =================================================================== */
.mandala-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.mandala-flow {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  overflow: hidden;
  contain: strict;
}

/* wrapper carries the travel AND the colour cycle */
.mandala {
  position: absolute;
  display: block;
  color: #fdf3de;                       /* the marigold base, so it starts invisible */
  will-change: transform, opacity;
  transform: translateZ(0);
  backface-visibility: hidden;
}
.mandala svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  position: relative;
  filter: drop-shadow(0 0 7px rgba(226, 182, 74, 0.5));
}

/* --- the celestial glow ---
   Two colours, not one: apricot at the core fading through to pale lilac
   at the rim - the two tones already in the Usha gradient. That is what
   makes it read as diffused dawn light rather than a gold lamp sitting
   behind each mandala. It breathes on its own rhythm, unsynchronised
   with the colour cycle, so the two never lock into a single pulse. */
.mandala-glow {
  position: absolute;
  inset: -42%;
  border-radius: 50%;
  pointer-events: none;
  /* warm gold throughout - bright at the core, fading golden at the rim */
  background: radial-gradient(circle,
    rgba(255, 214, 120, 0.50) 0%,
    rgba(232, 178, 74, 0.28) 32%,
    rgba(244, 196, 48, 0.10) 58%,
    transparent 74%);
  filter: blur(9px);
  will-change: transform, opacity;
  animation: mandalaGlow cubic-bezier(0.42, 0, 0.58, 1) infinite;
}
@keyframes mandalaGlow {
  0%, 100% { opacity: 0.24; transform: scale(0.84); }
  50%      { opacity: 0.80; transform: scale(1.20); }
}

/* --- the colour journey: nine stops --- */
/* Begins and
   ends on #fbeee7, the page's own base, so each mandala dissolves into
   the page completely before blooming again. */
@keyframes mandalaTint {
  /* Starts and ends on the page's own base, so each mandala rises out of
     the background, breathes through marigold and gold, and dissolves
     back into it - a continuous cycle with no foreign hue. */
  0%, 100% { color: #fdf3de; }   /* 1 - the page's own base */
  12.5%    { color: #f6e0ae; }   /* 2 - warming out of the page */
  25%      { color: #ffc14a; }   /* 3 - Festive Marigold */
  37.5%    { color: #d4a03a; }   /* 4 - gold */
  50%      { color: #fff3c4; }   /* 5 - the bright crest */
  62.5%    { color: #d4a03a; }   /* 6 - gold */
  75%      { color: #ffc14a; }   /* 7 - Festive Marigold */
  87.5%    { color: #f6e0ae; }   /* 8 - cooling into the page */
}

/* a slow shimmer in the linework, so the gold reads as sparkling */
@keyframes mandalaSparkle {
  0%, 100% { opacity: var(--o-min); }
  50%      { opacity: var(--o-max); }
}

/* --- motion 1: rises along a sine-swept path, fading in and out --- */
@keyframes mandalaRise {
  0% { opacity: 0; transform: translate3d(0.00px, -0.00vh, 0) rotate(0.00deg); }
  8% { opacity: 1; }
  8.3333% { transform: translate3d(15.00px, -11.17vh, 0) rotate(9.17deg); }
  16.667% { transform: translate3d(25.98px, -22.33vh, 0) rotate(18.33deg); }
  25% { transform: translate3d(30.00px, -33.50vh, 0) rotate(27.50deg); }
  33.333% { transform: translate3d(25.98px, -44.67vh, 0) rotate(36.67deg); }
  41.667% { transform: translate3d(15.00px, -55.83vh, 0) rotate(45.83deg); }
  50% { transform: translate3d(0.00px, -67.00vh, 0) rotate(55.00deg); }
  58.333% { transform: translate3d(-15.00px, -78.17vh, 0) rotate(64.17deg); }
  66.667% { transform: translate3d(-25.98px, -89.33vh, 0) rotate(73.33deg); }
  75% { transform: translate3d(-30.00px, -100.50vh, 0) rotate(82.50deg); }
  83.333% { transform: translate3d(-25.98px, -111.67vh, 0) rotate(91.67deg); }
  91.667% { transform: translate3d(-15.00px, -122.83vh, 0) rotate(100.83deg); }
  92% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(-0.00px, -134.00vh, 0) rotate(110.00deg); }
}
/* --- motion 2: turns in place --- */
@keyframes mandalaTurn {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
/* --- motion 3: drifts across on a gentle wave, fading in and out --- */
@keyframes mandalaDrift {
  0% { opacity: 0; transform: translate3d(0.00vw, 0.00vh, 0) rotate(0.00deg); }
  7% { opacity: 1; }
  8.3333% { transform: translate3d(11.00vw, -0.33vh, 0) rotate(8.00deg); }
  16.667% { transform: translate3d(22.00vw, -1.60vh, 0) rotate(16.00deg); }
  25% { transform: translate3d(33.00vw, -4.50vh, 0) rotate(24.00deg); }
  33.333% { transform: translate3d(44.00vw, -9.27vh, 0) rotate(32.00deg); }
  41.667% { transform: translate3d(55.00vw, -15.67vh, 0) rotate(40.00deg); }
  50% { transform: translate3d(66.00vw, -23.00vh, 0) rotate(48.00deg); }
  58.333% { transform: translate3d(77.00vw, -30.33vh, 0) rotate(56.00deg); }
  66.667% { transform: translate3d(88.00vw, -36.73vh, 0) rotate(64.00deg); }
  75% { transform: translate3d(99.00vw, -41.50vh, 0) rotate(72.00deg); }
  83.333% { transform: translate3d(110.00vw, -44.40vh, 0) rotate(80.00deg); }
  91.667% { transform: translate3d(121.00vw, -45.67vh, 0) rotate(88.00deg); }
  93% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(132.00vw, -46.00vh, 0) rotate(96.00deg); }
}

.mandala--rise  { animation: mandalaRise  linear infinite, mandalaTint cubic-bezier(0.42, 0, 0.58, 1) infinite; }
.mandala--turn  { animation: mandalaTurn  linear infinite, mandalaTint cubic-bezier(0.42, 0, 0.58, 1) infinite; }
.mandala--drift { animation: mandalaDrift linear infinite, mandalaTint cubic-bezier(0.42, 0, 0.58, 1) infinite; }
.mandala > svg  { animation: mandalaSparkle cubic-bezier(0.42, 0, 0.58, 1) infinite; }

/* Respect visitors who ask for less motion: the colour still breathes, travel stops */
@media (prefers-reduced-motion: reduce) {
  .mandala { animation: mandalaTint 40s cubic-bezier(0.42, 0, 0.58, 1) infinite !important; opacity: 1 !important; }
  .mandala > svg { animation: none !important; }
}

/* Never print the decoration */
@media print {
  .mandala-flow { display: none !important; }
}

/* ===================================================================
   GALLERY VIEWER - one photograph at a time in an Indian gold frame
   Nothing is shown until a collection is chosen; the whole viewer
   collapses again when the section loses focus.
   =================================================================== */
#galleryGrid[hidden] { display: none !important; }

.gallery-modes {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: 10px; margin: 0 auto 22px;
}
.mode-chip {
  padding: 8px 20px; border-radius: 99px; cursor: pointer;
  border: 1px solid var(--line); background: rgba(255, 255, 255, 0.7);
  color: var(--heading); font: inherit; font-size: 16px; font-weight: 600;
  transition: background 0.25s, color 0.25s, border-color 0.25s, box-shadow 0.25s;
}
.mode-chip:hover { background: rgba(255, 255, 255, 0.95); }
.mode-chip.active {
  background: linear-gradient(135deg, var(--heading), var(--copper));
  color: #fff; border-color: transparent;
  box-shadow: 0 10px 26px rgba(92, 45, 10, 0.28);
}
.gallery-prompt {
  text-align: center; color: var(--muted); font-style: italic;
  font-family: var(--font-display); font-size: 21px; margin: 8px 0 0;
}

.gallery-viewer {
  max-width: 860px; margin: 0 auto;
  animation: gvFadeIn 0.45s var(--ease) both;
}
.gallery-viewer[hidden] { display: none !important; }
@keyframes gvFadeIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* --- the gold frame --- */
.gv-frame {
  position: relative;
  padding: clamp(14px, 2vw, 26px);
  border-radius: 4px;
  background: none;
  box-shadow: none;
}

/* beaded inner band, echoing temple ornament */
.gv-mat {
  position: relative; padding: 9px; border-radius: 12px;
  background:
    repeating-linear-gradient(90deg, rgba(92,45,10,0.30) 0 2px, transparent 2px 8px),
    linear-gradient(160deg, #5c2d0a, #8b4513 55%, #5c2d0a);
  box-shadow:
    inset 0 0 0 1px rgba(255, 226, 160, 0.55),
    0 0 0 1px rgba(120, 70, 12, 0.55);
}
.gv-stage {
  position: relative; overflow: hidden; border-radius: 7px;
  background: #24120a; aspect-ratio: 3 / 2;
  box-shadow: inset 0 0 26px rgba(0, 0, 0, 0.55);
}
.gv-img {
  width: 100%; height: 100%; object-fit: contain;
  transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
}
.gv-img.gv-swap { opacity: 0; transform: scale(1.04); }

/* corner ornaments */
.gv-corner {
  position: absolute; width: 26px; height: 26px; pointer-events: none;
  border-color: rgba(255, 245, 210, 0.9); border-style: solid; border-width: 0;
}
.gv-corner--tl { top: 5px; left: 5px; border-top-width: 3px; border-left-width: 3px; border-top-left-radius: 14px; }
.gv-corner--tr { top: 5px; right: 5px; border-top-width: 3px; border-right-width: 3px; border-top-right-radius: 14px; }
.gv-corner--bl { bottom: 5px; left: 5px; border-bottom-width: 3px; border-left-width: 3px; border-bottom-left-radius: 14px; }
.gv-corner--br { bottom: 5px; right: 5px; border-bottom-width: 3px; border-right-width: 3px; border-bottom-right-radius: 14px; }

/* side navigation */
.gv-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(255, 226, 160, 0.75);
  background: rgba(36, 18, 10, 0.62); color: #ffeec2;
  font-size: 21px; line-height: 1; display: grid; place-items: center;
  backdrop-filter: blur(6px);
  transition: background 0.25s, color 0.25s, transform 0.25s, opacity 0.25s;
  z-index: 2;
}
.gv-nav:hover { background: var(--gold); color: #3d2814; transform: translateY(-50%) scale(1.08); }
.gv-nav--prev { left: 12px; }
.gv-nav--next { right: 12px; }
.gv-counter {
  position: absolute; right: 14px; bottom: 12px; z-index: 2;
  padding: 4px 12px; border-radius: 99px; font-size: 14px; font-weight: 700;
  letter-spacing: 0.06em; color: #ffeec2;
  background: rgba(36, 18, 10, 0.66); border: 1px solid rgba(255, 226, 160, 0.5);
}

.gv-caption { text-align: center; margin-top: 20px; }
.gv-caption strong {
  display: block; font-family: var(--font-display);
  font-size: 27px; font-weight: 600; color: var(--heading);
}
.gv-caption p { margin: 6px auto 0; max-width: 620px; color: var(--text); font-size: 17px; }
.gv-actions { display: flex; justify-content: center; flex-wrap: wrap; gap: 12px; margin-top: 20px; }

/* Progress hairline along the bottom of the frame.
   Bright and quick while the slideshow runs; faint and slow while the
   gallery is simply waiting for the arrows, so the visitor can see it
   is going to move on without feeling hurried by it. */
.gallery-viewer.is-playing .gv-stage::after,
.gallery-viewer.is-idling .gv-stage::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 3px; z-index: 3;
  border-radius: 0 99px 99px 0;
  /* full width, then scaled from the left - growing by transform rather
     than by width keeps the bar off the layout path and perfectly even */
  width: 100%; transform-origin: left center; will-change: transform;
}
.gallery-viewer.is-playing .gv-stage::after {
  background: linear-gradient(90deg, var(--gold), #f6e3ac);
  animation: gvProgress 4s linear infinite;
}
.gallery-viewer.is-idling .gv-stage::after {
  height: 2px;
  background: linear-gradient(90deg, oklch(0.72 0.11 66 / 0.5), oklch(0.86 0.10 86 / 0.6));
  animation: gvProgress 19s linear both;
}
@keyframes gvProgress { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@media (max-width: 720px) {
  .gv-frame { padding: 10px; border-radius: 14px; }
  .gv-nav { width: 38px; height: 38px; font-size: 18px; }
  .gv-nav--prev { left: 7px; } .gv-nav--next { right: 7px; }
  .gv-caption strong { font-size: 23px; }
}
@media (prefers-reduced-motion: reduce) {
  .gallery-viewer { animation: none; }
  .gv-img { transition: none; }
  .gallery-viewer.is-playing .gv-stage::after,
  .gallery-viewer.is-idling .gv-stage::after { animation: none; width: 100%; }
}

/* ===================================================================
   TEXT REVEAL SYSTEM
   Five effects, assigned by the role the text plays rather than at
   random across the board - because the wrong effect on body text
   makes people wait to read.

     eyebrow / small label  -> character stagger (short, decorative)
     heading                -> word stagger  or  line-safe blur-in
     lede / body            -> block fade-up  or  blur-in  (never split)
     one key line per page  -> gradient wipe

   Which of the permitted effects a given element gets is randomised on
   each visit, so the site does not reveal identically every time.
   =================================================================== */

/* Elements wait for .in-view, exactly as the existing reveal system does */
[data-reveal] { will-change: opacity, transform, filter; }

/* --- 1. block fade-up --- */
@keyframes txFadeUp { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
.in-view [data-reveal="fade-up"],
[data-reveal="fade-up"].in-view { animation: txFadeUp 0.55s var(--ease) both; }

/* --- 2. blur-in --- */
@keyframes txBlurIn { from { opacity: 0; filter: blur(10px); transform: translateY(10px); }
                      to   { opacity: 1; filter: blur(0);    transform: none; } }
.in-view [data-reveal="blur-in"],
[data-reveal="blur-in"].in-view { animation: txBlurIn 0.7s var(--ease) both; }

/* --- 3. word stagger (headings only) --- */
@keyframes txWordIn { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
[data-reveal="word"] .tx-w { display: inline-block; opacity: 0; }
.in-view [data-reveal="word"] .tx-w,
[data-reveal="word"].in-view .tx-w { animation: txWordIn 0.5s var(--ease) both; }

/* --- 4. character stagger (short labels only) --- */
@keyframes txCharIn { from { opacity: 0; transform: translateY(9px) scale(0.9); } to { opacity: 1; transform: none; } }
[data-reveal="char"] .tx-c { display: inline-block; white-space: pre; opacity: 0; }
.in-view [data-reveal="char"] .tx-c,
[data-reveal="char"].in-view .tx-c { animation: txCharIn 0.32s ease-out both; }

/* --- 5. gradient wipe (one line per page) --- */
@keyframes txWipe { from { background-size: 0% 100%; } to { background-size: 100% 100%; } }
[data-reveal="wipe"] {
  background-image: linear-gradient(90deg, var(--heading), var(--copper));
  background-repeat: no-repeat;
  background-size: 0% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: oklch(0.38 0.115 48 / 0.16);
  color: rgba(92, 45, 10, 0.16);
}
@supports (background-clip: text) or (-webkit-background-clip: text) {
  [data-reveal="wipe"] { color: transparent; -webkit-text-fill-color: transparent; }
  [data-reveal="wipe"]::selection { -webkit-text-fill-color: currentColor; }
}
.in-view [data-reveal="wipe"],
[data-reveal="wipe"].in-view { animation: txWipe 1.5s cubic-bezier(0.4, 0, 0.2, 1) both; }

/* Scroll-linked version of the wipe for the single dedication line.
   Progressive enhancement: without support it simply plays on reveal. */
@supports (animation-timeline: view()) {
  [data-reveal="wipe"][data-scroll-linked] {
    animation: txWipe linear both;
    animation-timeline: view();
    animation-range: cover 20% cover 58%;
  }
}

/* --- staggered card reveals ---
   Two separate concerns, deliberately kept apart:
     .observe-card  is hidden until revealed - only ever set in the HTML,
                    where the reveal observer can see it from the start.
     .stagger-item  carries a delay and nothing else. Safe to add at any
                    time to elements that are already visible.
   Mixing the two is what emptied whole sections. */
.in-view .observe-card:nth-child(1),  .observe-card.in-view:nth-child(1),
.stagger-item:nth-child(1)  { transition-delay: 0ms;   animation-delay: 0ms; }
.in-view .observe-card:nth-child(2),  .observe-card.in-view:nth-child(2),
.stagger-item:nth-child(2)  { transition-delay: 70ms;  animation-delay: 70ms; }
.in-view .observe-card:nth-child(3),  .observe-card.in-view:nth-child(3),
.stagger-item:nth-child(3)  { transition-delay: 140ms; animation-delay: 140ms; }
.in-view .observe-card:nth-child(4),  .observe-card.in-view:nth-child(4),
.stagger-item:nth-child(4)  { transition-delay: 210ms; animation-delay: 210ms; }
.in-view .observe-card:nth-child(5),  .observe-card.in-view:nth-child(5),
.stagger-item:nth-child(5)  { transition-delay: 280ms; animation-delay: 280ms; }
.in-view .observe-card:nth-child(6),  .observe-card.in-view:nth-child(6),
.stagger-item:nth-child(6)  { transition-delay: 350ms; animation-delay: 350ms; }
.in-view .observe-card:nth-child(7),  .observe-card.in-view:nth-child(7),
.stagger-item:nth-child(7)  { transition-delay: 400ms; animation-delay: 400ms; }
.in-view .observe-card:nth-child(n+8), .observe-card.in-view:nth-child(n+8),
.stagger-item:nth-child(n+8) { transition-delay: 450ms; animation-delay: 450ms; }

/* A delay must never apply to a hover or press - those have to feel
   instant, and an inherited 450ms would make the whole site feel broken. */
.stagger-item:hover, .stagger-item:active,
.stagger-item:focus-visible { transition-delay: 0ms !important; }



/* Nobody should ever wait to read. Reduced motion shows everything at once. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal], [data-reveal] .tx-w, [data-reveal] .tx-c {
    animation: none !important; opacity: 1 !important; filter: none !important; transform: none !important;
  }
  [data-reveal="wipe"] { background-size: 100% 100% !important; }
  .observe-card { animation-delay: 0ms !important; }
}

/* ===================================================================
   ACCORDIONS - proper height animation
   The panels above transition block-size: 0 -> auto. That only works
   because of interpolate-size below, which lets the browser animate to
   an intrinsic size instead of a number we had to guess. The old code
   used max-height with invented ceilings (460px, 600px, 200px), which
   is why long reflections were clipped and short ones eased oddly.

   @starting-style gives elements an entry state to animate FROM the
   first time they render, and transition-behavior: allow-discrete lets
   display and content-visibility participate in the transition, so a
   panel can animate in and out of display: none.
   =================================================================== */
:root { interpolate-size: allow-keywords; }

.story-copy, .tl-people, .help-body {
  transition-behavior: allow-discrete;
}

/* Timeline photo clusters can now fade rather than snap into place */
.tl-photo, .tl-photo-cluster, .tl-photo-stack, .tl-photo-grid {
  opacity: 0;
  transition: opacity 0.45s var(--ease), display 0.45s allow-discrete;
}
.timeline-item.story-open .tl-photo,
.timeline-item.story-open .tl-photo-cluster,
.timeline-item.story-open .tl-photo-stack,
.timeline-item.story-open .tl-photo-grid {
  opacity: 1;
}
@starting-style {
  .timeline-item.story-open .tl-photo,
  .timeline-item.story-open .tl-photo-cluster,
  .timeline-item.story-open .tl-photo-stack,
  .timeline-item.story-open .tl-photo-grid { opacity: 0; }
}

/* The gallery viewer and tour bubble get a proper entry state too */
.gallery-viewer {
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease),
              display 0.4s allow-discrete, overlay 0.4s allow-discrete;
}
@starting-style {
  .gallery-viewer { opacity: 0; transform: translateY(14px); }
}

/* Native <details> in the help section, animated the same way */
details::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.35s var(--ease), content-visibility 0.35s allow-discrete;
}
details[open]::details-content { block-size: auto; }

@media (prefers-reduced-motion: reduce) {
  .story-copy, .tl-people, .help-body, .gallery-viewer,
  details::details-content { transition: none !important; }
}

/* ===================================================================
   VIEW TRANSITIONS + BLUR-UP PHOTOGRAPHS
   =================================================================== */

/* Gallery photo swaps are handed to the browser's View Transition when
   available. Naming the image gives the browser a pair to morph between. */
#galleryViewerImage {
  view-transition-name: gallery-photo;
  /* never blurred - the gallery relies on its two transitions instead */
  filter: none !important;
}

::view-transition-old(gallery-photo) {
  animation: gvOut 0.32s cubic-bezier(0.4, 0, 1, 1) both;
}
::view-transition-new(gallery-photo) {
  animation: gvIn 0.42s cubic-bezier(0, 0, 0.2, 1) both;
}
@keyframes gvOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.98); } }
@keyframes gvIn  { from { opacity: 0; transform: scale(1.03); } to { opacity: 1; transform: scale(1); } }

/* Keep the transition contained to the photograph, not the whole page */
::view-transition-group(root) { animation-duration: 0.3s; }

/* --- blur-up --- */
.img-blur-up {
  filter: blur(14px);
  transform: scale(1.04);
  transition: filter 0.6s var(--ease), transform 0.6s var(--ease), opacity 0.4s;
}
.img-blur-up.img-ready {
  filter: blur(0);
  transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
  .img-blur-up { filter: none !important; transform: none !important; transition: none !important; }
  ::view-transition-old(gallery-photo),
  ::view-transition-new(gallery-photo) { animation: none !important; }
}

/* ===================================================================
   HEADER CONTROLS - language switch and sound toggle
   =================================================================== */
@media (max-width: 980px) {
}

/* ===================================================================
   THE OPENING — a diya is kindled
   ETERNAL FLAME: once lit, the flame is never animated out. There is no
   extinguish keyframe anywhere in this file. It burns through the
   opening, through the farewell, and on the closed screen after the tab
   is dismissed. Letting a lit diya go dark would be inauspicious, so the
   light is the one thing on this site that never ends.

   Fifteen seconds of choreography, staged so every beat has room:
     0.0s  the lamp settles onto its plate
     1.5s  the gold rim catches the light
     2.5s  the beading lights, one bead at a time
     3.2s  oil pools in the bowl
     4.2s  the wick rises
     5.0s  the flame takes — a leap, a settle, then it steadies
     6.5s  the glow swells and begins to breathe
     7.5s  the outer halo opens
     8.5s  the dedication line rises
    10.0s  the title
    11.5s  their names
    12.8s  Begin and Exit
    15.0s  the scene retires — the flame still burning as it goes
   =================================================================== */
.threshold {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  overflow: hidden;
  /* Layered light rather than one flat ramp: the lamp's own warmth at the
     centre, a warm source off the upper left, cool depth in the lower
     right, and a vignette to close the corners. That is what stops it
     reading as a printed gradient. */
  background:
    radial-gradient(ellipse 46% 38% at 50% 40%,  rgba(255, 176,  74, 0.20), transparent 64%),
    radial-gradient(ellipse 70% 62% at  6%  4%,  rgba(190, 116,  36, 0.34), transparent 62%),
    radial-gradient(ellipse 78% 70% at 96% 98%,  rgba( 34,  12,  50, 0.66), transparent 66%),
    radial-gradient(ellipse 128% 104% at 50% 50%, transparent 40%, rgba(6, 3, 10, 0.74) 100%),
    linear-gradient(116deg,
      #33200e 0%, #3d2413 20%, #45213a 48%, #2b1430 74%, #150b1c 100%);
  background-color: #150b1c;
  /* No auto-dismiss. The opening stays until Begin or Exit is pressed. */
}



.threshold.is-dismissed { animation: thresholdOut 0.7s var(--ease-soft) both; }
.threshold.is-gone { display: none; }
/* The scene fades as a whole. The flame inside it is at full strength
   the entire time - it is never dimmed, only carried out of view. */
@keyframes thresholdOut {
  from { opacity: 1; visibility: visible; transform: scale(1); }
  to   { opacity: 0; visibility: hidden;  transform: scale(1.05); }
}
body.threshold-open { overflow: hidden; }

/* ===================================================================
   THE VINE-CORNER BORDER — a single gold flourish-and-hairline frame,
   asset kept exactly as supplied (only its magenta backing removed).

   Two earlier attempts put this border directly on .threshold/.farewell
   itself, since border-image there would have let the box model shrink
   the content away from the border automatically. Both times it came
   back with a dark, hard-edged band sitting between the border and the
   content instead of a clean gold line. Root cause: .threshold and
   .farewell already carry a large radial-gradient vignette as their
   OWN background, and putting a real border on that same box changes
   the box (border-box vs padding-box) later `background` shorthand
   declarations size that vignette against - the vignette's darkest
   ring ended up landing at the border's inner edge instead of the true
   screen edge, then continuing flat past it. A background-origin fix
   was tried and, going by the report, still did not clear it.

   Rather than keep fighting that one shared box, the border now lives
   on its own empty ::after - a second, separate box stacked on top
   with position: absolute; inset: 0, and NO background of its own at
   all. There is nothing on this box for a gradient to interact with,
   so the class of bug above cannot recur here regardless of the exact
   mechanism behind it. .threshold/.farewell's own vignette background
   renders completely untouched, exactly as it did before any of this
   border work began.

   border-image still cuts the source into nine regions - four corners
   and four edges. The corner regions (via border-image-slice) are cut
   generously enough to hold the whole ornate flourish untouched, and
   because border-width is equal on every side, each square corner
   slice lands on an equally square patch of screen - a uniform scale
   up or down, never a stretch in only one direction. Only the four
   EDGE strips - the asset's own plain double hairline, no ornament in
   them - resize to reach the screen's actual size, and
   border-image-repeat: round repeats that plain line a whole number
   of times rather than stretching one long copy, so nothing in the
   image, ornate or plain, is ever visibly stretched.

   Since this border no longer occupies real layout space (an overlay,
   not a true border on the content box), .threshold/.farewell are
   given plain padding equal to the same width instead - ordinary
   padding, nothing border- or background-related, so it cannot ever
   trigger the same interaction. That padding is what keeps
   .threshold-inner's content clear of the border's own footprint: the
   border and the content still can never overlap, and no text can
   still ever be cut. */
/* REMOVED — the vine-corner border is gone from both screens.

   Three things had to come out together, not just the border itself:

   1. The border overlay (.threshold::after / .farewell::after) is
      removed outright. Leaving the rule in place but only clearing
      border-image-source would have been worse than useless: with
      border-style: solid and a 70-190px border-width still declared,
      the browser falls back to painting a plain solid band of that
      full width in the current text colour.
   2. The clamp(70px, 9vw, 190px) padding on .threshold/.farewell
      existed for one reason only - to hold the content clear of that
      border's footprint. With no border there is nothing to clear, so
      it is removed too; both screens go back to the base rule's own
      spacing and the content re-centres in the full viewport instead
      of sitting inside a 190px inset.
   3. The matching 720px media-query overrides for both of the above
      are removed, so nothing reintroduces either at narrow widths.

   The z-index line below is kept: it is what stacks the content above
   the screens' own background layers, and has nothing to do with the
   border. */
.threshold-inner, .farewell-inner, .sacred-content { position: relative; z-index: 1; }

/* Full width, centred by text-align rather than by shrink-to-fit.
   The old rule capped this box at 680px while the opening line runs to
   about 1220px, so the content overflowed its own alignment container -
   and once that happens justify-items: center stops being reliable and
   the browser drifts toward start-edge alignment. Text centring inside a
   full-width block cannot drift, whatever the content measures. */
.threshold-inner {
  display: block;
  width: 100%;
  max-width: none;
  padding: 26px 16px;
  text-align: center;
}

/* ---------------- the lamp ---------------- */
.diya-scene { position: relative; width: 260px; height: 220px; margin: 0 auto 6px; }

.diya-lamp {
  position: absolute; bottom: 0; left: 50%; width: 220px; height: 116px;
  transform: translateX(-50%);
  animation: lampSettle 1.6s var(--ease-spring) 0.2s both;
}
@keyframes lampSettle {
  from { opacity: 0; transform: translateX(-50%) translateY(34px) scale(0.88); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
/* The lamp carries its own gradients now. A CSS `fill` here would beat the
   element's own fill attribute, and #clayGrad no longer exists - that is
   exactly what left the bowl transparent. Only the stroke comes from CSS. */
.d-body  { stroke: rgba(232, 193, 98, 0.55); stroke-width: 1.1; }
.d-plate { fill: oklch(0.36 0.09 62); opacity: 0.9; }
.d-spout { fill: oklch(0.68 0.13 76); stroke: oklch(0.86 0.12 88); stroke-width: 1; }
.d-rim {
  fill: none; stroke: url(#rimGrad); stroke-width: 3.2; stroke-linecap: round;
  animation: fadeUpIn 1.5s var(--ease-soft) 1.5s both;
}
@keyframes fadeUpIn { from { opacity: 0; } to { opacity: 1; } }
.d-oil   { /* fill comes from the element itself */ }
@keyframes oilPool    { from { opacity: 0; } to { opacity: 0.85; } }
@keyframes oilShimmer { 0%, 100% { opacity: 0.76; } 50% { opacity: 0.96; } }
.d-beads circle {
  fill: oklch(0.9 0.14 92); opacity: 0;
  animation: beadLight 0.9s var(--ease-spring) both;
}
.d-beads circle:nth-child(1) { animation-delay: 2.5s; }
.d-beads circle:nth-child(2) { animation-delay: 2.64s; }
.d-beads circle:nth-child(3) { animation-delay: 2.78s; }
.d-beads circle:nth-child(4) { animation-delay: 2.92s; }
.d-beads circle:nth-child(5) { animation-delay: 3.06s; }
.d-beads circle:nth-child(6) { animation-delay: 3.2s; }
@keyframes beadLight { from { opacity: 0; transform: scale(0.2); transform-origin: center; }
                       to   { opacity: 1; transform: scale(1); } }
.d-petals path {
  fill: none; stroke: oklch(0.45 0.1 55); stroke-width: 1.3; opacity: 0;
  animation: fadeUpIn 1.3s var(--ease-soft) 2.9s both;
}

/* Stacking inside the lamp scene. Without this the lamp SVG - which comes
   last in the markup - paints over the flame, and the flame reads as if it
   were burning behind the bowl. */
.diya-scene .diya-halo  { z-index: 0; }
.diya-scene .diya-glow  { z-index: 0; }
.diya-scene .diya-lamp  { z-index: 1; }
.diya-scene .diya-wick  { z-index: 2; }
.diya-scene .flame      { z-index: 3; }
.diya-scene .ember      { z-index: 3; }

.flame-halo {
  /* The flame's own celestial glow - tight, centred on the flame itself,
     breathing on its own rhythm rather than the lamp's. */
  position: absolute; left: 50%; bottom: 46px;
  width: 210px; height: 210px; transform: translateX(-50%);
  border-radius: 50%; pointer-events: none; z-index: 2;
  background: radial-gradient(circle,
    rgba(255, 214, 130, 0.50) 0%, rgba(255, 176, 70, 0.26) 30%,
    rgba(255, 150, 50, 0.10) 56%, transparent 74%);
  filter: blur(10px);
  animation: glowSwell 2.2s var(--ease-soft) 1.9s both,
             flameHaloBreathe 6.8s ease-in-out 4.1s infinite;
}
@keyframes flameHaloBreathe {
  0%, 100% { opacity: 0.62; transform: translateX(-50%) scale(0.92); }
  50%      { opacity: 1;    transform: translateX(-50%) scale(1.12); }
}

.diya-wick {
  position: absolute; left: 50%; bottom: 79px; width: 4px; height: 15px;
  transform: translateX(-50%); border-radius: 2px 2px 0 0;
  background: linear-gradient(to top, oklch(0.42 0.05 60), oklch(0.30 0.03 55) 60%, oklch(0.58 0.14 55));
  animation: wickRise 0.9s var(--ease-spring) 1.1s both;
}
@keyframes wickRise { from { opacity: 0; transform: translateX(-50%) scaleY(0.15); }
                      to   { opacity: 1; transform: translateX(-50%) scaleY(1); } }

/* ---------------- the eternal flame ----------------
   Four layers, each on its own flicker rhythm. The durations are
   deliberately uneven (1.07 / 1.31 / 1.73 / 2.11s) so they never line
   up - a flame whose layers sync looks mechanical. The container adds a
   slow sway and a gentle breath on top. Once lit, none of these
   animations ever reduce opacity to zero or scale toward nothing.      */
.flame {
  position: absolute; left: 50%; bottom: 88px; width: 50px; height: 96px;
  transform: translateX(-50%); transform-origin: 50% 100%;
  animation:
    flameCatch 1.8s var(--ease-spring) 1.5s both,
    flameSway 7.3s ease-in-out 3.3s infinite,
    flameBreathe 3.9s ease-in-out 3.3s infinite;
}
/* the flame takes hold: a spark, a leap, a settle, then steady */
@keyframes flameCatch {
  0%   { opacity: 0;    transform: translateX(-50%) scale(0.08, 0.03); }
  14%  { opacity: 0.85; transform: translateX(-50%) scale(0.45, 0.28); }
  38%  { opacity: 1;    transform: translateX(-50%) scale(1.18, 1.36); }
  58%  { transform: translateX(-50%) scale(0.92, 0.88); }
  76%  { transform: translateX(-50%) scale(1.06, 1.08); }
  100% { opacity: 1;    transform: translateX(-50%) scale(1, 1); }
}
@keyframes flameSway {
  0%, 100% { transform: translateX(-50%) rotate(0deg)    skewX(0deg); }
  18%      { transform: translateX(-50%) rotate(-1.8deg) skewX(-1.2deg); }
  37%      { transform: translateX(-50%) rotate(0.9deg)  skewX(0.6deg); }
  55%      { transform: translateX(-50%) rotate(2.1deg)  skewX(1.4deg); }
  74%      { transform: translateX(-50%) rotate(-0.7deg) skewX(-0.5deg); }
}
@keyframes flameBreathe {
  0%, 100% { height: 96px; }
  33%      { height: 104px; }
  67%      { height: 90px; }
}
.flame > span { position: absolute; left: 50%; bottom: 0; transform: translateX(-50%); }

.flame-outer {
  width: 52px; height: 100px; border-radius: 50% 50% 44% 44% / 68% 68% 32% 32%;
  background: radial-gradient(ellipse at 50% 84%,
    oklch(0.74 0.20 46 / 0.80) 0%, oklch(0.68 0.19 38 / 0.38) 52%, transparent 78%);
  filter: blur(4px);
  animation: flickerA 2.11s ease-in-out infinite;
}
.flame-mid {
  width: 31px; height: 70px; border-radius: 50% 50% 46% 46% / 66% 66% 34% 34%;
  background: linear-gradient(to top,
    oklch(0.70 0.20 44) 0%, oklch(0.82 0.19 68) 34%, oklch(0.91 0.16 86) 72%, oklch(0.96 0.11 94) 100%);
  filter: blur(0.7px);
  animation: flickerB 1.73s ease-in-out infinite;
}
.flame-core {
  width: 15px; height: 40px; border-radius: 50% 50% 48% 48% / 64% 64% 36% 36%;
  background: linear-gradient(to top, oklch(0.88 0.12 88), oklch(0.99 0.045 100));
  animation: flickerC 1.31s ease-in-out infinite;
}
.flame-base {
  width: 11px; height: 13px; border-radius: 50%;
  background: radial-gradient(circle, oklch(0.64 0.17 236 / 0.8), oklch(0.6 0.14 240 / 0.25) 55%, transparent 72%);
  animation: flickerD 1.07s ease-in-out infinite;
}
/* Four distinct flicker shapes. Note every one returns to full opacity -
   none of them approaches zero, so the flame can never read as dying. */
@keyframes flickerA {
  0%, 100% { transform: translateX(-50%) scale(1, 1);       opacity: 0.92; }
  27%      { transform: translateX(-50%) scale(0.96, 1.08); opacity: 1; }
  61%      { transform: translateX(-50%) scale(1.05, 0.94); opacity: 0.88; }
}
@keyframes flickerB {
  0%, 100% { transform: translateX(-50%) scale(1, 1);       opacity: 1; }
  22%      { transform: translateX(-50%) scale(0.94, 1.10); opacity: 0.96; }
  48%      { transform: translateX(-50%) scale(1.06, 0.93); opacity: 1; }
  73%      { transform: translateX(-50%) scale(0.98, 1.05); opacity: 0.98; }
}
@keyframes flickerC {
  0%, 100% { transform: translateX(-50%) scale(1, 1)        translateY(0);    opacity: 1; }
  31%      { transform: translateX(-50%) scale(0.9, 1.14)   translateY(-2px); opacity: 1; }
  66%      { transform: translateX(-50%) scale(1.08, 0.9)   translateY(1px);  opacity: 0.95; }
}
@keyframes flickerD {
  0%, 100% { transform: translateX(-50%) scale(1);    opacity: 0.85; }
  40%      { transform: translateX(-50%) scale(1.16); opacity: 1; }
  70%      { transform: translateX(-50%) scale(0.92); opacity: 0.78; }
}

/* embers lifting off, endlessly */
.ember {
  position: absolute; left: 50%; bottom: 158px; width: 3px; height: 3px;
  border-radius: 50%; background: oklch(0.88 0.15 82); opacity: 0;
  animation: emberRise 4.4s ease-out infinite;
}
.ember.e1 { animation-delay: 7.2s;  --dx: -18px; }
.ember.e2 { animation-delay: 9.1s;  --dx: 14px;  width: 2px; height: 2px; }
.ember.e3 { animation-delay: 11.4s; --dx: -7px; }
@keyframes emberRise {
  0%   { opacity: 0;   transform: translate(-50%, 0) scale(0.5); }
  15%  { opacity: 0.95; }
  100% { opacity: 0;   transform: translate(calc(-50% + var(--dx)), -88px) scale(0.15); }
}

.diya-glow {
  position: absolute; left: 50%; bottom: 62px; width: 320px; height: 320px;
  transform: translateX(-50%); border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, oklch(0.89 0.15 86 / 0.5) 0%, oklch(0.77 0.16 64 / 0.2) 36%, transparent 68%);
  animation: glowSwell 2.0s var(--ease-soft) 2.0s both, glowBreathe 5.7s ease-in-out 4.0s infinite;
}
.diya-halo {
  position: absolute; left: 50%; bottom: 20px; width: 500px; height: 500px;
  transform: translateX(-50%); border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, oklch(0.73 0.13 70 / 0.17) 0%, transparent 62%);
  animation: glowSwell 2.4s var(--ease-soft) 2.3s both, haloBreathe 9.1s ease-in-out 4.7s infinite;
}
@keyframes glowSwell   { from { opacity: 0; transform: translateX(-50%) scale(0.35); }
                         to   { opacity: 1; transform: translateX(-50%) scale(1); } }
@keyframes glowBreathe { 0%, 100% { opacity: 0.82; transform: translateX(-50%) scale(1); }
                         50%      { opacity: 1;    transform: translateX(-50%) scale(1.06); } }
@keyframes haloBreathe { 0%, 100% { opacity: 0.85; transform: translateX(-50%) scale(1); }
                         50%      { opacity: 1;    transform: translateX(-50%) scale(1.04); } }

/* ---------------- the words ---------------- */
.threshold-line {
  /* First words on the screen, directly beneath the lamp. The 1.58vw cap
     is what holds the two long lines inside the viewport - it scales with
     the text, so raising the maximum never causes overflow. */
  margin: 14px 0 0; font-family: var(--font-display); font-style: italic;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.62vw);
  line-height: 1.6;
  /* dark gold, lifted off the background by a warm glow */
  color: #c98f14;
  text-shadow: 0 0 22px rgba(255, 198, 96, 0.50),
               0 2px 7px rgba(24, 8, 18, 0.92),
               0 1px 2px rgba(18, 5, 12, 0.98);
  white-space: nowrap;
  max-width: none;
  text-align: center;
  text-align-last: center;
  /* the words are revealed one by one, each with its own effect */
  animation: none;
  opacity: 1;
}
/* Two lines of 130-odd characters cannot hold at a readable size on a
   tablet or phone - it would need type under about 14px. Below this the
   text wraps freely instead, justified with the last line centred. */
@media (max-width: 900px) {
  .threshold-line {
    white-space: normal;
    font-size: clamp(26px, 5.2vw, 40px);
    line-height: 1.66;
    /* Centred only - no justification. */
    text-align: center;
    text-align-last: center;
    hyphens: none;
    text-wrap: pretty;
    max-width: 42ch;
  }

  /* the names keep pace with the heading here too - the 1.58vw cap is
     tuned for the 132-character heading and would crush a 56-character
     line to under 6px on a phone */
  .threshold-names {
    font-size: clamp(24px, 6.6vw, 31px);
    max-width: 16ch;          /* one name per line */
  }
}
.threshold-title {
  /* Matches the Sacred Dedication heading exactly: same clamp, same
     face, same weight. Only the colour differs - the section brown
     would vanish on this dark screen. */
  margin: 20px 0 0;
  font-family: var(--font-display); font-style: normal; font-weight: 700;
  font-size: clamp(32px, 4vw, 56px);
  line-height: 1.16; letter-spacing: -0.012em;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility; text-wrap: balance;
  color: #ffc95e;
  text-shadow: 0 0 26px rgba(255, 206, 110, 0.55),
               0 2px 9px rgba(40, 12, 30, 0.88),
               0 1px 2px rgba(30, 8, 20, 0.95);
  text-align: center;
  animation: riseIn 1.6s var(--ease-soft) 4.0s both;
}
.threshold-names {
  /* Same size as the heading above - the two are a pair. Bold, in High
     Tower Text with lookalike fallbacks for machines without it. */
  font-family: 'High Tower Text', 'Cormorant Garamond', 'Book Antiqua',
               'Palatino Linotype', Palatino, Georgia, serif;
  font-style: normal;
  font-weight: 700;
  margin: 18px 0 0;
  font-size: min(clamp(29px, 1.6vw, 34px), 1.58vw);
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: #ffd98c;
  text-shadow: 0 0 22px rgba(255, 206, 110, 0.55),
               0 2px 7px rgba(40, 12, 30, 0.88),
               0 1px 2px rgba(30, 8, 20, 0.95);
  text-shadow: 0 0 20px rgba(255, 219, 130, 0.65), 0 0 46px rgba(255, 190, 80, 0.35),
               0 1px 2px rgba(50, 20, 8, 0.5);
  text-align: center;
  text-wrap: balance;      /* on narrow screens: one name per line */
  max-width: 30ch;
  margin-left: auto; margin-right: auto;
  animation: riseIn 1.4s var(--ease-soft) 3.0s both;
}
@keyframes riseIn { from { opacity: 0; transform: translateY(20px); filter: blur(7px); }
                    to   { opacity: 1; transform: none; filter: blur(0); } }

.threshold-actions {
  /* Brought forward from 12.8s, then from 5.8s. The names land at 3.0s
     and the heading and this row now follow one second later, so the
     visitor is not left waiting on an animation before they can choose.
     The choreography continues behind it. */
  display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 26px;
  animation: riseIn 1.1s var(--ease-spring) 4.0s both;
}
.threshold-btn {
  font: inherit; font-size: 15px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 13px 34px; border-radius: 99px; cursor: pointer;
  transition: transform 0.32s var(--ease-spring), box-shadow 0.32s var(--ease-soft), background 0.28s;
}
.threshold-begin {
  border: 0; color: oklch(0.22 0.05 50);
  background: linear-gradient(135deg, oklch(0.86 0.15 88), oklch(0.74 0.17 60));
  box-shadow: 0 14px 36px oklch(0.62 0.16 66 / 0.45);
}
.threshold-begin:hover  { transform: translateY(-3px) scale(1.04); box-shadow: 0 20px 48px oklch(0.62 0.16 66 / 0.55); }
.threshold-begin:active { transform: translateY(-1px) scale(0.97); }
.threshold-exit {
  background: transparent; color: oklch(0.82 0.05 80 / 0.8);
  border: 1px solid oklch(0.8 0.05 80 / 0.32);
}
.threshold-exit:hover  { color: oklch(0.92 0.08 84); border-color: oklch(0.8 0.09 82 / 0.7);
                         background: oklch(0.8 0.09 82 / 0.1); transform: translateY(-3px) scale(1.03); }
.threshold-exit:active { transform: translateY(-1px) scale(0.97); }



/* ===================================================================
   THE CLOSING — the lamp is left burning
   The same fifteen-second choreography, and the same eternal flame.
   The flame is NOT lowered, dimmed or extinguished on the way out. The
   halo widens instead, as though the light carries past the page.
   =================================================================== */
.farewell {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  overflow: hidden;
  /* identical to the opening screen, so leaving mirrors arriving */
  background:
    radial-gradient(ellipse 46% 38% at 50% 40%,  rgba(255, 176,  74, 0.20), transparent 64%),
    radial-gradient(ellipse 70% 62% at  6%  4%,  rgba(190, 116,  36, 0.34), transparent 62%),
    radial-gradient(ellipse 78% 70% at 96% 98%,  rgba( 34,  12,  50, 0.66), transparent 66%),
    radial-gradient(ellipse 128% 104% at 50% 50%, transparent 40%, rgba(6, 3, 10, 0.74) 100%),
    linear-gradient(116deg,
      #33200e 0%, #3d2413 20%, #45213a 48%, #2b1430 74%, #150b1c 100%);
  background-color: #150b1c;
  animation: farewellIn 0.9s var(--ease-soft) both;
}
.farewell-inner {
  position: relative; z-index: 1;
  width: 100%; max-width: 100%; box-sizing: border-box;
  margin-left: auto; margin-right: auto; text-align: center;
}
.farewell-inner::before {
  content: ''; position: absolute; inset: -6% -5%;
  background: radial-gradient(ellipse at 50% 50%,
    rgba(12, 5, 16, 0.42) 0%, rgba(12, 5, 16, 0.28) 46%, transparent 74%);
  filter: blur(26px); z-index: -1; pointer-events: none;
}

@keyframes farewellIn { from { opacity: 0; } to { opacity: 1; } }
.farewell-inner { display: block; width: 100%; max-width: none; padding: 26px 16px; text-align: center; }

/* the light spreads rather than fading */
.farewell .diya-glow {
  animation: glowSwell 2s var(--ease-soft) 0.6s both, glowBreathe 5.7s ease-in-out 2.6s infinite;
}
.farewell .diya-halo {
  animation: haloWiden 6s var(--ease-soft) 1.2s both, haloBreathe 9.1s ease-in-out 7.2s infinite;
}
@keyframes haloWiden {
  from { opacity: 0.5; transform: translateX(-50%) scale(0.8); }
  to   { opacity: 1;   transform: translateX(-50%) scale(1.18); }
}

.farewell-line {
  /* the closing line, now last and smaller - it sits above the buttons */
  font-family: 'High Tower Text', 'Cormorant Garamond', 'Book Antiqua',
               'Palatino Linotype', Palatino, Georgia, serif;
  font-style: normal; font-weight: 700;
  margin: 14px 0 0;
  font-size: clamp(18px, 2.2vw, 31px);
  line-height: 1.3; letter-spacing: 0.03em;
  color: oklch(0.88 0.08 74);
  text-align: center;
  /* the words are revealed one by one, each with its own effect */
  animation: none;
  opacity: 1;
}
.farewell-names {
  font-family: 'High Tower Text', 'Cormorant Garamond', 'Book Antiqua',
               'Palatino Linotype', Palatino, Georgia, serif;
  font-style: italic;
  margin: 16px 0 0;
  font-size: clamp(30px, 4.4vw, 60px);
  line-height: 1.2; letter-spacing: 0.02em;
  color: oklch(0.85 0.07 62);
  text-align: center;
  animation: riseIn 1.3s var(--ease-soft) 0.6s both;
}
.farewell-blessing {
  margin: 12px 0 0;
  font-family: var(--font-display); font-style: italic;
  font-size: clamp(13px, 1.7vw, 17px);
  letter-spacing: 0.04em; color: oklch(0.76 0.05 70 / 0.86);
  text-align: center;
  animation: riseIn 1.0s var(--ease-soft) 1.0s both;
}
.farewell-actions {
  display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin-top: 28px;
  animation: riseIn 1.0s var(--ease-spring) 1.0s both;
}

@media (max-width: 560px) {
  .diya-scene { width: 200px; height: 178px; transform: scale(0.86); }
  .threshold-btn { padding: 11px 26px; font-size: 14px; }
}

/* Reduced motion: the flame still burns, it simply does not dance. */
@media (prefers-reduced-motion: reduce) {
  /* The screen still waits for Begin or Exit; only the choreography is
     collapsed so the buttons are available immediately. */
  .threshold-actions { animation: none !important; }
  .diya-lamp, .diya-wick, .flame, .flame > span, .diya-glow, .diya-halo,
  .d-rim, .d-oil, .d-beads circle, .d-petals path,
  .threshold-line, .threshold-title, .threshold-names, .threshold-actions,
  .farewell-line, .farewell-names, .farewell-blessing, .farewell-actions {
    animation: none !important; opacity: 1 !important; filter: none !important;
  }
  .diya-lamp, .diya-wick, .flame, .diya-glow, .diya-halo { transform: translateX(-50%) !important; }
  .flame > span { transform: translateX(-50%) !important; }
  .flame-outer { filter: blur(4px) !important; }
  .flame-mid { filter: blur(0.7px) !important; }
  .ember { display: none; }
  .farewell { animation-duration: 0.01s !important; }
}

/* ===================================================================
   SMOOTH + BUBBLY
   Interactive things gain a little spring: they swell a touch on hover
   and compress when pressed, the way a physical button would. Applied
   only to controls - never to text, where an overshoot would make words
   wobble while somebody is reading them.
   =================================================================== */
.btn, .virtue-btn, .tab, .help-toggle, .filter-chip, .mode-chip,
.nav-exit-btn, .tour-actions button, .gv-nav, .gallery-card,
.expand-pub-table-btn, .modal-close, .menu-toggle {
  transition:
    transform 0.34s var(--ease-spring),
    box-shadow 0.34s var(--ease-soft),
    background 0.26s var(--ease-soft),
    color 0.26s var(--ease-soft),
    border-color 0.26s var(--ease-soft);
}

/* hover: rise and swell a little */
.btn:hover, .virtue-btn:hover, .tab:hover, .help-toggle:hover,
.filter-chip:hover, .mode-chip:hover, .nav-exit-btn:hover,
.expand-pub-table-btn:hover {
  transform: translateY(-3px) scale(1.035);
}
/* press: squash back down. This is the half people notice without
   noticing - a button that only rises feels unfinished. */
.btn:active, .virtue-btn:active, .tab:active, .help-toggle:active,
.filter-chip:active, .mode-chip:active, .nav-exit-btn:active,
.expand-pub-table-btn:active, .gv-nav:active, .modal-close:active {
  transform: translateY(-1px) scale(0.955);
  transition-duration: 0.12s;
}

/* active pills settle with a gentle pop rather than snapping */
.filter-chip.active, .mode-chip.active, .tab.active, .virtue-btn.active {
  animation: bubblePop 0.42s var(--ease-spring);
}
@keyframes bubblePop {
  0%   { transform: scale(0.9); }
  60%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}

/* cards lift with a touch of spring */
.gallery-card:hover, .tribute-card:hover, .help-card:hover, .stat-card:hover {
  transform: translateY(-6px) scale(1.02);
}

/* nav underline grows from the centre */
.nav-links a { position: relative; transition: color 0.24s var(--ease-soft); }
.nav-links a::after {
  content: ""; position: absolute; left: 50%; bottom: -3px;
  width: 0; height: 2px; border-radius: 99px; background: var(--accent);
  transform: translateX(-50%);
  transition: width 0.34s var(--ease-spring);
}
.nav-links a:hover::after, .nav-links a.active::after { width: 72%; }

/* the arrows in the gallery frame get a rounder, springier feel */
.gv-nav:hover { transform: translateY(-50%) scale(1.14); }
.gv-nav:active { transform: translateY(-50%) scale(0.94); }

/* softer, rounder corners throughout */
.card, .tribute-card, .help-card, .stat-card, .parent-card, .admin-tribute {
  border-radius: 22px;
}
.btn, .virtue-btn, .tab, .help-toggle, .filter-chip, .mode-chip { border-radius: 99px; }

/* text reveals use the soft curve - no overshoot on anything being read */
.in-view [data-reveal="fade-up"], [data-reveal="fade-up"].in-view,
.in-view [data-reveal="blur-in"], [data-reveal="blur-in"].in-view,
.in-view [data-reveal="word"] .tx-w, [data-reveal="word"].in-view .tx-w {
  animation-timing-function: var(--ease-soft);
}
/* small decorative labels can afford the spring */
.in-view [data-reveal="char"] .tx-c, [data-reveal="char"].in-view .tx-c {
  animation-timing-function: var(--ease-spring);
}

/* section reveal, eased more gently */
.observe, .observe-card {
  transition: opacity 0.75s var(--ease-soft), transform 0.75s var(--ease-soft);
}

@media (prefers-reduced-motion: reduce) {
  .btn, .virtue-btn, .tab, .help-toggle, .filter-chip, .mode-chip,
  .nav-exit-btn, .gv-nav, .gallery-card, .expand-pub-table-btn,
  .tribute-card, .help-card, .stat-card {
    transform: none !important; animation: none !important;
  }
  .nav-links a::after { transition: none; }
}

/* ===================================================================
   THE CLOSED SCREEN — the lamp burns on after the page is done
   =================================================================== */
.closed-screen {
  display: grid; place-items: center; align-content: center; gap: 2px;
  min-height: 100vh; padding: 40px; text-align: center;
  font-family: var(--font-display), Georgia, serif;
  background: radial-gradient(ellipse 60% 50% at 50% 44%, oklch(0.28 0.075 58) 0%, oklch(0.14 0.04 48) 50%, oklch(0.08 0.022 44) 100%);
  color: oklch(0.88 0.11 86);
}
.closed-screen h1 { margin: 18px 0 0; font-weight: 600; font-size: clamp(23px, 3.4vw, 34px); }
.closed-names { margin: 6px 0 0; font-size: clamp(17px, 2.2vw, 21px); color: oklch(0.84 0.09 84 / 0.9); }
.closed-note  { margin: 14px 0 0; font-family: var(--font-body), system-ui, sans-serif;
                font-size: 15.5px; letter-spacing: 0.04em; color: oklch(0.72 0.05 78 / 0.8); }
/* the flame on this screen starts already lit and simply keeps burning */
.closed-diya .flame { animation: flameSway 7.3s ease-in-out infinite, flameBreathe 3.9s ease-in-out infinite; opacity: 1; }
.closed-diya .diya-lamp { animation: none; opacity: 1; }
.closed-diya .diya-wick { animation: none; opacity: 1; }
.closed-diya .d-rim, .closed-diya .d-petals path { animation: none; opacity: 1; }
.closed-diya .d-oil { animation: oilShimmer 6.3s ease-in-out infinite; opacity: 0.85; }
.closed-diya .d-beads circle { animation: none; opacity: 1; }
.closed-diya .diya-glow { animation: glowBreathe 5.7s ease-in-out infinite; opacity: 1; }
.closed-diya .diya-halo { animation: haloBreathe 9.1s ease-in-out infinite; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .closed-diya .flame, .closed-diya .diya-glow, .closed-diya .diya-halo, .closed-diya .d-oil {
    animation: none !important; opacity: 1 !important;
  }
}

/* ===================================================================
   UNIFIED SECTION HEADINGS
   The thirteen titles across the site now share one face, one size, one
   style, one weight and one colour. Previously they were h1 / h2 / h3 at
   111px / 73px / 49px in a light orange, so the same class rendered at
   three different scales depending on which tag it happened to sit on.

   "Sharp" is handled deliberately: grayscale smoothing, optimised
   legibility, balanced wrapping so multi-line titles break evenly, and
   -- most importantly -- no residual filter or fractional transform left
   behind by the reveal animation, which is what makes large type on a
   composited layer look soft.
   =================================================================== */
h1.heading-accent,
h2.heading-accent,
h3.heading-accent,
.sacred-content h1.heading-accent,
.sacred-content h2.heading-accent,
.section-heading h2.heading-accent,
.section-heading h3.heading-accent {
  /* one face, one style, one weight */
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 700;                 /* bold */
  font-variant-ligatures: common-ligatures;
  text-align: center;

  /* one size, everywhere */
  font-size: clamp(32px, 4vw, 56px);
  line-height: 1.16;
  letter-spacing: -0.012em;
  margin: 0 0 14px;

  /* darker */
  color: #43200a;
  color: var(--heading-strong);

  /* sharp */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  text-wrap: balance;
  text-shadow: none;
}

/* Long titles should not stretch the full page width - balanced wrapping
   only reads as deliberate when the measure is controlled. */
.section-heading h2.heading-accent,
.section-heading h3.heading-accent,
.sacred-content h1.heading-accent,
.sacred-content h2.heading-accent {
  max-width: 20ch;
  margin-left: auto;
  margin-right: auto;
}
@media (min-width: 760px) {
  .section-heading h2.heading-accent,
  .section-heading h3.heading-accent,
  .sacred-content h1.heading-accent,
  .sacred-content h2.heading-accent { max-width: 26ch; }
}

/* The reveal animations must leave nothing behind. A stranded blur
   filter or a sub-pixel translate keeps the element on a composited
   layer and softens the glyph edges - the usual cause of "fuzzy"
   headings after a fade-in. */
.heading-accent[data-reveal],
.heading-accent[data-reveal] .tx-w {
  will-change: auto;
}
.in-view .heading-accent[data-reveal],
.heading-accent[data-reveal].in-view,
.in-view .heading-accent[data-reveal] .tx-w,
.heading-accent[data-reveal].in-view .tx-w {
  animation-fill-mode: both;
  filter: none;
  transform: translateZ(0) translateY(0);
}

/* The split-title override sized the sacred heading separately - fold it
   into the shared rule so nothing escapes. */
.sacred-content h1, .sacred-content h2 { font-size: clamp(32px, 4vw, 56px); }
.split-title { white-space: normal; }

/* Underline keeps its proportions against the new size */
.animated-heading h2.heading-accent::after,
.animated-heading h3.heading-accent::after,
.sacred-content h1.heading-accent::after,
.sacred-content h2.heading-accent::after { margin-top: 12px; height: 3px; }

/* ===================================================================
   HEADING FIT — the three that need a specific shape
   The shared rule above gives every title one face, one weight and one
   size. Three of them additionally need a controlled number of lines,
   which is a width problem rather than a size problem: the size stays
   put and the measure is set so the text falls where it should.
   =================================================================== */

/* -- a. centred, and given room to sit on two comfortable lines -- */
#foundation .section-heading h2.heading-accent,
#academic .section-heading h3.heading-accent {
  text-align: center;
  margin-left: auto;
  margin-right: auto;
  max-width: 22ch;
  text-wrap: balance;
}

/* -- b. "Thank You for Honouring..." on a single line --
   Forty-nine characters cannot hold one line at 54px on a phone, so the
   size is allowed to track the viewport until it reaches the shared
   maximum. On any screen from a large phone upward it renders at the
   same size as every other heading; below that it scales rather than
   wrapping, because you asked for one line. */
#thanks h2.heading-accent {
  white-space: nowrap;
  max-width: none;
  font-size: min(clamp(32px, 4vw, 56px), 3.46vw);
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}
/* Below this width one line would drop under 17px, which is too small to
   read with dignity - so it is allowed to wrap on small phones only. */
@media (max-width: 430px) {
  #thanks h2.heading-accent {
    white-space: normal;
    font-size: clamp(23px, 6.4vw, 32px);
    text-wrap: balance;
    max-width: 20ch;
  }
}

/* -- c. "Go Forth on Paths..." on exactly two lines --
   The break is placed at the comma in the markup, which is the only
   reliable way to guarantee two lines at every width. The measure is
   widened so neither half wraps again on desktop. */
#exit h2.heading-accent {
  /* Matched to #thanks, the section before it: identical font-size
     formula so the two closing headings read at the same scale. It was
     capped at 1.75vw only because nowrap forced its long second line
     onto one line; allowing it to wrap is what lets it come up to size. */
  max-width: 30ch;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
  text-wrap: balance;
  font-size: min(clamp(32px, 4vw, 56px), 3.46vw);
  line-height: 1.28;
}
.line-break { display: inline; }
/* On narrow screens each half is allowed to wrap rather than overflow */
@media (max-width: 900px) {
  #exit h2.heading-accent {
    text-wrap: balance;
    font-size: clamp(23px, 4.4vw, 36px);
    max-width: 26ch;
  }
}

/* -- d. the opening screen title on a single line -- */
.threshold-title {
  white-space: nowrap;
  max-width: none;
  font-size: min(clamp(32px, 4vw, 56px), 6.8vw);
}

/* ===================================================================
   THE DEDICATION — plain and fully legible
   No gradient fill, no clipping, no italic. The gradient wipe paints
   text through background-clip, which means the glyphs are transparent
   until the animation finishes filling them - and on the opening screen
   that animation never completed, leaving the sentence invisible.
   This rule makes that impossible for this paragraph regardless of what
   any reveal effect tries to do to it.
   =================================================================== */
.dedication-plain {
  /* Typography is inherited from .lede + .lede-italic so this paragraph
     matches the Foundation sub-heading exactly. Nothing about the face,
     size, style or colour is set here - only the guarantees that keep it
     readable no matter what a reveal effect tries to do. */
  opacity: 1;

  /* undo every part of the clipped-gradient treatment */
  background: none !important;
  background-image: none !important;
  -webkit-background-clip: border-box !important;
  background-clip: border-box !important;
  -webkit-text-fill-color: currentColor !important;
  text-fill-color: currentColor !important;

  /* Full width, centred by text-align. A max-width here would put the
     text back inside a box narrower than itself once the lines are held
     with nowrap - the same trap that pushed the opening screen off
     centre. */
  display: block;
  width: 100%;
  max-width: none;
  margin-left: 0;
  margin-right: 0;
  text-align: center;
}

/* Safety net: should the wipe ever be applied without its animation
   running to completion, the text still ends up readable rather than
   sitting at 16% opacity. */
@supports (animation-timeline: view()) {
  [data-reveal="wipe"]:not(.in-view):not([data-scroll-linked]) {
    background-size: 100% 100%;
  }
}

/* ===================================================================
   "The Foundation Upon Which a Legacy Was Built" on one line
   Forty-three characters. Same approach as the Thank You heading: the
   size holds at the shared maximum on any reasonable screen and only
   tracks the viewport when one line would otherwise overflow.
   =================================================================== */
#foundation .section-heading h2.heading-accent {
  white-space: nowrap;
  max-width: none;
  font-size: min(clamp(32px, 4vw, 56px), 3.93vw);
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}
/* Below this, one line would fall under about 18px - too small to read
   comfortably - so it is allowed to wrap on small phones only. */
@media (max-width: 430px) {
  #foundation .section-heading h2.heading-accent {
    white-space: normal;
    font-size: clamp(24px, 6.6vw, 32px);
    text-wrap: balance;
    max-width: 20ch;
  }
}

/* ===================================================================
   SINGLE-LINE SUB-HEADINGS
   Each of these must hold one line. The shared 54px maximum stays in
   force; the size only tracks the viewport once one line would
   otherwise overflow, and the vw figure is set from the character
   count so each falls back by exactly as much as it needs and no more.
     35 chars -> 5.7vw      44 chars -> 4.5vw      49 chars -> 4.1vw
   Below 430px a single line would drop under about 18px, which is too
   small to read with dignity, so there they are allowed to wrap.
   =================================================================== */
#timeline .section-heading h3.heading-accent,
#virtues .section-heading h3.heading-accent,
#academic .section-heading h3.heading-accent {
  white-space: nowrap;
  max-width: none;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}
#timeline .section-heading h3.heading-accent { font-size: min(clamp(32px, 4vw, 56px), 4.95vw); }
#virtues .section-heading h3.heading-accent { font-size: min(clamp(32px, 4vw, 56px), 3.93vw); }
#academic .section-heading h3.heading-accent { font-size: min(clamp(32px, 4vw, 56px), 3.53vw); }

@media (max-width: 430px) {
  #timeline .section-heading h3.heading-accent,
  #virtues .section-heading h3.heading-accent,
  #academic .section-heading h3.heading-accent {
    white-space: normal;
    text-wrap: balance;
    max-width: 20ch;
    font-size: clamp(23px, 6.4vw, 32px);
  }
}


/* ===================================================================
   OPENING, FAREWELL AND CLOSED SCREEN — centred, and only centred
   Every child is a full-width block whose text is centred. Nothing here
   relies on shrink-to-fit or grid item alignment, so no element can be
   pushed off centre by content wider than its box.
   =================================================================== */
.threshold, .farewell {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.threshold-inner, .farewell-inner {
  display: block;
  width: 100%;
  max-width: none;
  text-align: center;
}
.threshold-line, .threshold-title, .threshold-names,
.farewell-line, .farewell-names, .farewell-blessing,
.closed-screen h1, .closed-names, .closed-note {
  display: block;
  width: 100%;
  margin-left: 0;
  margin-right: 0;
  text-align: center;
  text-align-last: center;
}
.threshold-actions, .farewell-actions {
  display: flex;
  width: 100%;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
}
.closed-screen { display: block; text-align: center; padding: 40px 16px; }
.closed-diya { margin: 0 auto; }

/* the farewell names share the opening's face */
.closed-names {
  font-family: 'High Tower Text', 'Cormorant Garamond', 'Book Antiqua',
               'Palatino Linotype', Palatino, Georgia, serif;
  font-style: italic;
  font-size: clamp(26px, 3.8vw, 50px);
}
.closed-screen h1 {
  font-family: 'High Tower Text', 'Cormorant Garamond', 'Book Antiqua',
               'Palatino Linotype', Palatino, Georgia, serif;
  font-size: clamp(17px, 2vw, 27px);
  order: 2;
}

/* ===================================================================
   TWO SUB-HEADINGS HELD TO A FIXED NUMBER OF LINES
   Both are centred full-width blocks, so nothing can drift off centre.
   The vw figure in each is derived from its own character count, so each
   falls back by exactly as much as it needs and no more. Below its own
   breakpoint each is allowed to wrap rather than shrink past reading
   size - a line nobody can read is worse than a line that wraps.
   =================================================================== */

/* -- the dedication, on exactly two lines (131 + 108 characters).
      The break sits at the comma after "the quest of research". -- */
#sacred .lede.dedication-plain {
  display: block;
  width: 100%;
  max-width: none;
  /* was nowrap: at the larger size the 131-character line would run off
     the page, so it now wraps. The <br> still holds the intended break
     after "the quest of research". */
  white-space: normal;
  text-wrap: balance;
  text-align: center;
  text-align-last: center;
  font-size: min(clamp(23px, 2.4vw, 27px), 1.73vw);
  line-height: 1.7;
}
@media (max-width: 860px) {
  #sacred .lede.dedication-plain {
    white-space: normal;
    font-size: clamp(18px, 2.8vw, 22px);
    text-wrap: pretty;
    max-width: 44ch;
    margin-left: auto;
    margin-right: auto;
  }
}

/* -- the legacy line, on one line (110 characters) -- */
#legacy .section-heading p.lede-italic,
#legacy p.lede.lede-italic {
  display: block;
  width: 100%;
  max-width: none;
  /* was nowrap - see the note on the dedication above */
  white-space: normal;
  text-wrap: balance;
  text-align: center;
  text-align-last: center;
  font-size: min(clamp(23px, 2.4vw, 27px), 2.05vw);
  margin-left: 0;
  margin-right: 0;
}
@media (max-width: 780px) {
  #legacy .section-heading p.lede-italic,
  #legacy p.lede.lede-italic {
    white-space: normal;
    font-size: clamp(18px, 3vw, 22px);
    text-wrap: balance;
    max-width: 38ch;
    margin-left: auto;
    margin-right: auto;
  }
}

/* ===================================================================
   NAVIGATION BAR — everything centred
   The bar used space-between, which pinned the brand to the far left
   and the links to the far right. Both groups are now centred together,
   and the text inside each is centred too: the brand's two lines sit
   centred over one another rather than ranged left, and every link and
   button centres its own label.
   =================================================================== */
.topbar {
  justify-content: center;
  flex-wrap: wrap;
  text-align: center;
  position: sticky;              /* unchanged - restated so the rule reads whole */
}

/* --- the brand: two lines, centred over each other --- */
.brand {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.brand-text {
  display: grid;
  justify-items: center;
  text-align: center;
}
.brand strong, .brand em { display: block; text-align: center; }

/* --- the links --- */
.nav-links {
  justify-content: center;
  flex-wrap: wrap;
  text-align: center;
}
.nav-links a,
.nav-links .nav-exit-btn,
.nav-links .admin-link {
  text-align: center;
  justify-content: center;
}

/* --- the mobile drawer, and the toggle that opens it --- */
@media (max-width: 980px) {
  /* the brand centres in the bar; the toggle keeps the right edge, which
     is where a thumb expects to find it */
  .topbar { position: sticky; justify-content: center; }
  .menu-toggle {
    position: absolute;
    right: clamp(12px, 4vw, 40px);
    top: 50%;
    transform: translateY(-50%);
  }
  .nav-links {
    align-items: center;
    text-align: center;
  }
  .nav-links a,
  .nav-links .nav-exit-btn {
    width: 100%;
    text-align: center;
    justify-content: center;
  }
}

/* ===================================================================
   NAVIGATION BAR — no border
   The ornamental frame has been removed. Padding is its own value again
   rather than being derived from ornament heights, and with nothing to
   clear at the sides the links have appreciably more width than before.
   Centring, the sticky behaviour and the gold shine are unchanged.
   =================================================================== */
.topbar {
  position: sticky;
  padding-top: 12px;
  padding-bottom: 12px;
  padding-left: clamp(16px, 3vw, 48px);
  padding-right: clamp(16px, 3vw, 48px);
  overflow: visible;
}
@media (max-width: 980px) {
  .menu-toggle { right: clamp(12px, 4vw, 40px); }
}

/* ===================================================================
   HELP — "Guidance" as its own sub-section
   It was a trailing note inside the About & Contact body. Now a real
   sub-section with its own heading, separated by a gold hairline so it
   reads as a distinct block rather than an afterthought.
   =================================================================== */
.help-subsection {
  margin-top: 16px;
  padding-top: 14px;
  /* the gradient bar below is the separator now, so the old pale
     hairline would only tint through it */
  border-top: 0;
  position: relative;
}
/* The same bar that tops every tile on the site - deep heading brown
   through gold into teal - rather than the short gold-only stub this
   used to be. Identical gradient, height and opacity to the tile edge,
   so the two read as one language. */
.help-subsection::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  right: 0;
  width: auto;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
  opacity: 0.9;
  pointer-events: none;
}
.help-subheading {
  margin: 0 0 6px;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--heading);
}
.help-subsection p {
  margin: 0;
  font-size: 15px;
  line-height: 1.7;
  color: var(--text);
}

/* ===================================================================
   PUBLICATIONS, CITATIONS AND THE TIME CAPSULE
   Styled to the gold theme already in use. Nothing here introduces a
   new colour; everything draws on the existing tokens.
   =================================================================== */

/* --- the Cite control on each publication row --- */
.pub-cite-cell { white-space: nowrap; }
.pub-cite {
  font: inherit; font-size: 12.5px; font-weight: 700;
  padding: 5px 13px; border-radius: 99px; cursor: pointer;
  border: 1px solid var(--line); background: oklch(1 0 0 / 0.6); color: var(--heading);
  transition: transform 0.28s var(--ease-spring), background 0.24s, color 0.24s, border-color 0.24s;
}
.pub-cite:hover {
  background: linear-gradient(135deg, var(--heading), var(--copper));
  color: #fff; border-color: transparent; transform: translateY(-2px) scale(1.05);
}
.pub-cite:active { transform: translateY(0) scale(0.96); }
.pub-title { min-width: 220px; }
.pub-doi {
  display: inline-block; margin-top: 3px; font-size: 12px; font-weight: 700;
  color: var(--accent-strong); text-decoration: none; letter-spacing: 0.02em;
}
.pub-doi:hover { text-decoration: underline; }
.pub-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.pub-export { font-size: 13px; padding: 9px 18px; }

/* --- the citation panel --- */
.cite-modal {
  max-width: 620px; width: min(620px, 92vw); padding: 30px 32px 26px;
  border: 0; border-radius: var(--radius); background: var(--cream);
  box-shadow: 0 40px 120px rgba(0,0,0,0.35);
}
.cite-modal::backdrop { background: oklch(0.2 0.04 50 / 0.55); backdrop-filter: blur(4px); }
.cite-kicker {
  margin: 0 0 4px; font-size: 12px; font-weight: 900; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--copper);
}
.cite-modal h3 {
  margin: 0 0 4px; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(19px, 2.4vw, 25px); line-height: 1.25; color: var(--heading-strong);
  text-align: left;
}
.cite-meta { margin: 0 0 16px; font-size: 13.5px; color: var(--muted); }
.cite-tabs { display: flex; gap: 7px; margin-bottom: 12px; flex-wrap: wrap; }
.cite-tab {
  font: inherit; font-size: 13px; font-weight: 700; padding: 7px 17px;
  border-radius: 99px; cursor: pointer; border: 1px solid var(--line);
  background: oklch(1 0 0 / 0.6); color: var(--heading);
  transition: transform 0.28s var(--ease-spring), background 0.24s, color 0.24s;
}
.cite-tab:hover { transform: translateY(-2px); }
.cite-tab.active {
  background: linear-gradient(135deg, var(--heading), var(--copper));
  color: #fff; border-color: transparent;
}
#citeText {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px; line-height: 1.6; padding: 14px 15px; resize: vertical;
  border: 1px solid var(--line); border-radius: 14px;
  background: oklch(1 0 0 / 0.72); color: var(--text);
}
#citeText:focus { outline: 3px solid var(--accent); outline-offset: 2px; }
.cite-actions { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
.cite-note { margin: 12px 0 0; font-size: 12.5px; color: var(--muted); }

/* --- the time capsule --- */
.capsule-grid {
  display: grid; gap: 18px; max-width: 1000px; margin: 0 auto;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.capsule-empty { text-align: center; color: var(--muted); font-style: italic; grid-column: 1 / -1; }
.capsule-card {
  position: relative; padding: 22px 24px; border-radius: 22px;
  background: var(--panel); border: 1px solid var(--line);
  box-shadow: var(--soft); overflow: hidden;
  transition: transform 0.34s var(--ease-spring), box-shadow 0.34s var(--ease-soft);
}
.capsule-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.capsule-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
}
.capsule-head { display: flex; gap: 13px; align-items: flex-start; }
.capsule-head h4 { margin: 0 0 2px; font-size: 18px; color: var(--heading-strong); }
.capsule-for { margin: 0; font-size: 13px; font-style: italic; color: var(--muted); }
.capsule-seal {
  flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(135deg, var(--heading), var(--copper));
  box-shadow: 0 6px 16px oklch(0.5 0.13 55 / 0.35);
}
.capsule-seal svg { width: 20px; height: 20px; }
.capsule-card.is-open .capsule-seal {
  background: linear-gradient(135deg, var(--gold), #f6e3ac); color: var(--heading-strong);
}

/* sealed: a quiet, waiting state */
.capsule-locked {
  margin-top: 16px; padding: 16px; border-radius: 14px; text-align: center;
  background: repeating-linear-gradient(45deg,
    oklch(0.46 0.09 55 / 0.05) 0 8px, transparent 8px 16px);
  border: 1px dashed var(--line);
}
.capsule-opens { margin: 0 0 5px; font-size: 14px; color: var(--muted); }
.capsule-opens strong { color: var(--heading); }
.capsule-count {
  margin: 0; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(19px, 2.4vw, 26px); color: var(--heading-strong);
  font-variant-numeric: tabular-nums;
}
/* opened: the letter itself */
.capsule-card.is-open { background: var(--panel2); }
.capsule-body { margin-top: 15px; }
.capsule-body p {
  margin: 0 0 10px; font-family: var(--font-display); font-size: 17px;
  line-height: 1.75; color: var(--text);
}
.capsule-meta { margin: 14px 0 0; font-size: 12.5px; color: var(--muted); letter-spacing: 0.03em; }
.capsule-note {
  max-width: 620px; margin: 26px auto 0; text-align: center;
  font-size: 13.5px; line-height: 1.7; color: var(--muted);
}

@media (max-width: 620px) {
  .cite-modal { padding: 24px 20px 20px; }
  .capsule-card { padding: 18px 18px; }
}
@media (prefers-reduced-motion: reduce) {
  .capsule-card, .pub-cite, .cite-tab { transition: none !important; transform: none !important; }
}
@media print { .pub-cite-cell, .pub-actions, .cite-modal { display: none !important; } }

/* --- the seal control on the tribute form --- */
.seal-field {
  margin-top: 16px; padding: 16px 18px; border-radius: 16px;
  background: oklch(1 0 0 / 0.5); border: 1px dashed var(--line);
}
.seal-label { display: flex; gap: 12px; align-items: flex-start; cursor: pointer; }
.seal-label strong { display: block; font-size: 15px; color: var(--heading); }
.seal-label em { display: block; font-size: 13px; font-style: normal; color: var(--muted); margin-top: 2px; }
.seal-icon {
  flex: 0 0 34px; width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(135deg, var(--heading), var(--copper));
}
.seal-icon svg { width: 17px; height: 17px; }
.seal-input {
  margin-top: 12px; width: 100%; max-width: 260px;
  padding: 11px 14px; font: inherit; font-size: 15px;
  border: 1px solid var(--line); border-radius: 12px;
  background: oklch(1 0 0 / 0.85); color: var(--text);
}
.seal-input:focus { outline: 3px solid var(--accent); outline-offset: 2px; border-color: var(--accent); }
.seal-help { margin: 10px 0 0; font-size: 13px; line-height: 1.65; color: var(--muted); }
.capsule-quiet { font-style: italic; color: var(--muted); }
.capsule-card .capsule-from-visitor { font-size: 12px; color: var(--copper); }


/* ===================================================================
   NAMES — centred, one line, High Tower Text (start and exit screens)
   The earlier centring block zeroes the side margins while the names
   carry a 30ch cap, which parked the box at the left edge and wrapped
   it. This final rule releases the cap on wide screens and restores the
   auto margins, so the pair of names sits centred as one line.
   =================================================================== */
.threshold-names, .farewell-names {
  font-family: 'High Tower Text', 'Cormorant Garamond', 'Book Antiqua',
               'Palatino Linotype', Palatino, Georgia, serif;
  font-style: normal;
  font-weight: 700;
  width: auto;
  max-width: none;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
  text-wrap: nowrap;
}
/* phones: back to one name per line, still centred */
@media (max-width: 900px) {
  .threshold-names, .farewell-names {
    text-wrap: balance;
    max-width: 16ch;
  }
}

/* ===================================================================
   START SCREEN — mandala artwork
   Two great blooms cropped into opposite corners as in the design, and a
   soft one behind the lamp. Each is a pair of the site's own symbols
   layered into a denser rosette, in teal-and-gold enamel, each carrying
   the celestial gold glow and turning almost imperceptibly.
   =================================================================== */
.threshold-art { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
.threshold-inner::before {
  /* Gold on a gold background has almost no contrast. This soft warm-dark
     scrim gives every line something to sit against, on the marigold half
     and the plum half alike. */
  content: ''; position: absolute; inset: -6% -5%;
  background: radial-gradient(ellipse at 50% 50%,
    rgba(12, 5, 16, 0.42) 0%, rgba(12, 5, 16, 0.28) 46%, transparent 74%);
  filter: blur(26px); z-index: -1; pointer-events: none;
}
.threshold-inner {
  position: relative; z-index: 1;
  width: 100%; max-width: 100%; box-sizing: border-box;
  margin-left: auto; margin-right: auto;
  text-align: center;
}

.t-mandala { position: absolute; display: block; opacity: 0;
  animation: tmIn 1.6s var(--ease-soft) 0.2s both; }
.t-mandala .tm-art {
  position: absolute; inset: 15%;          /* Padma, set within Chakra */
  display: block; overflow: visible;
  /* Padma sits deep copper. Darker than Chakra on purpose - that tonal
     gap is what separates the two rings; a stronger halo keeps the
     darker line from disappearing into the ground. */
  color: #b8761f;
  filter: drop-shadow(0 0 7px rgba(184, 118, 31, 0.9))
          drop-shadow(0 0 16px rgba(255, 190, 90, 0.35));
  animation: mandalaTurn 74s linear infinite;
}
@keyframes tmIn { to { opacity: 1; } }

.tm--tr { top: 0; right: 0; width: clamp(215px, 30vw, 500px); height: clamp(215px, 30vw, 500px);
  transform: translate(34%, -34%); }
.tm--bl .tm-art { animation-direction: reverse; }

/* A second, larger mandala behind each one, turning the other way. Sized
   with `inset`, never `transform`: the rotation animation also sets
   transform and would overwrite a scale - the very bug that stacked the
   last pair on top of one another. */
.tm-under {
  inset: -12% !important;                /* Chakra, the outer wheel */
  width: auto !important; height: auto !important;
  color: #e8c98a;                    /* pale gold - the quiet outer wheel */
  opacity: 0.36;
  animation-duration: 108s !important;
  animation-direction: reverse !important;
}
.tm--bl .tm-under { animation-direction: normal !important; color: #e8c98a; }
.tm--bl { bottom: 0; left: 0; width: clamp(215px, 30vw, 500px); height: clamp(215px, 30vw, 500px);
  transform: translate(-34%, 34%); }
.t-mandala .mandala-glow { z-index: -1; }

/* the hanging sparkle, top left - drawn rays, not a gradient trick */
.t-spark {
  position: absolute; top: 6%; left: 7%;
  width: clamp(58px, 6vw, 104px); height: clamp(58px, 6vw, 104px);
  overflow: visible;
  filter: drop-shadow(0 0 14px rgba(255, 240, 190, 0.85));
  opacity: 0;
  animation: tmIn 1.6s var(--ease-soft) 0.3s both,
             sparkTwinkle 6.5s ease-in-out 3.2s infinite;
}
@keyframes sparkTwinkle {
  0%, 100% { opacity: .95; transform: scale(1) rotate(0deg); }
  50%      { opacity: .55; transform: scale(.86) rotate(18deg); }
}
/* the pearl, bottom right - glows like the sparkle, on its own slower beat */
.t-gem {
  position: absolute; right: 5%; bottom: 7%;
  width: clamp(40px, 4.2vw, 76px); height: clamp(40px, 4.2vw, 76px);
  overflow: visible;
  filter: drop-shadow(0 0 16px rgba(240, 226, 255, 0.8));
  opacity: 0;
  animation: tmIn 1.6s var(--ease-soft) 0.45s both,
             pearlGlow 8.2s ease-in-out 3.6s infinite;
}
@keyframes pearlGlow {
  0%, 100% { opacity: .95; transform: scale(1) rotate(0deg); }
  50%      { opacity: .58; transform: scale(.9) rotate(-14deg); }
}

@media (max-width: 700px) {
  .tm--tr, .tm--bl { width: clamp(165px, 44vw, 235px); height: clamp(165px, 44vw, 235px); }
  .t-spark { width: 48px; height: 48px; }
  .t-gem { width: 28px; height: 28px; }
}
@media (prefers-reduced-motion: reduce) {
  .t-mandala, .t-mandala .tm-art, .t-spark, .t-gem { animation: none !important; opacity: 1; }
}


/* ===================================================================
   EXIT SCREEN — the names sit smaller than on the opening screen
   The farewell is a quieter moment; the names read as a signature
   rather than a title. This must come after the shared NAMES rule.
   =================================================================== */
.farewell-names {
  font-size: min(clamp(20px, 1.05vw, 24px), 1.05vw) !important;
  letter-spacing: 0.05em;
}
@media (max-width: 900px) {
  .farewell-names { font-size: clamp(17px, 4.4vw, 22px) !important; }
}

/* ===================================================================
   THE HEADING, REVEALED WORD BY WORD
   Each word is dealt one of six entrances at random, with a random
   offset, so the line assembles itself differently on every visit.
   The whole sequence begins only after the lamp is fully lit.
   =================================================================== */
.rw {
  display: inline-block;
  opacity: 0;
  will-change: transform, opacity, filter;
  animation-duration: 1.15s;
  animation-timing-function: var(--ease-soft);
  animation-fill-mode: both;
}
.rw--1 { animation-name: rwRise; }
.rw--2 { animation-name: rwBlur; }
.rw--3 { animation-name: rwScale; }
.rw--4 { animation-name: rwLeft; }
.rw--5 { animation-name: rwRight; }
.rw--6 { animation-name: rwTurn; }

@keyframes rwRise  { from { opacity: 0; transform: translateY(26px); }
                     to   { opacity: 1; transform: none; } }
@keyframes rwBlur  { from { opacity: 0; filter: blur(11px); transform: translateY(8px); }
                     to   { opacity: 1; filter: blur(0);     transform: none; } }
@keyframes rwScale { from { opacity: 0; transform: scale(0.62); }
                     to   { opacity: 1; transform: none; } }
@keyframes rwLeft  { from { opacity: 0; transform: translateX(-34px); }
                     to   { opacity: 1; transform: none; } }
@keyframes rwRight { from { opacity: 0; transform: translateX(34px); }
                     to   { opacity: 1; transform: none; } }
@keyframes rwTurn  { from { opacity: 0; transform: rotate(-7deg) translateY(20px) scale(0.86); }
                     to   { opacity: 1; transform: none; } }

/* Motion off: the words simply are there, at the same moment. */
@media (prefers-reduced-motion: reduce) {
  .rw { animation: none !important; opacity: 1 !important; filter: none !important; transform: none !important; }
}

/* ===================================================================
   THE THREE VOICES OF THE OPENING AND CLOSING SCREENS
   Dedication in EB Garamond, names in Cinzel, title in Jost - three
   distinct typefaces, with the dedication and the title set at the
   same size. That size is governed by the dedication, which is a long
   no-wrap line; the title is short and sits comfortably inside it.
   =================================================================== */
:root {
  --font-dedication: 'EB Garamond', 'Cormorant Garamond', Georgia, serif;
  --font-names:      'Cinzel', 'Trajan Pro', Georgia, serif;
  --font-hero:       'Playfair Display', 'Georgia', 'Times New Roman', serif;
  --size-screen-head: min(clamp(30px, 2.9vw, 62px), 2.48vw);
}
.threshold-line, .farewell-line {
  font-family: var(--font-dedication) !important;
  font-size: var(--size-screen-head) !important;
}
.threshold-names, .farewell-names {
  font-family: var(--font-names) !important;
  font-weight: 500 !important;
  font-style: normal !important;
  letter-spacing: 0.06em;
}
.threshold-title, .farewell-blessing {
  font-family: var(--font-hero) !important;
  font-weight: 900 !important;
  font-size: var(--size-screen-head) !important;
  letter-spacing: -0.004em;
}
/* the exit screen keeps its smaller names */
.farewell-names { font-size: min(clamp(20px, 1.05vw, 24px), 1.05vw) !important; }
@media (max-width: 900px) {
  :root { --size-screen-head: clamp(26px, 5.2vw, 40px); }
  .farewell-names { font-size: clamp(15px, 3.6vw, 19px) !important; }
}

/* ===================================================================
   SECTION HEADINGS IN JOST
   The fourteen .heading-accent headings now share the typeface of the
   opening screen's title, so "The Didolkar Family Legacy" reads the
   same wherever it appears. Jost is a geometric sans: at the old 700
   it turns blocky at display sizes, so these sit at 500 - the same
   weight the hero uses - with a touch of tracking to open it up.
   =================================================================== */
.heading-accent,
h1.heading-accent, h2.heading-accent, h3.heading-accent,
.split-title,
.sacred-content h1, .sacred-content h2,
.section-heading h2, .section-heading h3 {
  font-family: var(--font-hero) !important;
  font-weight: 900 !important;
  /* Playfair Display Black has heavy vertical stems and hairline joins.
     A whisker of negative tracking stops the letters looking loose. */
  letter-spacing: -0.006em;
}


/* ===================================================================
   PHOTOGRAPH FRAMES — notched corner brackets
   No full border: four L-shaped brackets with a notched step, in
   celestial gold, glowing gently. One rule serves the gallery, the
   timeline, the foundation photo and anything added later, so every
   photograph on the site is held the same way.

   The brackets are drawn with conic-gradient corners rather than
   borders, because a bordered pseudo-element cannot carry the notch
   step without a second element per corner.
   =================================================================== */
:root {
  --br-gold:      #e8c162;
  --br-gold-lo:   #b8801f;
  --br-gold-hi:   #fff0b8;
  --br-len:       clamp(26px, 4.2vw, 54px);   /* arm length      */
  --br-thick:     3px;                        /* arm thickness   */
  --br-notch:     0.42;                       /* step, as a fraction of the arm */
  --br-gap:       clamp(6px, 1vw, 12px);      /* stand-off from the photo */
}

.photo-brackets { position: relative; }
.photo-brackets > .br {
  position: absolute;
  width: var(--br-len);
  height: var(--br-len);
  pointer-events: none;
  z-index: 2;
  color: var(--br-gold);
  filter: drop-shadow(0 0 6px rgba(232, 193, 98, 0.75))
          drop-shadow(0 0 15px rgba(255, 199, 92, 0.38));
  animation: brGlow 6.5s ease-in-out infinite;
}
.photo-brackets > .br svg { display: block; width: 100%; height: 100%; overflow: visible; }

.br--tl { top: calc(var(--br-gap) * -1); left:  calc(var(--br-gap) * -1); }
.br--tr { top: calc(var(--br-gap) * -1); right: calc(var(--br-gap) * -1); transform: scaleX(-1); }
.br--bl { bottom: calc(var(--br-gap) * -1); left:  calc(var(--br-gap) * -1); transform: scaleY(-1); }
.br--br { bottom: calc(var(--br-gap) * -1); right: calc(var(--br-gap) * -1); transform: scale(-1, -1); }

/* the celestial breath - the same rhythm the mandalas use */
@keyframes brGlow {
  0%, 100% { filter: drop-shadow(0 0 4px  rgba(232, 193, 98, 0.55))
                     drop-shadow(0 0 11px rgba(255, 199, 92, 0.25)); }
  50%      { filter: drop-shadow(0 0 9px  rgba(255, 214, 130, 0.95))
                     drop-shadow(0 0 22px rgba(255, 199, 92, 0.55)); }
}

/* the photograph itself: square corners, a hairline edge, no heavy frame */
.photo-brackets img,
.photo-brackets .gv-img,
.photo-brackets > picture img {
  border-radius: 2px;
  box-shadow: 0 10px 30px rgba(30, 15, 0, 0.22),
              0 0 0 1px rgba(120, 78, 20, 0.28);
}

/* the wrapper shrink-wraps its photograph, so the brackets sit on the
   picture's own edges rather than on the figure around it */
.photo-shot { position: relative; display: inline-block; line-height: 0; max-width: 100%; }
.photo-shot > img { display: block; max-width: 100%; }
/* the timeline column must be free to grow down the tile */
.tl-photo-cluster { overflow: visible; }
.timeline-item, .timeline-photo { overflow: visible; }

/* the gallery viewer holds the largest brackets */
.gv-frame.photo-brackets { --br-len: clamp(34px, 5vw, 76px); --br-thick: 4px; --br-gap: clamp(8px, 1.2vw, 16px); }
/* timeline snapshots are small - shorter arms, thinner, tucked closer */
.tl-photo-item .photo-shot { --br-len: 22px; --br-thick: 2px; --br-gap: 3px; }
.foundation-photo-wrap, .photo-shot { display: inline-block; }
.photo-shot:has(> .foundation-photo) { display: block; --br-len: clamp(28px, 4vw, 56px); }
/* the temple monument is the largest picture on the site */
.photo-shot:has(> .temple-img-full) {
  display: block; --br-len: clamp(38px, 5.5vw, 84px); --br-thick: 4px; --br-gap: clamp(9px, 1.3vw, 18px);
}
.photo-shot > .temple-img-full { width: 100%; height: auto; }
/* the gallery thumbnails carry the smallest brackets */
.gallery-card .photo-shot { display: block; --br-len: 20px; --br-thick: 2px; --br-gap: 3px; }

@media (prefers-reduced-motion: reduce) {
  .photo-brackets > .br { animation: none !important;
    filter: drop-shadow(0 0 6px rgba(232, 193, 98, 0.7)); }
}

/* ===================================================================
   DARK SAFFRON HEADINGS
   All fourteen section headings take the deep saffron of the marigold
   palette. The opening and closing screens sit on a dark ground, so
   their hero uses a lighter saffron from the same family - the same
   hue, lifted enough to read.
   =================================================================== */
:root { --saffron-deep: #8c1c13; --saffron-lit: #e2624f; }
.heading-accent,
h1.heading-accent, h2.heading-accent, h3.heading-accent,
.split-title,
.sacred-content h1, .sacred-content h2,
.section-heading h2, .section-heading h3 {
  color: var(--saffron-deep) !important;
}
.threshold-title, .farewell-blessing {
  color: var(--saffron-lit) !important;
  text-shadow: 0 0 26px rgba(239, 154, 61, 0.55),
               0 3px 9px rgba(30, 10, 4, 0.9),
               0 1px 3px rgba(20, 6, 2, 0.95) !important;
}

/* ===================================================================
   ONE BUTTON, EVERYWHERE
   Every button and link-button on the site now shares the face, the
   colour and the behaviour of the gallery filter chips: the body font,
   a saffron label on a warm ground, a gold hairline, and the slow
   sweep of light that crosses the chips.
   =================================================================== */
.btn, .btn.primary, .btn.ghost, .filter-chip, .virtue-btn, .tab, .cite-tab,
.help-toggle, .threshold-btn, .nav-exit-btn, .filter-btn, .to-top {
  position: relative;
  overflow: hidden;
  font-family: var(--font-body) !important;
  font-weight: 600;
  letter-spacing: 0.02em;
  /* the label is engraved into the metal: dark, with a light
     edge below it, exactly as on the reference buttons */
  color: #4a2708 !important;
  /* a struck gold bar: bright along the top, deep at the base, with a
     lit rim - the pressed-metal look of the reference buttons. */
  background:
    linear-gradient(180deg,
      #f7dc93 0%, #e0b348 16%, #c8912c 42%,
      #a97418 60%, #c9942f 82%, #e6c168 100%) !important;
  border: 1px solid #7d5410 !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 246, 214, 0.85),
    inset 0 -1px 0 rgba(90, 56, 6, 0.6),
    inset 0 0 0 1px rgba(255, 232, 170, 0.28),
    0 3px 10px rgba(60, 34, 4, 0.30),
    0 0 12px rgba(232, 176, 74, 0.35);
  border-radius: 999px;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease),
              background 0.25s var(--ease), color 0.25s var(--ease);
  text-shadow: 0 1px 0 rgba(255, 240, 196, 0.75);
}
/* the light that crosses each chip */
.btn::after, .filter-chip::after, .virtue-btn::after, .tab::after,
.cite-tab::after, .help-toggle::after, .threshold-btn::after,
.nav-exit-btn::after, .to-top::after {
  content: "";
  position: absolute; inset: -50% auto -50% -30%;
  width: 30%;
  background: linear-gradient(90deg,
    transparent, rgba(255, 248, 214, 0.75), rgba(255, 236, 176, 0.35), transparent);
  transform: translateX(0) rotate(25deg);
  animation: sweep 5.4s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: transform;
  pointer-events: none;
}
/* the celestial gold glow, breathing around every button */
.btn, .filter-chip, .virtue-btn, .tab, .cite-tab, .help-toggle,
.threshold-btn, .nav-exit-btn, .to-top {
  animation: btnGlow 5.6s ease-in-out infinite;
}
@keyframes btnGlow {
  0%, 100% { box-shadow:
      inset 0 1px 0 rgba(255, 246, 214, 0.85),
      inset 0 -1px 0 rgba(90, 56, 6, 0.6),
      inset 0 0 0 1px rgba(255, 232, 170, 0.28),
      0 3px 10px rgba(60, 34, 4, 0.30),
      0 0 10px rgba(232, 176, 74, 0.30); }
  50%      { box-shadow:
      inset 0 1px 0 rgba(255, 250, 226, 0.95),
      inset 0 -1px 0 rgba(90, 56, 6, 0.55),
      inset 0 0 0 1px rgba(255, 240, 190, 0.42),
      0 3px 12px rgba(60, 34, 4, 0.30),
      0 0 24px rgba(255, 202, 96, 0.72); }
}
.btn:hover, .filter-chip:hover, .virtue-btn:hover, .tab:hover, .cite-tab:hover,
.help-toggle:hover, .threshold-btn:hover, .nav-exit-btn:hover, .to-top:hover {
  transform: translateY(-2px);
  filter: brightness(1.08);
  text-shadow: 0 1px 0 rgba(255, 246, 214, 0.9);
}
.btn:active, .filter-chip:active, .virtue-btn:active, .threshold-btn:active,
.to-top:active { transform: translateY(0) scale(0.98); }
.filter-chip.is-active, .tab.is-active, .cite-tab.is-active, .btn.primary {
  background: linear-gradient(180deg,
    #fff0bd 0%, #eec861 18%, #d9a437 44%,
    #b8801f 62%, #d9a437 84%, #f2d689 100%) !important;
}
/* on the dark screens the same button, inverted for the ground */


/* ===================================================================
   BACK TO TOP
   One at the close of every section, in the flow, for every visitor -
   not a floating button that only appears once you have scrolled.
   =================================================================== */
.to-top-row { display: flex; justify-content: flex-end; margin: clamp(30px, 5vw, 60px) 0 0; }
.to-top {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 22px; font-size: 15px;
}
.to-top .arw { font-size: 15px; line-height: 1; transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) {
  .btn::after, .filter-chip::after, .to-top::after, .threshold-btn::after { animation: none; }
  .btn, .filter-chip, .virtue-btn, .tab, .cite-tab, .help-toggle,
  .threshold-btn, .nav-exit-btn, .to-top { animation: none !important; }
  .btn:hover, .to-top:hover, .threshold-btn:hover { transform: none; }
}


/* ===================================================================
   THE TEMPLE PANEL
   The card carried a dark brown-to-teal gradient behind its picture.
   Whenever the image failed to load, that gradient was what you saw -
   the "broken frame". The panel now sits on the page's own parchment,
   so a missing image degrades quietly instead of looking broken.
   =================================================================== */
.portrait-card-fullimg {
  background: #f6ead0 !important;
  min-height: clamp(320px, 34vw, 520px);
  aspect-ratio: 16 / 9;
  box-shadow: 0 22px 60px rgba(30, 15, 0, 0.18);
  overflow: hidden;
}
.portrait-card-fullimg > picture { display: block; line-height: 0; width: 100%; height: 100%; }
.portrait-card-fullimg .photo-shot { display: block; width: 100%; height: 100%; }
/* the picture sets the panel's height rather than being stretched to fill it */
.temple-img-full {
  position: static !important;
  inset: auto !important;
  width: 100%;
  height: 100% !important;
  object-fit: cover !important;
  border-radius: 26px !important;
  display: block;
}

/* ===================================================================
   THE LAST FEW BUTTONS
   Four "Read Reflection", the gallery mode chips and the tour controls
   carried no shared class, so the gold treatment never reached them.
   =================================================================== */
[data-story], .mode-chip, #tourNext, #tourClose, #galleryPrev, #galleryNext,
.gallery-nav button, .modal-close, .story-close {
  position: relative;
  overflow: hidden;
  font-family: var(--font-body) !important;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #4a2708 !important;
  background:
    linear-gradient(180deg,
      #f7dc93 0%, #e0b348 16%, #c8912c 42%,
      #a97418 60%, #c9942f 82%, #e6c168 100%) !important;
  border: 1px solid #7d5410 !important;
  border-radius: 999px;
  padding: 10px 22px;
  cursor: pointer;
  text-decoration: none;
  text-shadow: 0 1px 0 rgba(255, 240, 196, 0.75);
  transition: transform 0.25s var(--ease), filter 0.25s var(--ease);
  animation: btnGlow 5.6s ease-in-out infinite;
}
[data-story]::after, .mode-chip::after, #tourNext::after, #tourClose::after,
.modal-close::after, .story-close::after {
  content: "";
  position: absolute; inset: -50% auto -50% -30%;
  width: 30%;
  background: linear-gradient(90deg,
    transparent, rgba(255, 248, 214, 0.75), rgba(255, 236, 176, 0.35), transparent);
  transform: translateX(0) rotate(25deg);
  animation: sweep 5.4s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: transform;
  pointer-events: none;
}
[data-story]:hover, .mode-chip:hover, #tourNext:hover, #tourClose:hover,
.modal-close:hover, .story-close:hover {
  transform: translateY(-2px);
  filter: brightness(1.08);
}
[data-story]:active, .mode-chip:active { transform: translateY(0) scale(0.98); }
.mode-chip[aria-pressed="true"] {
  background: linear-gradient(180deg,
    #fff0bd 0%, #eec861 18%, #d9a437 44%,
    #b8801f 62%, #d9a437 84%, #f2d689 100%) !important;
}
/* the close buttons stay round rather than becoming pills */
.modal-close, .story-close { padding: 0; width: 38px; height: 38px; border-radius: 50%; }
@media (prefers-reduced-motion: reduce) {
  [data-story], .mode-chip, #tourNext, #tourClose, .modal-close, .story-close { animation: none !important; }
  [data-story]::after, .mode-chip::after { animation: none; }
}


/* the temple tile's edge, applied to the three story sections' cards */
.card, .timeline-item, .form-card, .tribute-card, .admin-panel,
.help-card, .virtue-card, .gallery-card, .capsule-card, .empty-state,
.tributes-list > *, .story-copy, .pub-panel {
  border-radius: 32px !important;
  border: 1px solid rgba(212, 160, 58, 0.45) !important;
  box-shadow: 0 22px 60px rgba(30, 15, 0, 0.18) !important;
  background: linear-gradient(180deg, #fffdf6, #f7eeda) !important;
}

/* ---- the upper tile edge: gradient bar on EVERY tile ---- */
.card, .timeline-item, .form-card, .tribute-card, .tribute-placeholder-card,
.tribute-form, .dedication-strip, .admin-panel, .admin-tribute, .help-card,
.virtue-card, .gallery-card, .capsule-card, .capsule-empty, .empty-state,
.tributes-list > *, .pub-panel, .preview-state, .foundation-shell, .dossier-shell {
  border-radius: 32px !important;
  border: 1px solid rgba(212, 160, 58, 0.45) !important;
  box-shadow: 0 22px 60px rgba(30, 15, 0, 0.18) !important;
  background: linear-gradient(180deg, #fffdf6, #f7eeda) !important;
  position: relative;
}
.card::before, .timeline-item::before, .form-card::before, .tribute-card::before,
.tribute-placeholder-card::before, .tribute-form::before, .dedication-strip::before,
.admin-panel::before, .admin-tribute::before, .help-card::before, .virtue-card::before,
.gallery-card::before, .capsule-card::before, .capsule-empty::before, .empty-state::before,
.tributes-list > *::before, .pub-panel::before, .preview-state::before,
.foundation-shell::before, .dossier-shell::before {
  content: ""; position: absolute; top: 0; left: 26px; right: 26px; bottom: auto;
  height: 3px; width: auto; inset-inline: 26px;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal)) !important;
  border-radius: 0 0 3px 3px; opacity: 0.9; pointer-events: none; z-index: 3;
}


/* ---- Guest Tribute Window: relaid out ---- */
.field-row.field-row-3 {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px; align-items: end;
  background: none !important; border: 0 !important; box-shadow: none !important;
  border-radius: 0 !important; padding: 0; margin-bottom: 18px;
}
.field-row.field-row-3::before { display: none !important; }
.tribute-write .message-field { margin: 0; }
.tribute-write .message-field textarea { min-height: 340px; line-height: 1.65; }
.tribute-write .char-meter { margin-top: 10px; }
.tribute-write > small { display: block; margin-top: 6px; color: var(--muted); }
.tribute-media .seal-field { margin-top: 20px; }
.submit-row {
  display: flex; justify-content: center; margin-top: 26px;
}
.submit-row .btn { min-width: 320px; justify-content: center; text-align: center; }
.tribute-form .preview-state { margin-top: 26px; }
@media (max-width: 900px) {
  .field-row.field-row-3 { grid-template-columns: 1fr; gap: 14px; }
  .submit-row .btn { min-width: 0; width: 100%; }
}

/* ---- 1. the two tribute columns must end on the same line ---- */
.tribute-grid { align-items: stretch; }
.tribute-write, .tribute-media { display: flex; flex-direction: column; min-width: 0; }
.tribute-write .message-field {
  flex: 1 1 auto; display: grid; grid-template-rows: auto 1fr; min-height: 0;
}
.tribute-write .message-field textarea { height: 100%; min-height: 340px; }
.tribute-media .upload-grid, .tribute-media .tracker-state { flex: 0 0 auto; }
.tribute-media .seal-field { margin-top: auto; }

/* ---- 2. the mandir may never be wider than its column ----
   aspect-ratio 16/9 combined with a min-height in px made the browser
   solve for WIDTH (height x 16/9), which pushed the panel off the page.
   The width now comes from the grid column and the height follows it. */
.hero-layout > * { min-width: 0; }
.portrait-card.portrait-card-fullimg,
.portrait-card-fullimg {
  width: 100%; max-width: 100%;
  min-height: 0 !important;
  height: auto !important;
  aspect-ratio: 16 / 9;
}
.portrait-card-fullimg > picture,
.portrait-card-fullimg .photo-shot,
.portrait-card-fullimg .temple-img-full { max-width: 100%; }

/* ---- 3. Guest Book tiles carry the same upper border ----
   .approved-grid article::before was the decorative quote mark and, being
   more specific, it was winning over the shared tile rule. The quote moves
   to ::after so the gradient edge can use ::before like every other tile. */
.approved-grid article::before,
.approved-grid > *::before {
  content: "" !important; position: absolute; top: 0; left: 26px; right: 26px;
  bottom: auto; height: 3px; width: auto; font: inherit;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal)) !important;
  border-radius: 0 0 3px 3px; opacity: 0.9; pointer-events: none; z-index: 3;
}
.approved-grid article::after {
  content: "\201C"; position: absolute; right: 14px; top: -8px;
  font: 700 80px var(--font-display); color: rgba(212, 160, 58, 0.12);
  pointer-events: none;
}
.approved-grid > * {
  position: relative;
  border-radius: 32px !important;
  border: 1px solid rgba(212, 160, 58, 0.45) !important;
  box-shadow: 0 22px 60px rgba(30, 15, 0, 0.18) !important;
  background: linear-gradient(180deg, #fffdf6, #f7eeda) !important;
}


/* ---- the celestial glow belongs to the opening and farewell screens ----
   The mandalas drifting behind the ordinary pages carry no halo; only the
   four on #threshold and on the farewell keep theirs. */
.mandala-flow .mandala-glow,
#mandalaFlow .mandala-glow {
  display: none !important;
  animation: none !important;
  background: none !important;
  filter: none !important;
  opacity: 0 !important;
}
.threshold .mandala-glow,
.farewell .mandala-glow,
.t-mandala .mandala-glow {
  display: block !important;
  animation: mandalaGlow cubic-bezier(0.42, 0, 0.58, 1) infinite !important;
  opacity: 1;
}

/* ===================================================================
   +3pt: the opening screen's names and title, and the farewell's names
   Each keeps its own fluid curve - the 3px is added to BOTH arms of the
   min(), so the no-wrap caps still hold at every width and neither line
   can break. The dedication sentence and the farewell blessing are
   deliberately left alone.
   =================================================================== */
.threshold-names {
  font-size: min(clamp(32px, 1.6vw, 37px), calc(1.58vw + 3px)) !important;
}
.threshold-title {
  font-size: min(clamp(33px, 2.9vw, 65px), calc(2.48vw + 3px)) !important;
}
.farewell-names {
  font-size: min(clamp(23px, 1.05vw, 27px), calc(1.05vw + 3px)) !important;
}
@media (max-width: 900px) {
  .threshold-names { font-size: clamp(27px, 6.6vw, 34px) !important; }
  .threshold-title { font-size: clamp(29px, 5.2vw, 43px) !important; }
  .farewell-names  { font-size: clamp(18px, 3.6vw, 22px) !important; }
}

/* ===================================================================
   THE FAREWELL SCREEN, +3pt
   Its two display lines - the pair of names, and the closing line that
   carries the title's styling - each gain 3px on both arms of the
   min(), so the no-wrap names still cannot break.
   =================================================================== */
.farewell-names {
  font-size: min(clamp(26px, 1.05vw, 30px), calc(1.05vw + 6px)) !important;
}
.farewell-blessing {
  font-size: min(clamp(33px, 2.9vw, 65px), calc(2.48vw + 3px)) !important;
}
@media (max-width: 900px) {
  .farewell-names    { font-size: clamp(21px, 3.6vw, 25px) !important; }
  .farewell-blessing { font-size: clamp(29px, 5.2vw, 43px) !important; }
}

/* ===================================================================
   THE TWO SCREENS — MARIGOLD AND DARK LOTUS, NOTHING ELSE
   Every brown, copper, olive and neutral grey is gone from the opening
   and the farewell. Two hues remain: Festive Marigold for everything
   that carries light, Dark Lotus for everything that holds it. Tints of
   each are used for depth - a single flat value at both ends would make
   the type unreadable - but no third hue appears anywhere.
   The diya itself is untouched: its flame is never restyled.
   =================================================================== */
:root {
  --marigold:      #ffc14a;   /* Festive Marigold */
  --marigold-lit:  #ffdc95;   /* its light */
  --marigold-pale: #fff1cd;   /* its crest */
  --lotus:         #3a1a48;   /* Dark Lotus */
  --lotus-deep:    #241030;
  --lotus-night:   #140719;
}
.threshold, .farewell {
  background:
    radial-gradient(ellipse 46% 38% at 50% 40%,  rgba(255, 193, 74, 0.22), transparent 64%),
    radial-gradient(ellipse 70% 62% at  6%  4%,  rgba(255, 193, 74, 0.15), transparent 62%),
    radial-gradient(ellipse 78% 70% at 96% 98%,  rgba( 36, 16, 48, 0.74), transparent 66%),
    radial-gradient(ellipse 128% 104% at 50% 50%, transparent 40%, rgba(20, 7, 25, 0.80) 100%),
    linear-gradient(116deg,
      #2b1236 0%, #3a1a48 24%, #4a2159 50%, #2b1236 76%, #140719 100%) !important;
  background-color: var(--lotus-night) !important;
}
/* the scrim behind the words: lotus, not neutral black */
.threshold-inner::before, .farewell-inner::before {
  background: radial-gradient(ellipse at 50% 50%,
    rgba(20, 7, 25, 0.46) 0%, rgba(20, 7, 25, 0.30) 46%, transparent 74%) !important;
}
/* the dedication and the thank-you line */
.threshold-line, .farewell-line {
  color: var(--marigold) !important;
  text-shadow: 0 0 22px rgba(255, 193, 74, 0.45),
               0 2px 7px rgba(20, 7, 25, 0.94),
               0 1px 2px rgba(20, 7, 25, 0.98) !important;
}
/* the title, and the farewell's closing line */
.threshold-title, .farewell-blessing {
  color: var(--marigold-lit) !important;
  text-shadow: 0 0 26px rgba(255, 193, 74, 0.55),
               0 3px 9px rgba(20, 7, 25, 0.92),
               0 1px 3px rgba(20, 7, 25, 0.98) !important;
}
/* the Exit / ghost button on both screens */
.threshold-exit {
  background: transparent !important;
  color: var(--marigold-lit) !important;
  border: 1px solid rgba(255, 193, 74, 0.34) !important;
}
.threshold-exit:hover {
  color: var(--marigold-pale) !important;
  border-color: rgba(255, 193, 74, 0.72) !important;
  background: rgba(255, 193, 74, 0.10) !important;
}

/* ---- the names glow, continuously, on both screens ----
   The arrival animation is kept and the glow is chained after it, so
   the names still rise into place and then breathe on their own. */
@keyframes nameGlow {
  0%, 100% {
    color: var(--marigold-lit);
    text-shadow: 0 0 12px rgba(255, 193, 74, 0.40),
                 0 0 30px rgba(255, 193, 74, 0.18),
                 0 2px 6px rgba(20, 7, 25, 0.92);
  }
  50% {
    color: var(--marigold-pale);
    text-shadow: 0 0 22px rgba(255, 220, 149, 0.90),
                 0 0 58px rgba(255, 193, 74, 0.55),
                 0 0 104px rgba(255, 193, 74, 0.28),
                 0 2px 6px rgba(20, 7, 25, 0.92);
  }
}
.threshold-names {
  color: var(--marigold-lit) !important;
  animation: riseIn 1.4s var(--ease-soft) 3.0s both,
             nameGlow 5.2s ease-in-out 4.4s infinite !important;
}
.farewell-names {
  color: var(--marigold-lit) !important;
  animation: riseIn 1.3s var(--ease-soft) 0.6s both,
             nameGlow 5.2s ease-in-out 4.3s infinite !important;
}
@media (prefers-reduced-motion: reduce) {
  .threshold-names, .farewell-names {
    animation: none !important;
    color: var(--marigold-lit) !important;
    text-shadow: 0 0 18px rgba(255, 193, 74, 0.5), 0 2px 6px rgba(20, 7, 25, 0.92) !important;
  }
}

/* ===================================================================
   1. THE NAMES, BOLDER
   High Tower Text and its fallbacks have no black cut, so the weight is
   pushed to 900 and carried the rest of the way by a hairline stroke in
   the glyph's own colour - it thickens the stems without smearing the
   serifs the way a text-shadow would.
   =================================================================== */
.threshold-names, .farewell-names {
  font-weight: 900 !important;
  -webkit-text-stroke: 0.7px currentColor;
  paint-order: stroke fill;
}

/* ===================================================================
   2. THE NAVIGATION BAR'S EXIT
   It was wearing the struck-gold treatment while every other item in
   the bar is a quiet text pill. It now matches them exactly: same
   padding, face, colour, hover wash, sliding underline and glow.
   =================================================================== */
.nav-links .nav-exit-btn {
  padding: 8px 14px !important;
  border: 0 !important;
  border-radius: 999px !important;
  background: none !important;
  box-shadow: none !important;
  animation: none !important;
  font-family: inherit !important;
  font-size: 16px !important;
  font-weight: 700 !important;
  letter-spacing: normal !important;
  color: var(--text) !important;
  text-shadow: 0 0 8px rgba(212, 160, 58, 0.16) !important;
  cursor: pointer;
  position: relative;
  overflow: visible;
  transition: all 0.2s var(--ease), text-shadow 0.3s var(--ease);
}
.nav-links .nav-exit-btn:hover {
  background: rgba(212, 160, 58, 0.16) !important;
  color: var(--heading) !important;
  transform: translateY(-1px);
  filter: none;
  text-shadow: 0 0 12px rgba(212, 160, 58, 0.65), 0 0 3px rgba(212, 160, 58, 0.5) !important;
}
/* the metal sweep off, the nav underline on */
.nav-links .nav-exit-btn::after {
  content: "" !important;
  position: absolute !important;
  inset: auto auto 4px 50% !important;
  transform: translateX(-50%) !important;
  width: 0 !important; height: 2px !important;
  background: linear-gradient(90deg, var(--heading), var(--gold)) !important;
  animation: none !important;
  border-radius: 2px;
  transition: width 0.24s var(--ease-soft);
}
.nav-links .nav-exit-btn:hover::after { width: 72% !important; }

/* ===================================================================
   3. THE OPENING SCREEN'S EXIT = ITS BEGIN
   Identical struck gold, identical sweep, identical breathing glow -
   the two buttons now differ only in their words.
   =================================================================== */
.threshold-exit, .threshold-exit:hover, .threshold-exit:active {
  border: 1px solid #7d5410 !important;
  color: #4a2708 !important;
  background: linear-gradient(180deg,
    #f7dc93 0%, #e0b348 16%, #c8912c 42%,
    #a97418 60%, #c9942f 82%, #e6c168 100%) !important;
  text-shadow: 0 1px 0 rgba(255, 240, 196, 0.75) !important;
}
.threshold-exit { animation: btnGlow 5.6s ease-in-out infinite !important; }
.threshold-exit:hover { transform: translateY(-2px); filter: brightness(1.08); }
.threshold-exit:active { transform: translateY(0) scale(0.98); }

@media (prefers-reduced-motion: reduce) {
  .threshold-exit { animation: none !important; }
}

/* ===================================================================
   THE TWO SCREENS — BLACK AND FESTIVE MARIGOLD
   The Dark Lotus plum is replaced by true black. Black holds the
   ground and the corners; Festive Marigold is the only light in the
   room. Tints of each carry the depth - no third hue appears.
   The diya is untouched: its flame is never restyled.
   =================================================================== */
:root {
  --ink:       #0a0806;   /* the black ground */
  --ink-lift:  #1a1208;   /* black warmed by the marigold light */
}
.threshold, .farewell {
  background:
    radial-gradient(ellipse 46% 38% at 50% 40%,  rgba(255, 193, 74, 0.26), transparent 64%),
    radial-gradient(ellipse 70% 62% at  6%  4%,  rgba(255, 193, 74, 0.17), transparent 62%),
    radial-gradient(ellipse 78% 70% at 96% 98%,  rgba(  0,  0,  0, 0.82), transparent 66%),
    radial-gradient(ellipse 128% 104% at 50% 50%, transparent 38%, rgba(0, 0, 0, 0.92) 100%),
    linear-gradient(116deg,
      #14100a 0%, #241a0c 24%, #2e2109 50%, #150f07 76%, #000000 100%) !important;
  background-color: #000000 !important;
}
/* a background-origin/-clip override used to be needed here, back when
   the vine border was a real border on this same box - moving that
   border to .threshold::after/.farewell::after (see above) means this
   box's own background no longer shares anything with a border at
   all, so there is nothing left for that fix to correct. */
/* the scrim behind the words: black, so the marigold sits on it cleanly */
.threshold-inner::before, .farewell-inner::before {
  background: radial-gradient(ellipse at 50% 50%,
    rgba(0, 0, 0, 0.58) 0%, rgba(0, 0, 0, 0.38) 46%, transparent 74%) !important;
}
/* every shadow under the type turns black */
.threshold-line, .farewell-line {
  color: var(--marigold) !important;
  text-shadow: 0 0 22px rgba(255, 193, 74, 0.48),
               0 2px 7px rgba(0, 0, 0, 0.96),
               0 1px 2px rgba(0, 0, 0, 1) !important;
}
.threshold-title, .farewell-blessing {
  color: var(--marigold-lit) !important;
  text-shadow: 0 0 26px rgba(255, 193, 74, 0.58),
               0 3px 9px rgba(0, 0, 0, 0.95),
               0 1px 3px rgba(0, 0, 0, 1) !important;
}
/* exit screen: "The lamp remains lit." in the same dark red */
.farewell-blessing {
  color: #6b0f0f !important;
  text-shadow: 0 0 18px rgba(107, 15, 15, 0.35),
               0 3px 9px rgba(0, 0, 0, 0.95),
               0 1px 3px rgba(0, 0, 0, 1) !important;
}
/* start screen only: hero title "The Didolkar Family Legacy" in dark red */
.threshold-title {
  color: #6b0f0f !important;
  text-shadow: 0 0 18px rgba(107, 15, 15, 0.35),
               0 3px 9px rgba(0, 0, 0, 0.95),
               0 1px 3px rgba(0, 0, 0, 1) !important;
}
@keyframes nameGlow {
  0%, 100% {
    color: var(--marigold-lit);
    text-shadow: 0 0 12px rgba(255, 193, 74, 0.42),
                 0 0 30px rgba(255, 193, 74, 0.20),
                 0 2px 6px rgba(0, 0, 0, 0.96);
  }
  50% {
    color: var(--marigold-pale);
    text-shadow: 0 0 22px rgba(255, 220, 149, 0.92),
                 0 0 58px rgba(255, 193, 74, 0.58),
                 0 0 104px rgba(255, 193, 74, 0.30),
                 0 2px 6px rgba(0, 0, 0, 0.96);
  }
}
@media (prefers-reduced-motion: reduce) {
  .threshold-names, .farewell-names {
    text-shadow: 0 0 18px rgba(255, 193, 74, 0.55), 0 2px 6px rgba(0, 0, 0, 0.96) !important;
  }
}


/* ===================================================================
   THE ACADEMIC SECTION HAS NO BACK PANEL
   .dossier-shell was stripped bare earlier, but the site-wide tile rule
   later swept it back up and gave it a ground, a border and the gradient
   top edge. The tabs and the two cards now sit straight on the page.
   =================================================================== */
.dossier-shell {
  background: none !important;
  border: 0 !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  padding: 0 !important;
  backdrop-filter: none !important;
  overflow: visible !important;
}
.dossier-shell::before, .dossier-shell::after { content: none !important; display: none !important; }


/* ===================================================================
   1. FOUNDATION HEADING - true centring
   The heading is text-align: center already, but it carries
   white-space: nowrap while its container caps at 1000px. On wide
   screens the single line measures wider than that cap, so it
   overflowed to the right and only looked off-centre. Letting the
   container run the full width of .section-inner gives the centred
   line room to actually sit in the middle.
   =================================================================== */
#foundation .section-heading {
  max-width: 100% !important;
  width: 100% !important;
  margin-left: auto !important;
  margin-right: auto !important;
}
#foundation .section-heading h2.heading-accent {
  display: block !important;
  width: 100% !important;
  max-width: 100% !important;
  text-align: center !important;
  margin-left: auto !important;
  margin-right: auto !important;
}


/* ===================================================================
   2. THE TWO SCREENS - capsule regalia border
   Capsule shape, regalia pulse (scale + glow + border colour), a
   shifting gold wash beneath, and a liquid that rises to the brim
   then falls all the way back. Applied to the button GROUP only -
   the buttons themselves keep their existing colours untouched.
   =================================================================== */
.threshold-actions, .farewell-actions {
  position: relative;
  width: fit-content !important;
  max-width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
  padding: 12px;
  border-radius: 999px;
  border: 2px solid var(--marigold, #e0b348);
  overflow: hidden;
  isolation: isolate;
}
.threshold-actions::before, .farewell-actions::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(90deg, #3d2a08, #6b4a0d, #a97418, #6b4a0d, #3d2a08);
  background-size: 250% 100%;
  animation: capWash 6.8s linear infinite;
  will-change: background-position;
}
.threshold-actions::after, .farewell-actions::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0; height: 100%; z-index: 1;
  background: linear-gradient(180deg,
    rgba(255, 241, 205, 0.40) 0%, rgba(224, 179, 72, 0.47) 6%,
    rgba(224, 179, 72, 0.27) 30%, rgba(143, 100, 20, 0.20) 100%);
  animation: capTide 6.8s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: transform;
}
/* the buttons must sit above both fill layers */
.threshold-actions > *, .farewell-actions > * { position: relative; z-index: 2; }
.threshold-actions, .farewell-actions { will-change: transform, box-shadow; }

@keyframes capWash { 0% { background-position: 0 0; } 100% { background-position: 250% 0; } }
@keyframes capTide {
  0%        { transform: translateY(100%); }
  45%, 55%  { transform: translateY(0); }
  100%      { transform: translateY(100%); }
}
/* The scale was 1.03, enough to resample the button text every frame and
   make it shimmer. At 1.012 the swell still reads but the type stays
   crisp, and the glow now carries most of the movement. */
@keyframes capRegalia {
  0%, 100% { transform: scale(1);
             box-shadow: 0 0 4px #8f6414, inset 0 0 4px #8f6414;
             border-color: #8f6414; }
  35%      { transform: scale(1.012);
             box-shadow: 0 0 20px #e0b348, inset 0 0 14px rgba(255,241,205,0.42);
             border-color: #fff1cd; }
  70%      { transform: scale(0.998);
             box-shadow: 0 0 10px #fff1cd, inset 0 0 6px #e0b348;
             border-color: #e0b348; }
}
/* the arrival animation is kept, with the regalia pulse taking over after it */
.threshold-actions {
  animation: riseIn 1.1s var(--ease-spring) 4.0s both,
             capRegalia 6.8s cubic-bezier(0.45, 0, 0.55, 1) 5.1s infinite !important;
}
.farewell-actions {
  animation: riseIn 1.0s var(--ease-spring) 1.0s both,
             capRegalia 6.8s cubic-bezier(0.45, 0, 0.55, 1) 2.0s infinite !important;
}


/* ===================================================================
   3. EVERY OTHER PAGE - halo in sync with a deep double beat
   Border thickness, colour, the wide inner halo and the inner rim
   line all move on one set of keyframes, so they strike together on
   each of the two beats. Again the group is bordered, never the
   buttons - their colours are untouched.
   =================================================================== */
.button-row {
  position: relative;
  width: fit-content !important;
  max-width: 100%;
  justify-content: center !important;
  align-items: center;
  margin-left: auto !important;
  margin-right: auto !important;
  padding: 12px;
  border-radius: 10px;
  border: 2px solid #e0b348;
  animation: rowBeatRim 6.8s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: box-shadow, border-color;
}
/* The beat used to animate border-width from 1px to 9px. Because border
   width is a layout property, every frame forced the browser to re-lay
   out the group and nudged the buttons inside it - that jitter is what
   read as a constant flicker, everywhere this rule was used.

   The thickness is now drawn with an inset box-shadow ring instead. It
   looks the same but costs only a repaint, so the geometry never moves
   and the buttons sit perfectly still. The ramps are also wider and the
   cycle longer, so the two beats swell and fade rather than snapping. */
@keyframes rowBeatRim {
  0%, 100% { border-color: #7a5510;
             box-shadow: inset 0 0 0 1px rgba(143,100,20,0.55),
                         inset 0 0 14px 4px rgba(143,100,20,0.28),
                         0 0 5px rgba(143,100,20,0.22); }
  22%      { border-color: #f2d68c;
             box-shadow: inset 0 0 0 4px rgba(224,179,72,0.95),
                         inset 0 0 42px 16px rgba(255,241,205,0.42),
                         0 0 15px rgba(224,179,72,0.42); }
  40%      { border-color: #9a6d16;
             box-shadow: inset 0 0 0 1px rgba(143,100,20,0.65),
                         inset 0 0 16px 5px rgba(143,100,20,0.34),
                         0 0 7px rgba(143,100,20,0.26); }
  64%      { border-color: #fff1cd;
             box-shadow: inset 0 0 0 5px rgba(224,179,72,1),
                         inset 0 0 48px 19px rgba(224,179,72,0.50),
                         0 0 18px rgba(255,241,205,0.46); }
  84%      { border-color: #9a6d16;
             box-shadow: inset 0 0 0 1px rgba(143,100,20,0.65),
                         inset 0 0 16px 5px rgba(143,100,20,0.34),
                         0 0 7px rgba(143,100,20,0.26); }
}
/* On narrow screens the row stacks, so it must be free to fill the width */
@media (max-width: 620px) {
  .button-row { width: auto !important; padding: 10px; }
  .threshold-actions, .farewell-actions { padding: 10px; }
}
/* Nobody should be made to watch this. */
@media (prefers-reduced-motion: reduce) {
  .button-row, .threshold-actions, .farewell-actions,
  .threshold-actions::before, .farewell-actions::before,
  .threshold-actions::after, .farewell-actions::after {
    animation: none !important;
  }
  .threshold-actions::after, .farewell-actions::after { transform: translateY(55%); }
}


/* ===================================================================
   4. SHAPE BY GROUP SIZE
   Two or more buttons  -> cornered rectangle, group and buttons alike.
   A single button       -> capsule, revealed by a curtain parting over
                            a deep ink fill.
   Button COLOURS are never touched here - only radius and the group's
   own shape and border.
   =================================================================== */

/* ---- two or more buttons: cornered rectangle ---- */
.threshold-actions,
.farewell-actions,
.button-row:has(> :nth-child(2)) {
  border-radius: 12px !important;
}
/* the buttons inside them square off to match, colours unchanged */
.threshold-actions > .threshold-btn,
.farewell-actions  > .threshold-btn,
.button-row:has(> :nth-child(2)) > .btn,
.button-row:has(> :nth-child(2)) > a,
.button-row:has(> :nth-child(2)) > button {
  border-radius: 7px !important;
}

/* ---- a single button: capsule, curtain part over deep ink ----
   .submit-row holds exactly one button (Submit for Admin Review), so it
   takes the single-button treatment too. */
.button-row:has(> *:only-child),
.submit-row {
  position: relative;
  width: fit-content !important;
  max-width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
  border: 0 !important;
  border-radius: 999px !important;
  background: #0a0806 !important;
  box-shadow: none !important;
  animation: none !important;
  padding: 12px;
  overflow: visible;
}
.button-row:has(> *:only-child)::before,
.submit-row::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  border: 2px solid var(--marigold, #e0b348);
  border-radius: 999px;
  box-shadow: 0 0 12px rgba(224, 179, 72, 0.35),
              inset 0 0 10px rgba(224, 179, 72, 0.18);
  animation: curtainPart 6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: clip-path, opacity;
}
.button-row:has(> *:only-child) > *,
.submit-row > * {
  position: relative; z-index: 2;
  border-radius: 999px !important;
}
/* The curtain rests open for most of the cycle and closes gently, rather
   than snapping shut and reopening - that snap is what read as a flicker. */
@keyframes curtainPart {
  0%        { clip-path: inset(0 46% 0 46% round 999px); opacity: 0.45; }
  32%, 68%  { clip-path: inset(0 0 0 0 round 999px);     opacity: 1; }
  100%      { clip-path: inset(0 46% 0 46% round 999px); opacity: 0.45; }
}
/* the button goes full-width on smaller screens, so the capsule must
   stop shrink-wrapping or the two fight each other */
@media (max-width: 900px) {
  .submit-row { width: auto !important; padding: 10px; }
}
@media (prefers-reduced-motion: reduce) {
  .button-row:has(> *:only-child)::before,
  .submit-row::before {
    animation: none !important;
    clip-path: inset(0 0 0 0 round 999px);
    opacity: 1;
  }
}


/* ===================================================================
   5. THE REMAINING BUTTON GROUPS
   The virtue tabs, the academic archive tabs, the guest book and
   gallery filter bars and the citation tabs are all groups of two or
   more, so they take the cornered rectangle and the same beat as
   every other multi-button row. Their button colours - the struck
   gold, the peacock chips - are left exactly as they are.
   =================================================================== */
.virtue-controls,
.tabs,
.filter-chips,
.gallery-filters,
.cite-tabs {
  position: relative;
  width: fit-content;
  max-width: 100%;
  justify-content: center;
  align-items: center;
  margin-left: auto;
  margin-right: auto;
  padding: 12px;
  border-radius: 12px;
  border: 2px solid #e0b348;
  animation: rowBeatRim 6.8s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: box-shadow, border-color;
}
/* the group's own buttons square off to match the frame */
.virtue-controls > .virtue-btn,
.tabs > .tab,
.filter-chips > .filter-chip,
.gallery-filters > .filter-chip,
.cite-tabs > .cite-tab {
  border-radius: 7px !important;
}
/* the chip group lays out like the bar it came from */
.filter-chips {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
}
/* the bar itself is now only a layout wrapper - no frame, no beat */
.filter-bar {
  border: 0 !important;
  padding: 0 !important;
  width: auto !important;
  animation: none !important;
  flex-direction: column;
  gap: 14px;
}
@media (max-width: 620px) {
  .virtue-controls, .tabs, .filter-chips, .gallery-filters, .cite-tabs {
    width: auto;
    padding: 10px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .virtue-controls, .tabs, .filter-chips,
  .gallery-filters, .cite-tabs { animation: none !important; }
}


/* ===================================================================
   6. THE DEDICATION — one whole line, arriving a different way each time
   Fourteen arrivals. app.js adds .dl plus one .dl--n at random on every
   visit, so the opening is never quite the same twice. The line itself
   is untouched: same face, same colour, same wrapping.
   =================================================================== */
.dl {
  animation-duration: 2.6s;
  animation-timing-function: var(--ease-soft, cubic-bezier(0.22, 1, 0.36, 1));
  animation-fill-mode: both;
  will-change: opacity, transform, filter;
}
.dl--1  { animation-name: dlFade; }
.dl--2  { animation-name: dlRise; }
.dl--3  { animation-name: dlSettle; }
.dl--4  { animation-name: dlBlur; }
.dl--5  { animation-name: dlBreathe; }
.dl--6  { animation-name: dlFromLeft; }
.dl--7  { animation-name: dlFromRight; }
.dl--8  { animation-name: dlUnveil; }
.dl--9  { animation-name: dlCurtain; }
.dl--10 { animation-name: dlEmber; }
.dl--11 { animation-name: dlTilt; }
.dl--12 { animation-name: dlWiden; }
.dl--13 { animation-name: dlDeepen; }
.dl--14 { animation-name: dlLantern; }

@keyframes dlFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes dlRise {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes dlSettle {
  from { opacity: 0; transform: translateY(-16px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes dlBlur {
  from { opacity: 0; filter: blur(10px); }
  to   { opacity: 1; filter: blur(0); }
}
@keyframes dlBreathe {
  from { opacity: 0; transform: scale(0.94); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes dlFromLeft {
  from { opacity: 0; transform: translateX(-34px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes dlFromRight {
  from { opacity: 0; transform: translateX(34px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes dlUnveil {
  from { opacity: 0; clip-path: inset(0 0 100% 0); }
  to   { opacity: 1; clip-path: inset(0 0 0 0); }
}
@keyframes dlCurtain {
  from { opacity: 0; clip-path: inset(0 50% 0 50%); }
  to   { opacity: 1; clip-path: inset(0 0 0 0); }
}
@keyframes dlEmber {
  0%   { opacity: 0; filter: brightness(2.4) blur(6px); }
  55%  { opacity: 1; filter: brightness(1.35) blur(0); }
  100% { opacity: 1; filter: brightness(1) blur(0); }
}
@keyframes dlTilt {
  from { opacity: 0; transform: perspective(900px) rotateX(14deg) translateY(16px); }
  to   { opacity: 1; transform: perspective(900px) rotateX(0) translateY(0); }
}
@keyframes dlWiden {
  from { opacity: 0; letter-spacing: 0.16em; }
  to   { opacity: 1; letter-spacing: normal; }
}
@keyframes dlDeepen {
  from { opacity: 0; transform: scale(1.07); filter: blur(7px); }
  to   { opacity: 1; transform: scale(1); filter: blur(0); }
}
@keyframes dlLantern {
  0%   { opacity: 0; filter: brightness(0.4); transform: scale(0.97); }
  60%  { opacity: 1; filter: brightness(1.5); transform: scale(1.01); }
  100% { opacity: 1; filter: brightness(1); transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .dl {
    animation: none !important;
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
    clip-path: none !important;
  }
}


/* ===================================================================
   7. BACK TO TOP — hard right, level with the section's last block
   Positioned absolutely against the section, which already carries
   position: relative. The horizontal edge is fixed here, matching the
   section's own gutter so the button lines up with the content column
   rather than floating off the page edge. The vertical position is
   measured by app.js, which sets `top` so the button's lower edge
   finishes level with the panel tile, story tile or button group that
   section aligns to - a nested target the button cannot share a flex
   or grid line with. .is-corner is the fallback for a section with
   nothing to align against.
   =================================================================== */
.to-top-row--anchored {
  position: absolute;
  right: clamp(16px, 5vw, 72px);          /* matches the section's own gutter */
  top: 0;                                  /* app.js sets the real value */
  margin: 0 !important;
  z-index: 4;
}
/* the fallback, used only if a section has nothing to align against */
.to-top-row--anchored.is-corner { top: auto; bottom: 0; }

/* No room beside the panel - app.js measures this and adds .is-inflow.
   The button returns to the normal flow, right-aligned under the panel,
   so it can never sit on top of the content. This is what happens on
   narrower viewports, and on televisions, which often report a small
   CSS viewport despite the size of the screen. */
.to-top-row--anchored.is-inflow {
  position: static;
  display: flex;
  justify-content: flex-end;
  margin: clamp(22px, 3vw, 44px) 0 0 !important;
  top: auto;
  bottom: auto;
}

@media (max-width: 760px) {
  .to-top-row--anchored { right: clamp(14px, 4vw, 24px); }
}


/* ===================================================================
   8. THE GALLERY'S OWN PAIR
   Pause Slideshow and Close Gallery are a group of two, so they take
   the same cornered rectangle and beat as every other multi-button
   group. Their colours are untouched. Back to top aligns with this
   group's lower border, handled in app.js.
   =================================================================== */
.gv-actions {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: fit-content;
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
  padding: 12px;
  border-radius: 12px;
  border: 2px solid #e0b348;
  animation: rowBeatRim 6.8s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: box-shadow, border-color;
}
.gv-actions > .btn { border-radius: 7px !important; }
@media (max-width: 620px) {
  .gv-actions { width: auto; padding: 10px; }
}
@media (prefers-reduced-motion: reduce) {
  .gv-actions { animation: none !important; }
}





/* ===================================================================
   PROMPTS AND EMPTY STATES IN ITALIC
   These lines are not content in their own right - they are the site
   speaking to the visitor while it waits for something: a tab to be
   chosen, a tribute to be approved, a field to be filled. Setting them
   in italic separates that voice from the real body text around them.
   The academic lines and the guest book placeholder are also rewritten
   by app.js, but it replaces only their text, so the style holds.
   =================================================================== */
#raginiAcademic,
#vijayAcademic,
.tribute-placeholder-card p,
.admin-login input {
  font-style: italic;
}
.message-field textarea::placeholder,
.admin-login input::placeholder {
  font-style: italic;
}

/* The four foundation captions - "Founding member", "Contributor,
   Kanyakumari" and so on. Each is the quiet gloss under a bold label,
   so it takes the same italic voice. The labels themselves stay
   upright. */
.foundation-highlights small {
  font-style: italic;
}


/* ===================================================================
   FOUR SUB-HEADINGS HELD ON ONE LINE
   Each of these sentences is meant to read as a single line. A line
   stays on one line only if its type is small enough for its own
   length, so each gets a vw cap sized to its character count. The
   constant behind them is the site's own: the opening dedication sits
   at 1.6vw for 131 characters, which works out at about 208 vw-units
   per character - the same figure reproduces the Legacy line's 1.9vw
   exactly, so these four are calibrated to the design already here.

   Below 900px there is no width left to shrink into without the type
   becoming unreadable, so they wrap normally instead.
   =================================================================== */
#foundation p.lede.lede-italic,
#legacy p.lede.lede-italic,
#moments p.lede.lede-italic,
#admin p.lede.lede-italic {
  display: block;
  width: 100%;
  max-width: none;
  white-space: nowrap;
  text-align: center;
  text-align-last: center;
  text-wrap: nowrap;
  margin-left: 0;
  margin-right: 0;
  line-height: 1.7;
}
#foundation p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 1.62vw); }  /* 128 characters */
#legacy     p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 1.89vw); }  /* 110 characters */
#moments    p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 2.12vw); }  /*  98 characters */
#admin      p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 1.68vw); }  /* 124 characters */

@media (max-width: 900px) {
  #foundation p.lede.lede-italic,
  #legacy p.lede.lede-italic,
  #moments p.lede.lede-italic,
  #admin p.lede.lede-italic {
    white-space: normal;
    text-wrap: balance;
    max-width: 40ch;
    margin-left: auto;
    margin-right: auto;
    font-size: clamp(18px, 3vw, 22px);
  }
}


/* ===================================================================
   THE HELP CARD SUB-DESCRIPTIONS IN ITALIC
   "What each part of the site holds", "Citing their research properly"
   and the rest: each is the quiet gloss under its card's title, the
   same voice as the foundation captions, so it takes the same italic.
   The titles beside them stay upright.
   =================================================================== */
.help-head p {
  font-style: italic;
}


/* ===================================================================
   ROOM FOR THE ONE-LINE SUB-HEADINGS TO CENTRE
   .section-heading is capped at 900px and centred. A sub-heading held
   on one line by white-space: nowrap is wider than that, so it spilled
   out of the right-hand side of its own box - text-align: center has
   nothing to centre within once the content is wider than the block.
   That is why the Admin line read as pushed to the right rather than
   centred. Giving these three headings the full width lets the line
   sit in the true middle of the section. Foundation already carries
   this fix from earlier.
   =================================================================== */
#legacy .section-heading,
#moments .section-heading,
#admin .section-heading,
#timeline .section-heading,
#virtues .section-heading,
#academic .section-heading {
  max-width: 100%;
  width: 100%;
  margin-left: auto;
  margin-right: auto;
}


/* ===================================================================
   THE GALLERY'S LEFT AND RIGHT ARROWS
   The buttons and their handlers were always there, but the block that
   gives the last few controls the struck-gold face targets them by id
   (#galleryPrev, #galleryNext) and sets position: relative. An id beats
   .gv-nav's class, so the arrows lost their absolute placement, fell
   into the normal flow of .gv-stage - which clips its overflow - and
   became invisible. These rules put them back on the sides of the
   photograph, keeping the gold face. Selectors are id + class so they
   outrank the rule that broke it.
   =================================================================== */
#galleryPrev.gv-nav,
#galleryNext.gv-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  padding: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 21px;
  line-height: 1;
  z-index: 4;
}
#galleryPrev.gv-nav { left: 12px;  right: auto; }
#galleryNext.gv-nav { right: 12px; left: auto; }
/* keep the vertical centring while hovering, or they jump down */
#galleryPrev.gv-nav:hover,
#galleryNext.gv-nav:hover  { transform: translateY(-50%) scale(1.08); }
#galleryPrev.gv-nav:active,
#galleryNext.gv-nav:active { transform: translateY(-50%) scale(0.94); }
/* the browser's own hiding still wins when there is only one photograph */
#galleryPrev.gv-nav[hidden],
#galleryNext.gv-nav[hidden] { display: none; }

@media (max-width: 720px) {
  #galleryPrev.gv-nav,
  #galleryNext.gv-nav { width: 38px; height: 38px; font-size: 18px; }
  #galleryPrev.gv-nav { left: 7px; }
  #galleryNext.gv-nav { right: 7px; }
}


/* ===================================================================
   THE ADMIN CONTROL BAR
   .filter-bar was set to stack vertically when the guest book's search
   field was lifted out of its chip group. The admin bar shares that
   class but keeps its buttons as direct children, so they stacked into
   a single column - which is what put them one under another. Its two
   button groups now each take the shared group rule, side by side and
   centred, with the search field on its own line between them.
   =================================================================== */
.admin-filter-bar {
  align-items: center;
  gap: 16px;
}
.admin-filter-bar .admin-group {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 10px;
}
/* buttons inside any chip group square off like every other group */
.filter-chips > .btn { border-radius: 7px !important; }
.admin-filter-bar #adminSearch {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
}
/* the stat cards read as one row of four */
.admin-stats article { min-width: 0; }


/* ===================================================================
   THE MODAL CLOSE BUTTON, BACK IN ITS CORNER
   The block that gives the last few controls their gold face lists
   .modal-close and sets position: relative. That beat .modal-close's
   own position: absolute (same specificity, later in the file), so the
   x dropped into the normal flow of the dialog and the photograph
   pushed past it. Two classes here outrank that single one, and the
   z-index lifts it clear of both the image and the side arrows.
   =================================================================== */
.modal .modal-close,
dialog .modal-close {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 6;
}
/* the photograph must never paint over the controls */
.timeline-photo-modal img,
.gallery-modal img { position: relative; z-index: 1; }
.timeline-photo-modal .tl-photo-nav { z-index: 5; }

/* ===================================================================
   NO SCROLLING BEHIND AN OPEN DIALOG
   showModal() makes the page inert but does not stop the wheel from
   scrolling the document underneath, which is why the background kept
   moving - and drifting back to the opening section - while a
   photograph was open. app.js toggles .modal-open on <body> whenever
   any dialog is open.
   =================================================================== */
/* The pin itself is done in app.js with position: fixed on <body>,
   because overflow: hidden on the scrolling element makes the browser
   clamp the scroll position to the top - which is what sent the page
   behind the photograph up to the Sacred Dedication. This class only
   hides the scrollbar so the layout does not shift as it disappears. */
body.modal-open { overflow: hidden; overscroll-behavior: none; }
/* the page is pinned, so its own scrollbar must not be reserved twice */
html:has(body.modal-open) { overflow: hidden; }


/* ===================================================================
   EVERY SECTION HEADING IN CAPITALS
   Requested for the whole site, so it is set once on the shared
   .heading-accent rule rather than section by section.

   THE CATCH, AND WHY THE SIZES BELOW CHANGE TOO
   Capitals are far wider than mixed case - roughly a third wider in
   Playfair Display, because every letter is now a capital rather than
   mostly lowercase. Several of these headings are held on ONE line by
   white-space: nowrap, and their vw caps were calculated for mixed
   case. Left alone they would overflow the right-hand edge, which is
   the fault we chased three times already.

   So each pinned heading's cap is divided by 1.35 here, and the three
   longest lines get a further 8% of slack on top - a 1% margin is not
   enough to survive small differences in how browsers render the face. The tracking
   also flips from slightly negative to slightly positive: negative
   tracking suits Playfair's heavy lowercase, but capitals need a
   little air between them to stay legible.
   =================================================================== */
.heading-accent,
h1.heading-accent, h2.heading-accent, h3.heading-accent,
.split-title,
.sacred-content h1, .sacred-content h2,
.section-heading h2, .section-heading h3,
.threshold-title, .farewell-blessing {
  text-transform: uppercase;
  letter-spacing: 0.015em;
}

/* --- the headings pinned to a single line, recalculated for caps --- */
#timeline  .section-heading h3.heading-accent { font-size: min(clamp(26px, 3.2vw, 44px), 3.67vw); }
#virtues   .section-heading h3.heading-accent { font-size: min(clamp(26px, 3.2vw, 44px), 2.68vw); }
#academic  .section-heading h3.heading-accent { font-size: min(clamp(26px, 3.2vw, 44px), 2.40vw); }
#thanks    h2.heading-accent                  { font-size: min(clamp(26px, 3.2vw, 44px), 2.36vw); }
#exit      h2.heading-accent                  { font-size: min(clamp(26px, 3.2vw, 44px), 2.56vw); }
@media (min-width: 900px) {
  #foundation .section-heading h2.heading-accent,
  #help .section-heading h2.heading-accent { font-size: clamp(22px, 2.6vw, 38px); }
}

/* --- and the wrapping fallbacks on small screens --- */
@media (max-width: 430px) {
  #foundation .section-heading h2.heading-accent { font-size: clamp(19px, 5.3vw, 26px); }
}
@media (max-width: 620px) {
  #timeline .section-heading h3.heading-accent,
  #virtues .section-heading h3.heading-accent,
  #academic .section-heading h3.heading-accent,
  #thanks h2.heading-accent { font-size: clamp(19px, 5.1vw, 26px); }
}
@media (max-width: 760px) {
  #exit h2.heading-accent { font-size: clamp(19px, 3.5vw, 29px); }
}


/* ===================================================================
   EVERY HEADING CENTRED, WITHOUT EXCEPTION
   Most were centred already, but the rule is stated once here so no
   heading can be left out and none can be pulled off-centre by a rule
   further up the file.

   Two things are needed together, and only one of them is obvious:

     text-align: center      centres the line inside its box
     margin-left / right     centres the BOX itself, and max-width:100%
     : auto, max-width:100%  stops a narrow box from holding a wide line

   The second is the one that bit us repeatedly. A heading wider than
   its own box does not centre - it overflows to the right and merely
   looks pushed over. Setting the box to the full width of the section
   gives the centred line somewhere to sit. text-align-last matters for
   the headings that wrap, so their final line is centred too rather
   than left-aligned.
   =================================================================== */
.heading-accent,
h1.heading-accent, h2.heading-accent, h3.heading-accent,
.split-title,
.sacred-content h1, .sacred-content h2,
.section-heading h2, .section-heading h3,
.threshold-title, .farewell-blessing {
  text-align: center;
  text-align-last: center;
  margin-left: auto;
  margin-right: auto;
}
/* The box a heading sits in must be able to hold it - but only the
   sections whose heading is genuinely wider than 900px are widened.
   Widening every one would also let the wrapping sub-heading beneath
   run to a very long measure, which is harder to read, so the rest
   keep their comfortable column. (foundation, legacy, timeline,
   virtues, academic, moments and admin are already widened earlier;
   the remaining three hold headings that fit well inside 900px.) */
.sacred-content {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}


/* ===================================================================
   COMBINATION 216 — the pages, not the two screens
     Cormorant Unicase  ·  Didact Gothic  ·  EB Garamond

   Applied to every section of the site. The opening screen and the
   closing screen are deliberately untouched: they keep Playfair
   Display for their title and blessing, Cinzel for the names and EB
   Garamond for the dedication line. Those two are separate selectors
   (.threshold-title / .farewell-blessing), and neither screen uses
   .heading-accent, .eyebrow or .lede-italic, so nothing here reaches
   them.

   Cormorant Unicase is a UNICASE face: capitals and lowercase are
   drawn at a single height. That is what gives the headings their even,
   carved rhythm. It is also lighter and narrower than Playfair, so it
   sits at 600 rather than 400 and a little larger, or it would read
   faint beside the gold and the dark red.
   =================================================================== */
.heading-accent,
h1.heading-accent, h2.heading-accent, h3.heading-accent,
.split-title,
.section-heading h2, .section-heading h3 {
  font-family: 'Cormorant Unicase', 'Cormorant Garamond', Georgia, serif !important;
  font-weight: 600 !important;
  letter-spacing: 0.02em;
}
/* the label above each heading */
.eyebrow {
  font-family: 'Didact Gothic', system-ui, sans-serif;
  font-weight: 400;
  letter-spacing: 0.26em;
}
/* the italic sentence beneath each heading */
.lede-italic,
.section-heading p.lede-italic {
  font-family: 'EB Garamond', 'Cormorant Garamond', Georgia, serif;
}

/* --- sizes, re-fitted for the new face -----------------------------
   Cormorant Unicase runs narrower than Playfair, so the headings that
   are pinned to one line gain room and can come up in size; without
   this they would look noticeably smaller than before. The caps below
   are still checked against the longest line in each section. */
#timeline  .section-heading h3.heading-accent { font-size: min(clamp(28px, 3.6vw, 50px), 4.20vw); }
#virtues   .section-heading h3.heading-accent { font-size: min(clamp(28px, 3.6vw, 50px), 3.10vw); }
#academic  .section-heading h3.heading-accent { font-size: min(clamp(28px, 3.6vw, 50px), 2.78vw); }
#thanks    h2.heading-accent                  { font-size: min(clamp(28px, 3.6vw, 50px), 2.72vw); }
#exit      h2.heading-accent                  { font-size: min(clamp(28px, 3.6vw, 50px), 2.72vw); }
@media (min-width: 900px) {
  #foundation .section-heading h2.heading-accent,
  #help .section-heading h2.heading-accent { font-size: clamp(25px, 3.0vw, 44px); }
}


/* ===================================================================
   TWO HEADINGS HELD TO A FIXED NUMBER OF LINES

   a. "THE DIDOLKAR FAMILY LEGACY"  —  one line
      It was breaking because .sacred-content h2 is capped at 26ch, and
      a ch is the width of a "0", which is narrower than the average
      capital. 26 characters therefore needed more than 26ch and the
      last word dropped. The cap is lifted and the line pinned instead.
      Scoped to #sacred so the two closing headings, which share the
      .sacred-content class, are not affected.

   b. "GO FORTH ON PATHS PAVED WITH PURPOSE, / CARRYING A GRATEFUL
      SPIRIT AT HEART AND A MANTLE OF BLESSINGS."  —  exactly two lines
      The <br> already marks where the break belongs. Letting each half
      wrap freely is what produced three and four lines, so each half is
      now pinned and the size is set by the LONGER half: 62 characters,
      which needs about 2.43vw to fit the content column. 2.30vw is used,
      leaving roughly 5% of slack.

   Below 760px there is not enough width to hold those lines at a
   readable size, so both are allowed to wrap again.
   =================================================================== */
#sacred .sacred-content h2.heading-accent {
  white-space: nowrap;
  max-width: none;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}
#exit h2.heading-accent {
  white-space: nowrap;
  max-width: none;
  text-wrap: nowrap;
  font-size: min(clamp(28px, 3.6vw, 50px), 2.30vw);
  line-height: 1.45;
}
/* The exit heading gives up its two-line setting sooner than the other:
   holding 62 characters on one line below about 1000px would drive the
   type under 23px, which is too small for a closing blessing. Sacred
   holds its single line much further down, because 26 characters need
   far less room. */
@media (max-width: 1000px) {
  #exit h2.heading-accent {
    white-space: normal;
    text-wrap: balance;
    max-width: 26ch;
    font-size: clamp(21px, 3.6vw, 30px);
  }
}
@media (max-width: 620px) {
  #sacred .sacred-content h2.heading-accent {
    white-space: normal;
    text-wrap: balance;
    max-width: 20ch;
  }
}


/* ===================================================================
   THREE ALIGNMENT FIXES, ALL THE SAME UNDERLYING CAUSE

   A centred line only looks centred while it fits inside its own box.
   Once it is wider, it spills out of the right-hand edge and reads as
   pushed over — the alignment was never wrong, the box was too small.

   a. "THE DIDOLKAR FAMILY LEGACY"  — pinned to one line and given the
      full width of the section to centre within.

   b. "DOCTORATE, RESEARCH, SCHOLARSHIP & CAREER ARCHIVE" — an early
      rule caps this heading at 22ch, which is under half the length of
      the line. The cap is cleared here so the heading can occupy, and
      therefore centre within, the whole column.

   c. "Every strong family stands upon an unseen foundation..." — this
      one broke for a subtler reason. The four one-line sub-headings
      were capped in vw for Cormorant Garamond italic. Combination 216
      changed that face to EB Garamond, which is about 16% wider, so
      Foundation and Admin began to overflow. Every cap is recalculated
      here for the new face, with roughly 4% of slack.
   =================================================================== */

/* a + b — the headings */
#sacred .sacred-content h2.heading-accent,
#academic .section-heading h3.heading-accent {
  max-width: none !important;
  width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
  text-align: center !important;
  text-align-last: center !important;
}

/* c — the one-line sub-headings, re-capped for EB Garamond italic */
#foundation p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 1.34vw); }
#legacy     p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 1.56vw); }
#moments    p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 1.76vw); }
#admin      p.lede.lede-italic { font-size: min(clamp(23px, 2.4vw, 27px), 1.39vw); }
#foundation p.lede.lede-italic,
#legacy     p.lede.lede-italic,
#moments    p.lede.lede-italic,
#admin      p.lede.lede-italic {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
  text-align-last: center;
}


/* ===================================================================
   "THE DIDOLKAR FAMILY LEGACY" — ONE LINE, PROPERLY THIS TIME

   The previous attempt never took effect. That heading is an
   <h1 class="split-title heading-accent">, and the rule was written for
   h2 — so it matched nothing. Two things were holding the line back:

     .split-title { white-space: normal }   forces it to wrap
     .sacred-content h1.heading-accent      caps it at 20ch, far under
                                            the 26 characters it needs

   Both are overridden below by a selector specific enough to win
   wherever it sits in the file, and the size is capped in vw so the
   single line still fits on a phone rather than running off the edge.
   =================================================================== */
#sacred .sacred-content h1.heading-accent,
#sacred .sacred-content h1.split-title,
#sacred .sacred-content h2.heading-accent {
  white-space: nowrap !important;
  text-wrap: nowrap !important;
  max-width: none !important;
  width: 100%;
  margin-left: auto !important;
  margin-right: auto !important;
  text-align: center !important;
  text-align-last: center !important;
  /* 26 capitals of Cormorant Unicase need about 5.8vw to fit the
     content column; 5.2vw leaves roughly a tenth of the width spare,
     all the way down to a small phone. */
  font-size: min(clamp(22px, 3.6vw, 50px), 5.2vw) !important;
}


/* ===================================================================
   a. THE ACADEMIC HEADING, CENTRED FOR REAL
   Every previous attempt measured this line against the width of the
   section. That was the wrong yardstick: the heading lives inside
   .section-inner, which is capped at 1200px however wide the screen
   gets. At 50px the line runs to about 1470px, so it overflowed that
   column and sat pushed to the right - which is exactly what the
   screenshot shows. The size is now capped against 1200px instead.
   Virtues had the same fault and is corrected with it.
   =================================================================== */
#academic .section-heading h3.heading-accent {
  font-size: min(clamp(24px, 3.0vw, 38px), 2.85vw) !important;
}
#virtues .section-heading h3.heading-accent {
  font-size: min(clamp(26px, 3.3vw, 42px), 3.15vw) !important;
}
#foundation .section-heading h2.heading-accent {
  font-size: min(clamp(25px, 3.0vw, 42px), 3.05vw);
}

/* ===================================================================
   b. THE FOUNDATION SENTENCE, MATCHED TO THE SACRED ONE
   "Every strong family stands upon an unseen foundation..." now takes
   the same size formula as the opening dedication sentence, which is
   what was asked for. At that size 128 characters cannot sit on one
   line - it would need roughly 1480px inside a 1200px column - so the
   line is released to wrap and balance across two, centred, exactly as
   the sacred one does.
   =================================================================== */
#foundation p.lede.lede-italic {
  font-size: min(clamp(23px, 2.4vw, 27px), 1.73vw) !important;
  white-space: normal !important;
  text-wrap: balance !important;
  max-width: 62ch;
  margin-left: auto !important;
  margin-right: auto !important;
  text-align: center !important;
  text-align-last: center !important;
  line-height: 1.7;
}


/* ===================================================================
   HEADINGS THAT CENTRE THEMSELVES, WHATEVER THE FACE MEASURES

   The Academic, Virtues, Timeline and Help headings have each been
   "centred" more than once and kept drifting right. The reason was
   always the same: they are held on one line by white-space: nowrap,
   and I was sizing them from an ESTIMATE of how wide a capital is in
   this typeface. When the estimate ran low the line grew past its
   column and spilled out of the right-hand edge - and a line wider
   than its box cannot centre, whatever text-align says.

   Rather than keep guessing the metric, the single-line requirement is
   dropped for these four. Only the Sacred title and the Exit blessing
   were ever asked to hold a fixed number of lines; nobody asked for
   these. With text-wrap: balance they still sit on one line whenever
   they fit, and break into two balanced centred lines when they do
   not. Overflow becomes impossible, so they are centred at every width
   and at any font size - it can no longer be got wrong.
   =================================================================== */
#timeline  .section-heading h3.heading-accent,
#virtues   .section-heading h3.heading-accent,
#academic  .section-heading h3.heading-accent,
#foundation .section-heading h2.heading-accent,
#help      .section-heading h2.heading-accent,
#thanks    h2.heading-accent {
  white-space: normal !important;
  text-wrap: balance !important;
  max-width: 100% !important;
  width: auto !important;
  margin-left: auto !important;
  margin-right: auto !important;
  text-align: center !important;
  text-align-last: center !important;
  overflow-wrap: break-word;
}
/* the Exit blessing keeps its two lines, but with more room to spare */
#exit h2.heading-accent { font-size: min(clamp(28px, 3.6vw, 50px), 1.90vw); }


/* ===================================================================
   THE OPENING SCREEN TITLE — SAFFRON, IN POPPINS
   This applies only to .threshold-title, which exists nowhere but the
   start screen, so the closing screen and every page heading keep the
   faces they already have. Saffron is #ff9933; against the black and
   marigold background it is given a warm halo rather than the red-toned
   one it inherited, so the shadow supports the colour instead of
   muddying it. The face is Cormorant Unicase at 600 - exactly what the
   Sacred Dedication heading uses further down the page, so the same
   words are set the same way in both places. It is already loaded for
   every section heading, so this costs no extra download.
   =================================================================== */
.threshold-title {
  /* the same face, weight and tracking as the Sacred Dedication heading
     on the page below, so the title reads identically in both places */
  font-family: 'Cormorant Unicase', 'Cormorant Garamond', Georgia, serif !important;
  font-weight: 600 !important;
  color: #ff9933 !important;
  letter-spacing: 0.02em;
  text-shadow: 0 0 26px rgba(255, 153, 51, 0.55),
               0 0 60px rgba(255, 153, 51, 0.28),
               0 3px 9px rgba(0, 0, 0, 0.95),
               0 1px 3px rgba(0, 0, 0, 1) !important;
}


/* ===================================================================
   THE CLOSING SCREEN — 15 FIREWORKS, 284 SPARKS, EVERY ONE A
   DIFFERENT SIZE

   The 284 spread values are laid out evenly across the range and then
   shuffled, rather than drawn at random: random draws collide once
   rounded, and about eighty of them did. Constructing the list this way
   guarantees all 284 are genuinely distinct - from pinpoints under a
   pixel to embers of about nine.

   Size is carried by the box-shadow SPREAD, which costs nothing: the
   sparks are still shadows of one 3px dot per firework, so 284 sparks
   are 15 animated elements. One transform opens each whole burst and
   only transform and opacity animate, which is what keeps it smooth.

   The layer covers the closing screen corner to corner at z-index 0,
   beneath .farewell-inner, so every burst passes behind the names, the
   blessing and the buttons, and takes no pointer events.
   =================================================================== */
.fw-layer {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
}
.fw-layer i {
  position: absolute;
  width: 3px; height: 3px;
  border-radius: 50%;
  background: transparent;
  opacity: 0;
  transform: scale(0.06);
  animation-name: fwBurst;
  animation-timing-function: cubic-bezier(0.16, 0.84, 0.34, 1);
  animation-iteration-count: infinite;
}
/* 24 sparks, 62px across */
.fw-layer i:nth-child(1) {
  left: 65.4%; top: 21.8%;
  animation-duration: 5.75s; animation-delay: 7.97s;
  box-shadow:
    68.5px -9.7px 0 -0.065px #ff9933,
    48.7px 26.2px 0 -0.239px #ff9933,
    43.5px 46.8px 0 1.268px #ff9933,
    10.4px 58.7px 0 0.123px #ff9933,
    -8.7px 67.7px 0 2.021px #ff9933,
    -40.6px 56.3px 0 1.007px #ff9933,
    -51.6px 38.4px 0 0.761px #ff9933,
    -62.4px 20.0px 0 2.369px #ff9933,
    -60.9px -20.0px 0 2.731px #ff9933,
    -43.3px -36.7px 0 2.905px #ff9933,
    -26.5px -64.8px 0 1.674px #ff9933,
    3.6px -62.2px 0 0.225px #ff9933,
    23.5px -53.7px 0 2.326px #ff9933,
    52.0px -43.6px 0 0.703px #ff9933,
    48.6px -24.7px 0 1.529px #ff9933,
    31.7px 10.8px 0 -0.253px #fff1cd,
    13.5px 23.2px 0 0.268px #fff1cd,
    -0.8px 31.8px 0 0.138px #fff1cd,
    -22.9px 21.5px 0 -0.123px #fff1cd,
    -32.5px 3.9px 0 -0.413px #fff1cd,
    -25.2px -26.7px 0 -0.847px #fff1cd,
    -5.8px -32.3px 0 1.123px #fff1cd,
    12.3px -26.9px 0 0.558px #fff1cd,
    31.5px -8.8px 0 -0.456px #fff1cd;
}
/* 22 sparks, 95px across */
.fw-layer i:nth-child(2) {
  left: 43.7%; top: 82.5%;
  animation-duration: 6.44s; animation-delay: 5.86s;
  box-shadow:
    95.7px 11.2px 0 -0.876px #ffd45e,
    84.9px 34.4px 0 1.254px #ffd45e,
    55.6px 89.5px 0 1.225px #ffd45e,
    33.1px 98.6px 0 0.804px #ffd45e,
    -35.7px 88.4px 0 2.630px #ffd45e,
    -55.8px 66.5px 0 1.051px #ffd45e,
    -85.9px 40.1px 0 -0.036px #ffd45e,
    -82.1px -2.9px 0 0.616px #ffd45e,
    -78.1px -54.4px 0 -0.340px #ffd45e,
    -66.7px -78.1px 0 0.573px #ffd45e,
    -19.1px -92.5px 0 0.544px #ffd45e,
    23.7px -79.7px 0 -0.499px #ffd45e,
    58.6px -71.5px 0 -0.702px #ffd45e,
    101.4px -30.6px 0 1.413px #ffd45e,
    40.6px 13.2px 0 1.819px #fffaf0,
    23.6px 53.5px 0 0.239px #fffaf0,
    -15.3px 37.7px 0 2.557px #fffaf0,
    -47.4px 17.4px 0 1.601px #fffaf0,
    -31.4px -16.6px 0 2.499px #fffaf0,
    -22.7px -43.1px 0 1.659px #fffaf0,
    15.1px -42.3px 0 -0.427px #fffaf0,
    38.3px -12.5px 0 0.210px #fffaf0;
}
/* 20 sparks, 52px across */
.fw-layer i:nth-child(3) {
  left: 42.6%; top: 74.9%;
  animation-duration: 9.02s; animation-delay: 7.75s;
  box-shadow:
    47.7px -4.1px 0 1.862px #ff8a6b,
    44.2px 26.1px 0 2.413px #ff8a6b,
    27.3px 43.0px 0 2.615px #ff8a6b,
    -0.5px 47.3px 0 2.543px #ff8a6b,
    -22.2px 49.4px 0 -0.311px #ff8a6b,
    -46.8px 24.2px 0 2.007px #ff8a6b,
    -53.5px -6.4px 0 1.152px #ff8a6b,
    -38.6px -29.1px 0 1.167px #ff8a6b,
    -24.3px -51.4px 0 1.094px #ff8a6b,
    6.8px -49.1px 0 1.891px #ff8a6b,
    26.0px -52.4px 0 -0.644px #ff8a6b,
    48.5px -34.6px 0 0.891px #ff8a6b,
    18.1px 10.5px 0 1.456px #ffe0cf,
    6.6px 20.3px 0 0.877px #ffe0cf,
    -13.1px 23.7px 0 2.572px #ffe0cf,
    -22.8px 13.0px 0 1.109px #ffe0cf,
    -23.4px -5.4px 0 0.906px #ffe0cf,
    -7.0px -27.3px 0 0.297px #ffe0cf,
    11.1px -26.6px 0 0.442px #ffe0cf,
    19.4px -6.6px 0 0.949px #ffe0cf;
}
/* 18 sparks, 69px across */
.fw-layer i:nth-child(4) {
  left: 56.6%; top: 31.9%;
  animation-duration: 5.60s; animation-delay: 4.37s;
  box-shadow:
    59.8px -8.1px 0 2.224px #f7c26a,
    57.9px 38.7px 0 2.876px #f7c26a,
    24.6px 57.4px 0 0.920px #f7c26a,
    -2.3px 63.6px 0 1.630px #f7c26a,
    -58.2px 53.3px 0 -0.181px #f7c26a,
    -60.9px 8.6px 0 0.660px #f7c26a,
    -77.4px -10.6px 0 0.717px #f7c26a,
    -49.5px -55.7px 0 0.819px #f7c26a,
    -13.8px -67.3px 0 2.181px #f7c26a,
    28.6px -61.7px 0 0.529px #f7c26a,
    51.5px -30.6px 0 -0.442px #f7c26a,
    31.7px 18.0px 0 2.514px #fff2dc,
    8.8px 36.1px 0 2.152px #fff2dc,
    -16.2px 25.4px 0 0.935px #fff2dc,
    -42.5px 7.1px 0 -0.891px #fff2dc,
    -23.2px -33.7px 0 1.210px #fff2dc,
    6.9px -35.4px 0 2.311px #fff2dc,
    35.2px -10.3px 0 -0.079px #fff2dc;
}
/* 16 sparks, 111px across */
.fw-layer i:nth-child(5) {
  left: 81.1%; top: 66.6%;
  animation-duration: 9.18s; animation-delay: 2.44s;
  box-shadow:
    117.5px 16.5px 0 1.500px #ffcf7a,
    90.6px 79.9px 0 -0.528px #ffcf7a,
    41.9px 116.0px 0 0.515px #ffcf7a,
    -50.2px 105.8px 0 1.355px #ffcf7a,
    -102.4px 61.1px 0 2.818px #ffcf7a,
    -117.9px 3.8px 0 -0.601px #ffcf7a,
    -94.0px -49.2px 0 -0.224px #ffcf7a,
    -30.6px -90.8px 0 -0.297px #ffcf7a,
    30.2px -111.3px 0 0.964px #ffcf7a,
    85.5px -56.8px 0 0.486px #ffcf7a,
    44.4px 35.5px 0 1.297px #ffeed6,
    -3.1px 57.4px 0 -0.673px #ffeed6,
    -58.3px 36.7px 0 2.659px #ffeed6,
    -56.7px -38.2px 0 -0.543px #ffeed6,
    -9.9px -60.6px 0 -0.920px #ffeed6,
    39.1px -27.1px 0 2.601px #ffeed6;
}
/* 14 sparks, 50px across */
.fw-layer i:nth-child(6) {
  left: 44.5%; top: 28.2%;
  animation-duration: 5.94s; animation-delay: 3.64s;
  box-shadow:
    47.6px -4.9px 0 0.080px #ffab45,
    39.0px 24.6px 0 0.152px #ffab45,
    1.8px 50.2px 0 2.485px #ffab45,
    -28.2px 48.5px 0 -0.166px #ffab45,
    -53.5px 18.1px 0 1.616px #ffab45,
    -49.7px -20.8px 0 -0.818px #ffab45,
    -31.5px -40.0px 0 -0.934px #ffab45,
    11.2px -41.8px 0 0.283px #ffab45,
    38.2px -23.5px 0 1.848px #ffab45,
    17.5px 12.5px 0 2.891px #fff0d0,
    -7.8px 21.2px 0 1.239px #fff0d0,
    -25.6px 0.7px 0 1.065px #fff0d0,
    -8.3px -22.0px 0 0.051px #fff0d0,
    19.5px -13.6px 0 0.471px #fff0d0;
}
/* 22 sparks, 92px across */
.fw-layer i:nth-child(7) {
  left: 48.4%; top: 31.8%;
  animation-duration: 8.69s; animation-delay: 8.64s;
  box-shadow:
    78.9px -5.5px 0 0.775px #ff7a18,
    74.7px 28.3px 0 2.036px #ff7a18,
    69.7px 69.0px 0 1.717px #ff7a18,
    26.3px 98.3px 0 0.254px #ff7a18,
    -24.3px 76.2px 0 1.949px #ff7a18,
    -76.6px 68.8px 0 1.775px #ff7a18,
    -84.5px 57.1px 0 2.456px #ff7a18,
    -91.0px 2.2px 0 2.688px #ff7a18,
    -69.7px -48.7px 0 2.137px #ff7a18,
    -63.4px -80.9px 0 1.645px #ff7a18,
    -13.4px -89.8px 0 2.050px #ff7a18,
    37.6px -92.3px 0 -0.963px #ff7a18,
    53.2px -62.0px 0 1.934px #ff7a18,
    83.1px -35.8px 0 -0.760px #ff7a18,
    36.2px 10.9px 0 0.978px #ffe4c4,
    23.1px 40.4px 0 0.384px #ffe4c4,
    -15.6px 39.3px 0 1.920px #ffe4c4,
    -46.8px 20.9px 0 -0.470px #ffe4c4,
    -48.3px -30.4px 0 1.543px #ffe4c4,
    -19.6px -37.5px 0 1.732px #ffe4c4,
    21.8px -41.0px 0 2.340px #ffe4c4,
    44.5px -22.6px 0 1.905px #ffe4c4;
}
/* 20 sparks, 76px across */
.fw-layer i:nth-child(8) {
  left: 9.9%; top: 6.8%;
  animation-duration: 8.84s; animation-delay: 0.37s;
  box-shadow:
    78.4px 2.4px 0 -0.485px #ffb27a,
    56.6px 39.5px 0 2.862px #ffb27a,
    49.1px 70.4px 0 1.384px #ffb27a,
    -11.8px 82.8px 0 1.746px #ffb27a,
    -23.8px 61.9px 0 3.007px #ffb27a,
    -61.0px 41.9px 0 2.775px #ffb27a,
    -67.2px -2.8px 0 0.631px #ffb27a,
    -57.2px -34.1px 0 2.934px #ffb27a,
    -48.9px -62.5px 0 2.978px #ffb27a,
    1.3px -78.4px 0 1.804px #ffb27a,
    34.7px -66.7px 0 -0.152px #ffb27a,
    58.8px -42.1px 0 1.340px #ffb27a,
    34.3px 17.7px 0 -0.717px #ffe8c4,
    21.8px 37.9px 0 -0.775px #ffe8c4,
    -15.3px 29.1px 0 1.963px #ffe8c4,
    -36.6px 18.8px 0 2.108px #ffe8c4,
    -39.1px -18.6px 0 2.586px #ffe8c4,
    -16.8px -42.5px 0 0.167px #ffe8c4,
    11.6px -30.2px 0 1.688px #ffe8c4,
    27.3px -7.4px 0 -0.094px #ffe8c4;
}
/* 18 sparks, 99px across */
.fw-layer i:nth-child(9) {
  left: 55.5%; top: 31.2%;
  animation-duration: 8.48s; animation-delay: 0.17s;
  box-shadow:
    98.7px 2.2px 0 0.587px #ff9933,
    75.4px 55.5px 0 1.427px #ff9933,
    28.7px 82.6px 0 -0.514px #ff9933,
    -4.7px 103.6px 0 -0.905px #ff9933,
    -50.7px 78.8px 0 1.703px #ff9933,
    -102.3px 21.7px 0 1.080px #ff9933,
    -87.3px -18.8px 0 1.181px #ff9933,
    -72.8px -76.3px 0 2.644px #ff9933,
    -16.6px -87.0px 0 -0.369px #ff9933,
    48.4px -88.8px 0 2.427px #ff9933,
    101.3px -46.3px 0 -0.978px #ff9933,
    43.1px 28.8px 0 0.066px #fff1cd,
    12.9px 43.0px 0 1.138px #fff1cd,
    -39.5px 36.8px 0 2.833px #fff1cd,
    -45.3px 2.1px 0 2.094px #fff1cd,
    -28.6px -33.3px 0 2.123px #fff1cd,
    13.7px -54.3px 0 1.514px #fff1cd,
    44.3px -15.4px 0 1.992px #fff1cd;
}
/* 16 sparks, 47px across */
.fw-layer i:nth-child(10) {
  left: 44.8%; top: 6.2%;
  animation-duration: 8.65s; animation-delay: 2.09s;
  box-shadow:
    51.3px 4.5px 0 -0.630px #ffd45e,
    34.3px 21.2px 0 2.166px #ffd45e,
    14.3px 44.8px 0 1.326px #ffd45e,
    -17.4px 49.6px 0 1.978px #ffd45e,
    -42.3px 27.5px 0 -0.572px #ffd45e,
    -46.6px 7.0px 0 1.587px #ffd45e,
    -30.7px -27.3px 0 0.833px #ffd45e,
    -21.9px -44.5px 0 0.602px #ffd45e,
    17.9px -37.0px 0 2.920px #ffd45e,
    35.0px -23.0px 0 1.022px #ffd45e,
    19.0px 13.2px 0 1.471px #fffaf0,
    2.5px 25.7px 0 -0.398px #fffaf0,
    -19.4px 9.0px 0 0.746px #fffaf0,
    -19.3px -15.7px 0 0.674px #fffaf0,
    0.3px -26.6px 0 2.702px #fffaf0,
    22.4px -9.0px 0 -1.021px #fffaf0;
}
/* 24 sparks, 47px across */
.fw-layer i:nth-child(11) {
  left: 7.3%; top: 59.4%;
  animation-duration: 6.96s; animation-delay: 5.54s;
  box-shadow:
    51.5px 2.9px 0 -0.688px #ff8a6b,
    44.5px 25.6px 0 1.558px #ff8a6b,
    30.0px 43.7px 0 -0.137px #ff8a6b,
    12.4px 45.6px 0 -0.557px #ff8a6b,
    -8.9px 50.1px 0 2.760px #ff8a6b,
    -21.9px 40.4px 0 2.355px #ff8a6b,
    -36.7px 34.1px 0 2.949px #ff8a6b,
    -45.3px 1.9px 0 -1.007px #ff8a6b,
    -42.9px -4.1px 0 0.181px #ff8a6b,
    -36.4px -25.1px 0 0.095px #ff8a6b,
    -14.7px -38.5px 0 -0.731px #ff8a6b,
    -7.1px -41.3px 0 0.196px #ff8a6b,
    18.0px -47.3px 0 -0.050px #ff8a6b,
    24.1px -33.4px 0 1.442px #ff8a6b,
    43.6px -20.2px 0 1.833px #ff8a6b,
    15.3px 8.1px 0 -0.326px #ffe0cf,
    11.5px 15.7px 0 1.283px #ffe0cf,
    -5.6px 22.1px 0 2.268px #ffe0cf,
    -13.7px 14.2px 0 2.963px #ffe0cf,
    -21.9px -1.9px 0 0.500px #ffe0cf,
    -15.2px -15.8px 0 0.413px #ffe0cf,
    -1.1px -26.3px 0 2.442px #ffe0cf,
    14.2px -23.7px 0 0.341px #ffe0cf,
    19.3px -9.2px 0 0.689px #ffe0cf;
}
/* 20 sparks, 94px across */
.fw-layer i:nth-child(12) {
  left: 77.3%; top: 88.3%;
  animation-duration: 8.01s; animation-delay: 1.75s;
  box-shadow:
    90.7px 9.3px 0 -0.862px #f7c26a,
    81.5px 48.1px 0 -0.282px #f7c26a,
    60.6px 88.5px 0 -0.268px #f7c26a,
    -4.7px 87.2px 0 -1.050px #f7c26a,
    -32.7px 87.6px 0 1.485px #f7c26a,
    -86.1px 54.9px 0 2.195px #f7c26a,
    -96.8px -7.0px 0 -0.992px #f7c26a,
    -78.2px -33.7px 0 2.282px #f7c26a,
    -48.5px -73.3px 0 0.370px #f7c26a,
    11.9px -93.9px 0 0.022px #f7c26a,
    57.9px -90.3px 0 2.079px #f7c26a,
    78.4px -54.0px 0 2.239px #f7c26a,
    44.3px 12.4px 0 0.428px #fff2dc,
    16.2px 55.5px 0 -0.659px #fff2dc,
    -11.2px 43.6px 0 1.790px #fff2dc,
    -40.0px 23.4px 0 1.036px #fff2dc,
    -45.2px -27.6px 0 -0.804px #fff2dc,
    -22.9px -46.2px 0 2.789px #fff2dc,
    21.6px -36.6px 0 0.008px #fff2dc,
    42.5px -22.8px 0 0.848px #fff2dc;
}
/* 18 sparks, 78px across */
.fw-layer i:nth-child(13) {
  left: 19.4%; top: 4.9%;
  animation-duration: 7.08s; animation-delay: 6.28s;
  box-shadow:
    84.4px -9.9px 0 -0.210px #ffcf7a,
    64.3px 35.7px 0 1.761px #ffcf7a,
    39.8px 61.8px 0 2.804px #ffcf7a,
    -10.8px 81.7px 0 -0.108px #ffcf7a,
    -50.5px 64.3px 0 1.572px #ffcf7a,
    -68.9px 30.7px 0 2.528px #ffcf7a,
    -84.6px -23.7px 0 2.065px #ffcf7a,
    -45.9px -66.8px 0 3.036px #ffcf7a,
    -22.8px -71.5px 0 0.993px #ffcf7a,
    34.5px -63.2px 0 0.457px #ffcf7a,
    65.5px -40.1px 0 -0.021px #ffcf7a,
    47.9px 13.9px 0 2.673px #ffeed6,
    12.3px 36.6px 0 0.645px #ffeed6,
    -22.2px 30.3px 0 2.847px #ffeed6,
    -38.6px 2.1px 0 1.398px #ffeed6,
    -24.8px -26.3px 0 -1.036px #ffeed6,
    7.7px -45.8px 0 0.790px #ffeed6,
    36.4px -14.2px 0 0.037px #ffeed6;
}
/* 16 sparks, 50px across */
.fw-layer i:nth-child(14) {
  left: 28.1%; top: 34.4%;
  animation-duration: 8.07s; animation-delay: 4.58s;
  box-shadow:
    45.0px -6.3px 0 1.311px #ffab45,
    46.7px 25.0px 0 -0.195px #ffab45,
    11.1px 44.6px 0 0.862px #ffab45,
    -15.3px 53.1px 0 0.399px #ffab45,
    -37.0px 25.1px 0 2.210px #ffab45,
    -45.4px 4.2px 0 1.369px #ffab45,
    -43.1px -23.6px 0 2.384px #ffab45,
    -25.0px -50.6px 0 -0.833px #ffab45,
    14.4px -48.5px 0 0.732px #ffab45,
    45.4px -29.7px 0 2.992px #ffab45,
    18.6px 13.7px 0 -0.789px #fff0d0,
    0.6px 19.9px 0 -0.615px #fff0d0,
    -28.9px 14.2px 0 -0.586px #fff0d0,
    -19.5px -12.7px 0 -0.949px #fff0d0,
    -1.4px -24.5px 0 0.109px #fff0d0,
    19.0px -14.7px 0 3.021px #fff0d0;
}
/* 16 sparks, 91px across */
.fw-layer i:nth-child(15) {
  left: 72.6%; top: 38.6%;
  animation-duration: 8.85s; animation-delay: 7.97s;
  box-shadow:
    98.3px 9.7px 0 1.877px #ff7a18,
    64.4px 50.6px 0 -0.746px #ff7a18,
    22.3px 98.2px 0 -0.384px #ff7a18,
    -28.2px 82.1px 0 -0.007px #ff7a18,
    -65.9px 47.9px 0 1.196px #ff7a18,
    -102.6px -7.5px 0 2.253px #ff7a18,
    -77.3px -56.8px 0 -0.355px #ff7a18,
    -16.5px -81.2px 0 0.326px #ff7a18,
    42.1px -83.8px 0 2.470px #ff7a18,
    59.5px -53.4px 0 2.746px #ff7a18,
    44.3px 20.7px 0 0.312px #ffe4c4,
    3.0px 51.2px 0 0.355px #ffe4c4,
    -36.8px 21.9px 0 2.717px #ffe4c4,
    -48.9px -20.7px 0 3.050px #ffe4c4,
    0.9px -43.7px 0 2.398px #ffe4c4,
    54.0px -21.9px 0 2.297px #ffe4c4;
}
@keyframes fwBurst {
  0%   { transform: scale(0.06) translateY(0);     opacity: 0; }
  5%   { opacity: 1; }
  40%  { transform: scale(0.80) translateY(4px);   opacity: 0.9; }
  70%  { transform: scale(1.04) translateY(14px);  opacity: 0.42; }
  100% { transform: scale(1.18) translateY(30px);  opacity: 0; }
}
@keyframes nameFlare {
  0%, 100% { text-shadow: 0 0 12px rgba(255,193,74,.42), 0 2px 6px rgba(0,0,0,.96); }
  10%      { text-shadow: 0 0 10px #fff6d8, 0 0 30px rgba(255,241,205,.9),
                          0 0 70px rgba(255,153,51,.6), 0 2px 6px rgba(0,0,0,.96); }
  45%      { text-shadow: 0 0 16px rgba(255,220,149,.66), 0 2px 6px rgba(0,0,0,.96); }
}
.farewell-names {
  position: relative;
  animation: riseIn 1.3s var(--ease-soft) 3.0s both,
             nameGlow 5.2s ease-in-out 4.3s infinite,
             nameFlare 6.3s ease-in-out 4.6s infinite !important;
}
@media (prefers-reduced-motion: reduce) {
  .fw-layer { display: none !important; }
  .farewell-names { animation: none !important; }
}


/* ===================================================================
   THE NAMES ON THE OPENING SCREEN — INSTRUMENT SERIF
   .threshold-names and .farewell-names share one rule further up, so
   this scopes the change to the opening screen alone; the closing
   screen keeps Cinzel, which suits the fireworks behind it.

   Abril Fatface is drawn at one weight only, and it is already very
   heavy. An earlier rule asks these names for 900 and adds a synthetic
   stroke on top; both are cleared here, or the browser would thicken an
   already fat didone into a blur.
   =================================================================== */
.threshold-names {
  font-family: 'Abril Fatface', 'Cormorant Garamond', Georgia, serif !important;
  font-weight: 400 !important;          /* the only weight it has */
  font-style: normal !important;
  letter-spacing: 0.02em;               /* a fat didone needs less air, not more */
  -webkit-text-stroke: 0 !important;    /* no faux bold over an already heavy face */
  /* two points larger than before: 32->34 and 37->39, and the vw arm
     lifted by the same 2px so the increase holds at every width */
  font-size: min(clamp(34px, 1.6vw, 39px), calc(1.58vw + 5px)) !important;
}


/* ===================================================================
   THE TWO ROLES UNDER EACH NAME, REVEALED ON REQUEST
   "Mother · Scholar · Guide" and "Father · Scholar · Public Servant"
   stay out of sight until the reader chooses one of the five virtues,
   then fade in with the reflection. Pressing the same virtue again, or
   scrolling past so the section closes itself, hides them once more.

   They are collapsed with grid-template-rows rather than display:none
   so the reveal can be animated, and aria-hidden keeps them out of a
   screen reader's way while they are closed.
   =================================================================== */
.parent-role {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  margin: 0;
  transition: grid-template-rows 0.5s var(--ease-soft),
              opacity 0.4s var(--ease-soft),
              margin 0.5s var(--ease-soft);
}
.parent-role > * { overflow: hidden; }
#virtues.virtue-chosen .parent-role {
  grid-template-rows: 1fr;
  opacity: 1;
  margin-top: 4px;
}
@media (prefers-reduced-motion: reduce) {
  .parent-role { transition: none; }
}


/* ===================================================================
   ===================================================================
   THE OPENING AND CLOSING SCREENS
   ===================================================================

   This block is last in the file on purpose. Both screens were built up
   over many passes and carry a lot of earlier rules; rather than hunt
   each one down, everything the two screens need is stated here, once,
   where it wins. Later rules of equal weight beat earlier ones, and
   !important is used only where an earlier rule already used it.

   ORDER ON BOTH SCREENS
     1 diya          2 statement     3 ornamental divider
     4 names         5 heading       6 buttons

   The two screens are deliberately identical in composition, type and
   colour. Only the words differ, plus the fireworks on the way out:
   leaving should feel like arriving.

   THE COLOUR RAMP - measured against the background #0a0806
     names       #ffdc95   15.2:1   brightest, so the eye lands here
     heading     #f0b45a   10.8:1
     statement   #c99a5e    7.4:1   quietest
   The old saffron #ff9933 is retired from these screens: it sits more
   orange than everything near it and breaks the ramp.

   THE ARRIVAL - the clock is shared with arriveWords() in app.js
     0.45s statement, word by word      2.05s names settle out of blur
     1.05s hairlines draw outward       3.30s heading, then gold sweep
     1.35s lotus blooms open            4.05s buttons
   =================================================================== */

/* --- three families of type, nothing else on these screens. --------
   Cinzel now carries the names on their own - an inscriptional,
   ceremonial capital face, distinct from the Cormorant Unicase used
   for the heading and buttons one step below it. EB Garamond italic
   remains for the statement. */
#threshold .threshold-names, .farewell .farewell-names {
  font-family: 'Cinzel', 'Cormorant Unicase', Georgia, serif !important;
  font-weight: 600 !important;
  -webkit-text-stroke: 0 !important;
}
#threshold .threshold-title, .farewell .farewell-title {
  font-family: 'Cormorant Unicase', 'Cormorant Garamond', Georgia, serif !important;
  font-weight: 600 !important;
  -webkit-text-stroke: 0 !important;
}
#threshold .threshold-line, .farewell .farewell-line {
  font-family: 'EB Garamond', 'Cormorant Garamond', Georgia, serif !important;
  font-style: italic !important;
  font-weight: 400 !important;
}

/* --- 2. THE STATEMENT — first among the words, and the quietest ----
   The old rule held this on one line with white-space:nowrap and made
   it the largest text on the screen. Both are undone here: a line that
   is wider than its box cannot centre, it can only overflow, so the
   statement is allowed to wrap and can no longer run off the edge.
   Raised again and given more weight - it was reported barely visible
   against the lamp's own glow. */
/* the dedication now carries one manual break, splitting it at its own
   comma into two roughly even clauses - a hard 2-line shape that holds
   regardless of exactly how "balance" would have wrapped it on its
   own. The box is widened well past the old 900px so each clause has
   room to sit on a single line without re-wrapping a second time. */
#threshold .threshold-line, .farewell .farewell-line {
  margin: 6px auto 0 !important;
  max-width: min(86vw, 1180px) !important;
  font-size: clamp(19px, 2.05vw, 34px) !important;
  font-weight: 500 !important;
  line-height: 1.5 !important;
  white-space: normal !important;
  text-wrap: balance;
  color: #ddb078 !important;
  text-shadow: 0 2px 9px rgba(24, 8, 18, 0.95), 0 0 22px rgba(201, 154, 94, 0.24) !important;
  animation: none !important;   /* the words carry their own arrival */
}

/* --- 4. THE NAMES — in the middle, directly below the ornament -----
   Held to one line now rather than wrapping - white-space:nowrap plus
   a font-size keyed mainly to viewport width (2.3vw) so the line keeps
   fitting its 80vw box at any width rather than jumping between fixed
   steps. Flanking the names, two small eight-point stars twinkle in
   place of a plain divider - a celestial mark either side rather than
   punctuation - while the glow itself now breathes through three
   tones in turn: warm gold, then a cool starlit white, then a whisper
   of sindoor rose, so the shimmer reads as more than one colour of
   light rather than a single hue pulsing brighter and dimmer. Cinzel's
   letters are more open than Cormorant Unicase's, so the tracking is
   widened slightly to suit it. text-shadow is inherited, so animating
   it here reaches the individual word spans without touching the
   per-word arrival animation they already carry. */
#threshold .threshold-names, .farewell .farewell-names {
  position: relative;
  margin: 0 auto !important;
  max-width: 92vw !important;
  padding: 0 1.3em !important;
  white-space: nowrap !important;
  font-size: clamp(11px, 2.3vw, 27px) !important;
  letter-spacing: 0.045em !important;
  line-height: 1.3 !important;
  text-wrap: nowrap;
  color: #ffdc95 !important;
  text-shadow: 0 0 22px rgba(255, 196, 110, 0.42),
               0 0 58px rgba(255, 160, 60, 0.22),
               0 2px 8px rgba(24, 8, 18, 0.85) !important;
  animation: nmShimmer 6.6s ease-in-out 3.4s infinite !important;
}
@keyframes nmShimmer {
  0%, 100%  { text-shadow: 0 0 22px rgba(255, 196, 110, 0.42),
                           0 0 58px rgba(255, 160, 60, 0.22),
                           0 2px 8px rgba(24, 8, 18, 0.85); }
  33%       { text-shadow: 0 0 26px rgba(255, 246, 224, 0.68),
                           0 0 64px rgba(255, 210, 150, 0.34),
                           0 2px 8px rgba(24, 8, 18, 0.85); }
  66%       { text-shadow: 0 0 24px rgba(255, 150, 140, 0.5),
                           0 0 60px rgba(226, 90, 70, 0.28),
                           0 2px 8px rgba(24, 8, 18, 0.85); }
}
#threshold .threshold-names::before, #threshold .threshold-names::after,
.farewell .farewell-names::before,   .farewell .farewell-names::after {
  content: '';
  position: absolute; top: 50%; width: 7px; height: 7px;
  background: linear-gradient(135deg, #fff8e2 0%, #ffd98a 55%, #e0913f 100%);
  clip-path: polygon(50% 0%, 63% 37%, 100% 50%, 63% 63%, 50% 100%, 37% 63%, 0% 50%, 37% 37%);
  box-shadow: 0 0 7px rgba(255, 216, 150, 0.9), 0 0 16px rgba(255, 180, 90, 0.5);
  animation: nmStarTwinkle 3.4s ease-in-out infinite;
}
#threshold .threshold-names::before, .farewell .farewell-names::before {
  left: 0; transform: translateY(-50%) rotate(0deg);
}
#threshold .threshold-names::after, .farewell .farewell-names::after {
  right: 0; transform: translateY(-50%) rotate(0deg); animation-delay: 1.6s;
}
@keyframes nmStarTwinkle {
  0%, 100% { opacity: 0.3;  transform: translateY(-50%) scale(0.65) rotate(0deg); }
  50%      { opacity: 1;    transform: translateY(-50%) scale(1.25) rotate(180deg); }
}

/* --- 5. THE HEADING — at the foot, just above the buttons ----------
   The gold sweep is clipped to the glyphs, which means the letters
   themselves are transparent; any text-shadow would show through as a
   silhouette, so it is removed here rather than left to fight. */
#threshold .threshold-title, .farewell .farewell-title {
  margin: 0 auto !important;
  max-width: min(88vw, 1200px);
  /* +2pt again over the swapped-in size; text-wrap:balance plus
     white-space:normal (not nowrap) is what lets this settle onto
     whatever number of lines - one or two - the text itself needs,
     rather than forcing either. */
  font-size: clamp(28px, 3.3vw, 62px) !important;
  letter-spacing: 0.075em !important;
  line-height: 1.25 !important;
  text-transform: uppercase !important;
  white-space: normal !important;
  text-wrap: balance;
  text-align: center !important;
  color: #f0b45a !important;
  text-shadow: none !important;
  /* THE BUG THAT MADE THIS A SOLID BAR: the "background" SHORTHAND,
     even set to just a gradient, resets every background-* sub-property
     it doesn't mention - including background-clip - back to its
     initial value (border-box). Because that reset rides on the same
     !important as the shorthand itself, the background-clip:text two
     lines below could never win against it, no matter what order they
     came in. The gradient painted the whole box solid instead of
     clipping to the letters - which is exactly the "thick line" this
     was reported as. Using background-image (a longhand) instead never
     touches background-clip at all, which is also why the buttons'
     identical technique, built with background-image from the start,
     never had this problem. */
  background-image: linear-gradient(100deg, #f0b45a 0%, #f0b45a 38%,
              #fff0cf 50%, #f0b45a 62%, #f0b45a 100%) !important;
  background-size: 280% 100% !important;
  -webkit-background-clip: text !important; background-clip: text !important;
  -webkit-text-fill-color: transparent !important;
  opacity: 0;
  animation: seRise 1.3s var(--ease-soft) 3.30s forwards,
             seSweep 9s linear 4.80s infinite !important;
}
@keyframes seRise  { from { opacity: 0; transform: translateY(12px); }
                     to   { opacity: 1; transform: none; } }
@keyframes seSweep { 0%   { background-position: 180% 0; }
                     100% { background-position: -80% 0; } }

/* --- the spacing between the parts ---------------------------------- */
#threshold .diya-scene, .farewell .diya-scene { margin-bottom: 4px !important; }
#threshold .threshold-names, .farewell .farewell-names { margin: 16px auto 22px !important; }
#threshold .threshold-title, .farewell .farewell-title { margin-bottom: 28px !important; }

/* the vine draws itself across the full width in one motion */
.orn-dr {
  stroke-dasharray: var(--orn-len);
  stroke-dashoffset: var(--orn-len);
  animation: ornDraw 1.6s var(--ease-soft) 1.0s forwards;
}
@keyframes ornDraw { to { stroke-dashoffset: 0; } }

/* each flower opens out from its own bindu, all identically sized so
   the rhythm along the vine is even, with a small extra twist on the
   way in for more life than a plain scale-up. transform-box lets an
   SVG group scale about its own centre instead of the canvas origin. */
.orn-lotus {
  transform-box: fill-box; transform-origin: center;
  opacity: 0; transform: scale(0.28) rotate(-30deg);
  animation: ornBloom 1.3s var(--ease-soft) forwards;
}
@keyframes ornBloom {
  60%  { opacity: 1; transform: scale(1.12) rotate(6deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

.orn-bindu {
  transform-box: fill-box; transform-origin: center;
  opacity: 0;
  animation: ornBinduIn 0.45s var(--ease-soft) forwards,
             ornBindu 3.6s var(--ease-soft) 3s infinite;
}
@keyframes ornBinduIn { to { opacity: 1; } }
@keyframes ornBindu { 0%, 100% { opacity: 0.6;  transform: scale(0.85); }
                      50%      { opacity: 1;    transform: scale(1.65); } }

/* the leaves unfurl from the vine, then sway very gently - the sway
   delay is set per leaf inline so the four of them never move in
   lockstep. transform-origin sits at the base of the leaf (0,0), where
   it meets the vine, so it swings rather than swelling in place. */
.orn-leaf {
  transform-box: fill-box; transform-origin: 0 0;
  opacity: 0;
  animation: ornLeafIn 0.6s var(--ease-soft) forwards,
             ornLeafSway 4.6s ease-in-out var(--leaf-sway-delay, 3.4s) infinite;
}
@keyframes ornLeafIn   { to { opacity: 0.9; } }
@keyframes ornLeafSway { 0%, 100% { transform: rotate(-3.5deg); }
                         50%      { transform: rotate(3.5deg); } }

/* a brighter dash of light that keeps travelling the full length of the
   vine, threading through all five flowers, plus a second, dimmer one
   a third of the way behind it - two points of shine rather than one,
   so the line never sits dark for long between passes */
.orn-shine {
  stroke-width: 2 !important;
  stroke-dasharray: 40 1300; opacity: 0;
  animation: ornShineIn 0.55s linear 2.6s forwards,
             ornTravel 6.5s linear 2.6s infinite;
}
.orn-shine-2 {
  stroke-width: 1.3 !important;
  stroke-dasharray: 26 1300; opacity: 0;
  animation: ornShineIn 0.55s linear 3.3s forwards,
             ornTravel2 6.5s linear 1.3s infinite;
}
@keyframes ornShineIn { to { opacity: 0.95; } }
/* declared path length 628, so each dash starts fully hidden past the
   far end (628 + its own dash size) and finishes fully hidden past the
   near end (-its own dash size), for a clean full traversal */
@keyframes ornTravel  { from { stroke-dashoffset: 668; } to { stroke-dashoffset: -40; } }
@keyframes ornTravel2 { from { stroke-dashoffset: 654; } to { stroke-dashoffset: -26; } }

/* ===================================================================
   THE WORDS ARRIVING
   app.js wraps each word in one of these two spans and sets its own
   delay, so a long line still arrives in a steady sweep.
   =================================================================== */
#threshold .aw, .farewell .aw {
  display: inline-block; opacity: 0;
  animation: awWord 0.8s var(--ease-soft) forwards;
}
@keyframes awWord { from { opacity: 0; transform: translateY(6px); }
                    to   { opacity: 1; transform: none; } }

/* the names settle out of a soft blur - slower and heavier than the
   statement, because they are the thing the screen is actually for */
#threshold .nw, .farewell .nw {
  display: inline-block; opacity: 0;
  filter: blur(9px); transform: translateY(14px);
  animation: nwSettle 1.5s var(--ease-soft) forwards;
}
@keyframes nwSettle { to { opacity: 1; filter: blur(0); transform: none; } }

/* ===================================================================
   THE LIGHT — chakra and padma, turning against each other
   Reported back barely visible: the parent's own opacity (0.26) and
   the twinkle's floor (0.7) were multiplying together, so the two
   mandalas never actually reached above about a quarter-strength.
   Both are raised substantially here, each given a brighter, more
   saturated version of its own hue and a matching glow, and the
   twinkle amplitude is widened so the sparkle is unmistakable.

   Each corner stacks two mandalas: the wheel (mChakra, underneath, the
   larger outer ring) and the lotus (mPadma, on top, inset within it -
   the inner mandala). Reported still not prominent enough, the inner
   one is now given more presence twice over: a deep red in place of
   the saffron, closer to sindoor/kumkum than to rust, and a slightly
   stronger glow than its outer partner so it reads as the brighter of
   the two. The chakra stays dark gold and turns clockwise; the padma
   turns counter-clockwise - two things in quiet opposition, each
   breathing a slow scale alongside its rotation and twinkling on its
   own independent cycle.
   =================================================================== */
#threshold .t-mandala, .farewell .t-mandala {
  opacity: 0.96 !important;
  position: absolute;   /* .t-mandala is already absolute; restated so the sparks below have a positioned ancestor */
}
/* the direction was reported as not opposing after all: an old rule
   further up the file (.tm-under and .tm--bl .tm-art) sets
   animation-direction: reverse with !important, and because that is a
   longhand riding on the same weight as this block's own animation
   shorthand, it kept winning regardless of source order - the same
   class of bug as the heading's background-clip earlier. Pinning
   animation-direction as its own !important declaration here, on both
   layers, is what actually settles it: the chakra now always plays
   seTurnA forward (clockwise) and the padma always plays seTurnB
   forward (its own keyframes already count backward through negative
   degrees, which reads as counter-clockwise) - in every corner. */
#threshold .tm-art.tm-under, .farewell .tm-art.tm-under {
  color: #e0b64a !important;                     /* the chakra, lit dark gold - raised a shade brighter */
  filter: drop-shadow(0 0 10px rgba(224, 182, 74, 0.95))
          drop-shadow(0 0 26px rgba(255, 214, 120, 0.62)) !important;
  animation: seTurnA 90s linear infinite,
             seTwinkleGold 4.4s ease-in-out infinite;
  animation-direction: normal !important;
}
#threshold .tm-art:not(.tm-under), .farewell .tm-art:not(.tm-under) {
  color: #d6473c !important;                     /* the padma, inner mandala - deep red, brighter still */
  filter: drop-shadow(0 0 11px rgba(214, 71, 60, 1))
          drop-shadow(0 0 28px rgba(255, 120, 98, 0.7)) !important;
  animation: seTurnB 74s linear infinite,
             seTwinkleSaffron 5.6s ease-in-out 1.1s infinite;
  animation-direction: normal !important;
}
/* the top-right pair (tm--tr) turns clockwise outer / counter-clockwise
   inner; the bottom-left pair (tm--bl) is given the same opposition but
   at different speeds so the two corners are never seen to move in
   lockstep with each other, only within themselves */
.tm--bl .tm-art.tm-under      { animation-duration: 105s; }
.tm--bl .tm-art:not(.tm-under) { animation-duration: 86s; }
@keyframes seTurnA {
  0%   { transform: rotate(0deg)    scale(1); }
  50%  { transform: rotate(180deg)  scale(1.09); }
  100% { transform: rotate(360deg)  scale(1); }
}
@keyframes seTurnB {
  0%   { transform: rotate(0deg)    scale(1); }
  50%  { transform: rotate(-180deg) scale(1.09); }
  100% { transform: rotate(-360deg) scale(1); }
}
@keyframes seTwinkleGold    { 0%, 100% { opacity: 0.68; } 50% { opacity: 1; } }
@keyframes seTwinkleSaffron { 0%, 100% { opacity: 0.8; }  50% { opacity: 1; } }   /* the inner mandala's floor is raised - it should never dim as far as its partner */

/* twelve small celestial sparks scattered around each mandala's rim -
   two pseudo-elements were never going to be enough for twelve, so
   these are real spans, built once at runtime by scatterMandalaSparks()
   in app.js and positioned here by the inline left/top it sets on
   each one. Three radii in rotation (44%/49%/53%) keep them from
   forming one too-perfect ring. */
.tm-sparks { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.tm-spark2 {
  position: absolute; width: 7px; height: 7px;
  transform: translate(-50%, -50%);
  background: linear-gradient(135deg, #fff8e2 0%, #ffd98a 55%, #e0913f 100%);
  clip-path: polygon(50% 0%, 63% 37%, 100% 50%, 63% 63%, 50% 100%, 37% 63%, 0% 50%, 37% 37%);
  box-shadow: 0 0 8px rgba(255, 220, 160, 0.95), 0 0 18px rgba(255, 180, 100, 0.55);
  animation: tmSparkTwinkle 3.8s ease-in-out infinite;
}
.tm-spark2:nth-child(3n)   { width: 5px; height: 5px; }
.tm-spark2:nth-child(4n+1) { width: 9px; height: 9px; }
@keyframes tmSparkTwinkle {
  0%, 100% { opacity: 0.2;  transform: translate(-50%, -50%) scale(0.55) rotate(0deg); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.3) rotate(180deg); }
}
@media (max-width: 700px) {
  .tm-spark2 { width: 5px; height: 5px; }
  .tm-spark2:nth-child(3n)   { width: 4px; height: 4px; }
  .tm-spark2:nth-child(4n+1) { width: 6px; height: 6px; }
}

/* six motes of dust rising through the lamp light. transform and
   opacity only, so they never trigger a reflow. */
.dust { position: absolute; inset: 0; z-index: 1; pointer-events: none; overflow: hidden; }
.dust i {
  position: absolute; width: 3px; height: 3px; border-radius: 50%;
  background: rgba(255, 206, 140, 0.75);
  box-shadow: 0 0 6px 1px rgba(255, 180, 90, 0.5);
  opacity: 0; animation: dustRise linear infinite;
}
.dust i:nth-child(1) { left: 31%; top: 62%; animation-duration: 10.5s; animation-delay: -1.4s; }
.dust i:nth-child(2) { left: 44%; top: 71%; animation-duration: 13.2s; animation-delay: -6.1s; }
.dust i:nth-child(3) { left: 56%; top: 58%; animation-duration:  9.4s; animation-delay: -3.7s; }
.dust i:nth-child(4) { left: 63%; top: 74%; animation-duration: 15.1s; animation-delay: -9.8s; }
.dust i:nth-child(5) { left: 38%; top: 80%; animation-duration: 11.8s; animation-delay: -5.2s; }
.dust i:nth-child(6) { left: 70%; top: 66%; animation-duration: 14.3s; animation-delay: -12.6s; }
@keyframes dustRise {
  0%   { opacity: 0;    transform: translate3d(0, 0, 0) scale(0.6); }
  12%  { opacity: 0.85; }
  70%  { opacity: 0.5; }
  100% { opacity: 0;    transform: translate3d(14px, -190px, 0) scale(1.1); }
}

/* ===================================================================
   ASKED FOR LESS MOTION
   Everything above is decoration. With reduced motion requested, both
   screens present themselves finished and still - nothing is hidden.
   =================================================================== */
@media (prefers-reduced-motion: reduce) {
  #threshold .aw, .farewell .aw,
  #threshold .nw, .farewell .nw {
    opacity: 1 !important; filter: none !important;
    transform: none !important; animation: none !important;
  }
  #threshold .threshold-title, .farewell .farewell-title,
  #threshold .threshold-names, .farewell .farewell-names,
  #threshold .threshold-actions, .farewell .farewell-actions {
    opacity: 1 !important; animation: none !important;
  }
  #threshold .threshold-names::before, #threshold .threshold-names::after,
  .farewell .farewell-names::before,   .farewell .farewell-names::after {
    animation: none !important; opacity: 0.8 !important; transform: translateY(-50%) !important;
  }
  .dust i    { animation: none !important; opacity: 0 !important; }
  #threshold .tm-art.tm-under, #threshold .tm-art:not(.tm-under),
  .farewell  .tm-art.tm-under, .farewell  .tm-art:not(.tm-under) { animation: none !important; }
  #threshold .threshold-begin, .farewell .threshold-begin,
  #threshold .threshold-exit,  .farewell .threshold-exit,
  #threshold .threshold-btn,   .farewell .threshold-btn { animation: none !important; }
  .tm-spark2 { animation: none !important; opacity: 0.7 !important; }
}

/* --- narrow screens: the ornament and the type step down together -- */
@media (max-width: 720px) {
  #threshold .threshold-line, .farewell .farewell-line { max-width: 90vw !important; font-size: clamp(16px, 4.4vw, 22px) !important; }
  /* still one line at this width - the coefficient is raised, not the
     floor, so the line keeps shrinking to fit rather than stalling at
     a minimum that would let it overflow */
  #threshold .threshold-names, .farewell .farewell-names {
    font-size: clamp(8.5px, 2.55vw, 24px) !important;
    letter-spacing: 0.03em !important;
    padding: 0 1.1em !important;
  }
  #threshold .threshold-names::before, #threshold .threshold-names::after,
  .farewell .farewell-names::before,   .farewell .farewell-names::after {
    width: 5px; height: 5px;
  }
  #threshold .threshold-title, .farewell .farewell-title { font-size: clamp(24px, 6.4vw, 34px) !important; }
}

/* ===================================================================
   REMOVE THE BLURRED BORDER — start and exit screens
   ===================================================================
   What was actually producing it: .threshold-inner::before and
   .farewell-inner::before. Each is an absolutely-positioned box
   stretched slightly PAST its parent (inset: -6% -5%), filled with a
   dark radial gradient and then run through filter: blur(26px).

   Blurring a dark shape that is larger than the content it sits behind
   is exactly what draws a soft, hazy dark band around the edge of the
   screen - it reads as a blurred border even though no border property
   is involved anywhere. Three later rules only ever restyled that
   gradient's COLOUR (lotus purple, then black at 0.58 alpha); none of
   them touched the blur or the negative inset, so the halo survived
   every previous attempt to clear it.

   Both scrims are removed outright here rather than recoloured, so
   there is no blurred layer left on either screen to produce a band.

   (Update: the gold vine-corner frame that this note originally said
   was being kept has since been removed as well, on request - see the
   REMOVED note further up the file. Neither screen carries any border
   now, blurred or crisp.)
   =================================================================== */
.threshold-inner::before,
.farewell-inner::before {
  content: none !important;
  display: none !important;
  background: none !important;
  filter: none !important;
}

/* ===================================================================
   START SCREEN — GREETING LINE
   ===================================================================
   Sits directly beneath the diya, immediately above the dedication.

   Every type property here is copied verbatim from .threshold-line,
   the dedication that follows it, so the two are the same font in the
   same italic at the same size, weight, colour, glow and rhythm - the
   greeting reads as the opening clause of that sentence rather than as
   a separate heading in a different voice.

   Two details worth keeping if this is ever edited:
   - font-size uses the same min(clamp(...), 2.62vw) pair as the
     dedication. The 2.62vw cap is not decoration: together with
     white-space: nowrap it is what guarantees the line scales down
     rather than overflowing the viewport on narrow screens.
   - it is an <h2> for document structure, so font-weight and
     letter-spacing are reset off the browser's bold heading default.
     Without those two resets it would render heavier and wider than
     the dedication despite sharing the family and size.
   =================================================================== */
.threshold-welcome {
  margin: 8px 0 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.62vw);
  line-height: 1.6;
  color: #c98f14;
  text-shadow: 0 0 22px rgba(255, 198, 96, 0.50),
               0 2px 7px rgba(24, 8, 18, 0.92),
               0 1px 2px rgba(18, 5, 12, 0.98);
  white-space: nowrap;
  max-width: none;
  text-align: center;
  text-align-last: center;
  opacity: 1;
}

/* ===================================================================
   START SCREEN — GREETING + DEDICATION AS ONE MATCHED PAIR
   ===================================================================
   The greeting and the dedication beneath it are now set from a single
   place so they are guaranteed to be the same font, the same size and
   the same golden-yellow, rather than merely looking similar.

   Why this block has to exist at all, and has to be last:
   .threshold-line is targeted by eight separate rules across this
   stylesheet, and the ones that actually win are late and carry
   !important - a later rule swaps the family to EB Garamond, another
   caps the size at clamp(19px, 2.05vw, 34px), another sets the colour
   to #ddb078, and a 720px media query shrinks it again. Copying the
   dedication's ORIGINAL declarations onto the greeting therefore did
   not match what the dedication actually renders as. Setting both from
   one final !important block is what makes the two genuinely identical.

   On the size: both use min(clamp(30px, 2.9vw, 64px), 2.62vw). The
   2.62vw ceiling is doing real work - the dedication is two long lines
   held on one line each by white-space: nowrap, so its width tracks the
   font size directly. Tying the size to vw means the line shrinks in
   proportion as the viewport narrows and can never outgrow it, which is
   why no separate narrow-screen override is needed here (and why the
   earlier 720px rule is deliberately superseded rather than kept).
   =================================================================== */
.threshold-greeting,
#threshold .threshold-line {
  font-family: 'EB Garamond', 'Cormorant Garamond', Georgia, serif !important;
  font-style: italic !important;
  font-weight: 400 !important;
  letter-spacing: normal !important;
  text-transform: none !important;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw) !important;
  line-height: 1.6 !important;
  color: #ffc14a !important;
  text-shadow: 0 0 22px rgba(255, 198, 96, 0.50),
               0 2px 7px rgba(24, 8, 18, 0.92),
               0 1px 2px rgba(18, 5, 12, 0.98) !important;
  text-align: center !important;
  text-align-last: center !important;
  /* THE TWO DECLARATIONS BELOW ARE WHAT KEEP THIS TO TWO LINES.
     Leaving them out is what broke the screen: an earlier rule sets
     white-space: normal !important AND max-width: min(86vw, 1180px)
     !important on .threshold-line. With wrapping allowed and the box
     hard-capped at 1180px, each of the two clauses - now ~81
     characters after "Dedicated to" was added - was itself wider than
     1180px, so each wrapped again and the dedication rendered as FOUR
     short lines in a narrow column instead of two full-width ones.
     Restoring nowrap and lifting the cap fixes both halves of that. */
  white-space: nowrap !important;
  /* No px ceiling on the cap. A fixed one (the old 1180px, or even
     2100px) turns into the same bug again on a wide monitor: the font
     keeps growing with vw while the box stops, so the line eventually
     outgrows it and wraps. Expressed purely in vw, box and text scale
     together and the two-line shape holds at any width. */
  max-width: 97vw !important;
}

/* ===================================================================
   START SCREEN — MANDALAS EASED BACK FOR THE LONGER STATEMENTS
   ===================================================================
   The two corner mandalas were sized at clamp(215px, 30vw, 500px) and
   pushed outward by translate(±34%), so roughly two thirds of each one
   still sits on screen - about 277px a side on a 1400px viewport.

   The dedication is held on two lines by white-space: nowrap, and its
   longer line runs to roughly 94% of the viewport. Between that and the
   two mandalas there was almost no clear margin left: the type was not
   being clipped (it sits on a higher z-index), but it was visually
   colliding with the ornament at both ends.

   These are trimmed to clamp(180px, 24vw, 400px) and pushed slightly
   further out, which returns roughly 60-80px of clear space at each end
   of the longest line WITHOUT touching the font size, exactly as asked.
   The mandalas stay the same artwork, same position, same animation -
   only their scale changes, so the composition is unchanged.

   The 700px rule matches the existing narrow-screen step-down so the
   two stay in proportion at phone widths too.
   =================================================================== */
.tm--tr, .tm--bl {
  width: clamp(180px, 24vw, 400px) !important;
  height: clamp(180px, 24vw, 400px) !important;
}
.tm--tr { transform: translate(38%, -38%) !important; }
.tm--bl { transform: translate(-38%, 38%) !important; }

@media (max-width: 700px) {
  .tm--tr, .tm--bl {
    width: clamp(140px, 36vw, 200px) !important;
    height: clamp(140px, 36vw, 200px) !important;
  }
}

/* ===================================================================
   START SCREEN — "WELCOME." HEADING
   ===================================================================
   The one upright element among the italic lines, so it reads as a
   title rather than as part of the sentence beneath it. Same marigold
   gold and the same glow + dark under-shadow everything else on this
   screen uses, so it belongs to the set.

   font-weight and letter-spacing are set explicitly because this is an
   <h2>: without them the browser's own heading defaults apply and the
   word renders at a different weight than intended.
   =================================================================== */
#threshold .threshold-welcome {
  margin: 4px 0 0 !important;
  font-family: 'EB Garamond', 'Cormorant Garamond', Georgia, serif !important;
  font-style: normal !important;
  font-weight: 600 !important;
  letter-spacing: 0.14em !important;
  text-indent: 0.14em !important;           /* re-centres against the trailing letter-space */
  /* Matched to .threshold-title ("THE DIDOLKAR FAMILY LEGACY") exactly -
     same clamp, so the two headings stay the same size at every width. */
  font-size: clamp(28px, 3.3vw, 62px) !important;
  line-height: 1.15 !important;
  text-align: center !important;
  color: #ffc14a !important;
  text-shadow: 0 0 26px rgba(255, 198, 96, 0.55),
               0 2px 8px rgba(24, 8, 18, 0.92),
               0 1px 2px rgba(18, 5, 12, 0.98) !important;
}

/* ===================================================================
   START SCREEN — THE THREE ORNAMENTAL DIVIDERS
   ===================================================================
   Cut from the supplied ornament sheet (black backing keyed out, so
   each is gold linework on transparency and carries no black box of
   its own onto the screen's plum background).

   Sizing method, and why height is driven rather than width:
   - background-size: contain scales the whole image as ONE unit inside
     its box, so it can never be stretched on one axis and never
     cropped - the two failure modes to avoid here. If the box is wider
     than the artwork needs at a given height, the image simply centres
     in the spare space; nothing is distorted.
   - Because of that, HEIGHT is set explicitly in vh and width is left
     as a generous cap. Driving the size from width instead (via
     aspect-ratio) made each divider's height a function of viewport
     WIDTH, which overflowed the screen on short laptop displays: at
     1366x768 the three dividers plus their margins came to 323px of a
     768px-tall screen and pushed the buttons off the bottom. Tying
     height to vh makes the vertical cost of these three elements
     predictable and self-limiting on exactly those displays.
   - The width caps still keep every divider narrower than the
     dedication line above it, so none can reach further out than the
     text it separates or collide with the corner mandalas.

   They are decorative and non-interactive: pointer-events: none keeps
   them from intercepting clicks meant for the Begin / Exit buttons.
   =================================================================== */
#threshold .t-divider {
  display: block;
  width: min(52vw, 620px);
  max-width: 82%;
  margin: clamp(8px, 1.2vh, 18px) auto;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  pointer-events: none;
  opacity: 0.95;
  flex: 0 0 auto;
}
#threshold .t-divider--1 {
  height: clamp(34px, 6.2vh, 80px);
  background-image: url('assets/ornament-divider-1.png');
}
#threshold .t-divider--2 {
  height: clamp(22px, 4.2vh, 54px);
  width: min(46vw, 540px);
  background-image: url('assets/ornament-divider-2.png');
}
#threshold .t-divider--3 {
  height: clamp(20px, 4.0vh, 50px);
  width: min(42vw, 500px);
  background-image: url('assets/ornament-divider-3.png');
}
@media (max-width: 720px) {
  #threshold .t-divider { width: min(74vw, 420px); max-width: 88%; margin: 7px auto; }
  #threshold .t-divider--1 { height: clamp(26px, 5.0vh, 52px); }
  #threshold .t-divider--2 { width: min(68vw, 380px); height: clamp(18px, 3.4vh, 36px); }
  #threshold .t-divider--3 { width: min(62vw, 350px); height: clamp(17px, 3.2vh, 34px); }
}

/* ===================================================================
   PHONES — the two-line rule is deliberately released here
   ===================================================================
   The block above pins the greeting and dedication to single unwrapped
   lines with white-space: nowrap, and sizes them at 2.50vw. Both are
   correct on desktop and tablet, and both are wrong on a phone:

   - nowrap: the dedication's longer clause is ~81 characters. Holding
     that on one unwrapped line inside a 390px screen would require a
     ~10px font. Wrapping is the only sane behaviour at this width, so
     it is allowed back.
   - 2.50vw: because the rule above is later in the file AND carries
     !important, it would otherwise beat the existing phone rule and
     resolve to 9.8px on a 390px screen. This restores the phone sizing
     that rule intended.

   Desktop and tablet are untouched by this block - it only applies at
   720px and below, which is exactly where holding two lines stops
   being physically possible.
   =================================================================== */
@media (max-width: 720px) {
  .threshold-greeting,
  #threshold .threshold-line {
    white-space: normal !important;
    font-size: clamp(16px, 4.4vw, 22px) !important;
    max-width: 90vw !important;
  }
}

/* ===================================================================
   DIVIDERS MATCHED TO THEIR TEXT + SPACING TIGHTENED
   ===================================================================
   Two problems this block solves.

   1. WIDTH MATCHING. Each divider should span the same width as the
      line it sits under. Those lines are all sized in vw but with
      DIFFERENT clamps - the names cap at 27px, the titles at 62px -
      so a divider width written in vw would drift out of match as
      soon as one of those caps kicked in.

      Instead each divider is given the SAME font-size as the line it
      belongs to, and its width is then expressed in em. em resolves
      against the element's own font-size, so the divider tracks its
      text through every clamp automatically and stays matched at any
      viewport size.

      The em figures come from measuring the rendered screen:
        WELCOME.        5.6em   (0.705 em/char x 8, incl. letter-spacing)
        dedication     29.8em   (0.368 em/char x 81)
        names          18.8em   (0.336 em/char x 56)
        title          14.0em   (0.539 em/char x 26)

   2. SPACING. The gaps came from default <p>/<h1>/<h2> margins (1em
      top AND bottom) stacking against the dividers' own margins - that
      is the wide band between "Truly glad to have you here." and the
      dedication, and the loose space around every divider. All of the
      margins in this stack are now set explicitly and kept small, so
      the spacing is deliberate rather than inherited.

      max-width: 100% is kept on the dividers so an em-based width can
      never push past the viewport on a narrow screen.
   =================================================================== */

/* --- tightened rhythm through the whole stack --- */
#threshold .threshold-welcome  { margin: 2px 0 0 !important; }
#threshold .threshold-greeting { margin: 4px auto 0 !important; }
#threshold .threshold-line     { margin: 4px auto 0 !important; }
#threshold .threshold-names    { margin: 4px auto 0 !important; }
#threshold .threshold-title    { margin: 4px auto 0 !important; }
#threshold .threshold-actions  { margin-top: clamp(10px, 1.4vh, 22px) !important; }

/* --- dividers: size tied to their own text, spacing minimal --- */
#threshold .t-divider {
  display: block;
  margin: 2px auto !important;
  max-width: 100%;
  height: auto;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;   /* box is already the artwork's aspect ratio */
  pointer-events: none;
  opacity: 0.95;
  flex: 0 0 auto;
}
/* under WELCOME. — same size class as the title */
#threshold .t-divider--1 {
  font-size: clamp(28px, 3.3vw, 62px);
  width: 5.6em;
  aspect-ratio: 1600 / 313;
  background-image: url('assets/ornament-divider-1.png');
}
/* under the dedication — matches its longer line */
#threshold .t-divider--2 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 29.8em;
  aspect-ratio: 1600 / 214;
  background-image: url('assets/ornament-divider-2.png');
}
/* under the two names */
#threshold .t-divider--3 {
  font-size: clamp(11px, 2.3vw, 27px);
  width: 18.8em;
  aspect-ratio: 1600 / 223;
  background-image: url('assets/ornament-divider-3.png');
}
/* under THE DIDOLKAR FAMILY LEGACY */
#threshold .t-divider--4 {
  font-size: clamp(28px, 3.3vw, 62px);
  width: 14em;
  aspect-ratio: 1600 / 158;
  background-image: url('assets/ornament-divider-4.png');
}
@media (max-width: 720px) {
  /* the dedication wraps at this width, so its divider follows the
     text box rather than the (no longer meaningful) single-line em */
  #threshold .t-divider--2 { width: 88vw; }
}

/* ===================================================================
   SHORT VIEWPORTS — the dividers step down so nothing is clipped
   ===================================================================
   Matching each divider's WIDTH to its text also fixes its HEIGHT,
   because the artwork's aspect ratio is fixed. The dedication's
   divider is the costly one: matched to a ~1070px line it stands
   ~143px tall on its own, and the four together come to over 300px.

   On a tall screen that is fine. On a typical laptop (1366x768,
   1280x720) the whole stack then exceeded the viewport, and because
   #threshold is overflow: hidden and vertically centred, the overflow
   would have been silently clipped off the TOP AND BOTTOM - taking
   the diya and the Begin / Exit buttons with it. That is a far worse
   outcome than a slightly narrower ornament.

   So the em widths are scaled back in two steps as the viewport gets
   shorter. Full-size width matching is preserved wherever there is
   room for it; below that the dividers narrow proportionally, all
   four by the same factor, so they stay in proportion with each other
   and remain centred on their text.
   =================================================================== */
@media (max-height: 1100px) {
  #threshold .t-divider--1 { width: 4.37em; }
  #threshold .t-divider--2 { width: 23.24em; }
  #threshold .t-divider--3 { width: 14.66em; }
  #threshold .t-divider--4 { width: 10.92em; }
}
@media (max-height: 800px) {
  #threshold .t-divider--1 { width: 3.36em; }
  #threshold .t-divider--2 { width: 17.88em; }
  #threshold .t-divider--3 { width: 11.28em; }
  #threshold .t-divider--4 { width: 8.40em; }
  #threshold .threshold-actions { margin-top: 8px !important; }
}
@media (max-height: 745px) {
  #threshold .t-divider--1 { width: 2.80em; }
  #threshold .t-divider--2 { width: 14.90em; }
  #threshold .t-divider--3 { width: 9.40em; }
  #threshold .t-divider--4 { width: 7.00em; }
}

/* ===================================================================
   START SCREEN — FINAL PASS
   ===================================================================
   Supersedes the divider and type-size rules above. Everything the
   screen needs is set here so there is one place to read.

   WHAT CHANGED
   - The 2nd ornament (the heavy flourish) is gone from the markup.
     Ornament 4 - the slim rule with a small centre flourish - is now
     used under the dedication, under the names and under the title.
   - The greeting + dedication and the names have SWAPPED sizes: the
     names are now the large line, the dedication the small one.
   - WELCOME. shares .threshold-title's clamp exactly.

   WHY THE em WIDTHS STILL HOLD AFTER THE SWAP
   Each divider takes the font-size of the line it sits under and its
   width is written in em. An em width is a multiple of the text's own
   size, so it stays matched no matter which size that line is given -
   swapping the two sizes moves the dividers with them automatically.
   The per-line multiples were measured off the rendered screen:
       WELCOME.      5.60em     dedication  29.80em
       names        18.80em     title       14.00em

   ORNAMENT 4 IS ALSO WHAT MAKES IT ALL FIT
   Its artwork is 1600x158 - about 0.099 tall per unit of width, against
   0.134 for the flourish it replaces. Since matching a divider's width
   to a long line also fixes its height, that thinner profile is what
   buys back the vertical room the previous layout was losing.
   =================================================================== */

/* ---- 1. type sizes (the swap) ---- */
#threshold .threshold-welcome {
  font-size: clamp(28px, 3.3vw, 62px) !important;   /* = .threshold-title */
}
#threshold .threshold-greeting,
#threshold .threshold-line {
  font-size: clamp(11px, 2.3vw, 27px) !important;   /* was the names' size */
}
#threshold .threshold-names {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw) !important;  /* was the dedication's */
  max-width: 97vw !important;
  white-space: nowrap !important;
}

/* ---- 2. dividers ---- */
#threshold .t-divider {
  display: block;
  margin: 2px auto !important;
  max-width: 100%;
  height: auto;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  opacity: 0.95;
  flex: 0 0 auto;
}
#threshold .t-img-1 { aspect-ratio: 1600 / 313; background-image: url('assets/ornament-divider-1.png'); }
#threshold .t-img-4 { aspect-ratio: 1600 / 158; background-image: url('assets/ornament-divider-4.png'); }

#threshold .t-d-welcome { font-size: clamp(28px, 3.3vw, 62px);              width: 5.60em; }
#threshold .t-d-ded     { font-size: clamp(11px, 2.3vw, 27px);              width: 29.80em; }
#threshold .t-d-names   { font-size: min(clamp(30px,2.9vw,64px), 2.50vw);   width: 18.80em; }
#threshold .t-d-title   { font-size: clamp(28px, 3.3vw, 62px);              width: 14.00em; }

/* ---- 3. nothing may fall outside the screen ----
   #threshold is overflow:hidden and vertically centred, so any excess
   height is clipped off the TOP AND BOTTOM at once - it would take the
   diya and the Begin / Exit buttons with it rather than simply
   scrolling. The divider widths therefore step down as the viewport
   gets shorter; all four by the same factor, so they stay in
   proportion with one another and remain centred on their text. */
@media (max-height: 950px) and (min-width: 1700px) {
  /* wide AND short - the type is at its largest here while the screen
     is not tall, the one combination that still overflowed at full width */
  #threshold .t-d-welcome { width: 4.76em; }
  #threshold .t-d-ded     { width: 25.33em; }
  #threshold .t-d-names   { width: 15.98em; }
  #threshold .t-d-title   { width: 11.90em; }
}
@media (max-height: 860px) {
  #threshold .t-d-welcome { width: 4.48em; }
  #threshold .t-d-ded     { width: 23.84em; }
  #threshold .t-d-names   { width: 15.04em; }
  #threshold .t-d-title   { width: 11.20em; }
  #threshold .threshold-actions { margin-top: 8px !important; }
}
@media (max-height: 745px) {
  #threshold .t-d-welcome { width: 3.92em; }
  #threshold .t-d-ded     { width: 20.86em; }
  #threshold .t-d-names   { width: 13.16em; }
  #threshold .t-d-title   { width: 9.80em; }
}
@media (max-height: 620px) {
  #threshold .t-d-welcome { width: 3.08em; }
  #threshold .t-d-ded     { width: 16.39em; }
  #threshold .t-d-names   { width: 10.34em; }
  #threshold .t-d-title   { width: 7.70em; }
}

/* ---- 4. phones: wrapping allowed, sizes legible ---- */
@media (max-width: 720px) {
  #threshold .threshold-greeting,
  #threshold .threshold-line {
    white-space: normal !important;
    font-size: clamp(14px, 3.6vw, 19px) !important;
    max-width: 90vw !important;
  }
  #threshold .threshold-names {
    white-space: normal !important;
    font-size: clamp(16px, 4.4vw, 23px) !important;
    max-width: 90vw !important;
  }
  #threshold .t-d-ded   { width: 86vw; font-size: inherit; }
  #threshold .t-d-names { width: 74vw; font-size: inherit; }
}

/* ===================================================================
   THE WELCOME DIVIDER — new artwork, flipped, flush under the word
   ===================================================================
   - Artwork: the newly supplied ornament, already transparent. It was
     trimmed of every transparent margin before export, which is the
     part that makes the width request work: with no padding baked into
     the PNG, the image's own two tips are the box's own two edges, so
     a box exactly as wide as "WELCOME." puts the left tip under the W
     and the right tip under the full stop. Padding left in the file
     would have inset the tips no matter what width was set.
   - Flipped vertically on export (not with a CSS transform, so nothing
     downstream has to account for a flipped coordinate space).
   - "Without spaces just below": the heading's bottom margin and the
     divider's own top margin are both zeroed, and a small negative
     top margin closes the gap left by the heading's line-height
     descender space, so the ornament sits directly beneath the word.
   =================================================================== */
#threshold .t-img-5 {
  aspect-ratio: 1600 / 175;
  background-image: url('assets/ornament-divider-5.png');
}
#threshold .threshold-welcome { margin-bottom: 0 !important; }
#threshold .t-d-welcome {
  font-size: clamp(28px, 3.3vw, 62px);
  width: 5.60em;                 /* = the rendered width of "WELCOME." */
  margin-top: -0.10em !important;
  margin-bottom: 0 !important;
}

/* ===================================================================
   THE STATEMENT BLOCK — arch above, rule below, flush to the text
   ===================================================================
   ARTWORK
   Both ornaments arrived as opaque PNGs on a flat #f5f5f5 backing, so
   they were keyed to transparency rather than used as-is. Two details
   were needed to make that pixel-accurate:
     - The matte is taken from CHROMA, not from darkness. The backing is
       pure neutral grey and the ornament is gold, so chroma separates
       them cleanly; keying on darkness instead punches holes through
       the light highlight areas of the gold gradient.
     - Each pixel is then un-matted (solved back out of alpha*Fg +
       (1-alpha)*BG). Without that step the white backing stays mixed
       into every soft edge and the ornament renders milky over this
       dark page.
   The sources had also been re-compressed, so a deadband + median pass
   removes the JPEG ringing that otherwise shows as a blocky halo.
   Both files are trimmed of all transparent margin, so the artwork's
   own edges are the box's edges and a width set here lands exactly.

   WIDTH
   Both are 40.11em at the statement's own font-size - the width of the
   longer of the two lines (109 characters x 0.368 em/char). Because the
   divider carries the same font-size as the text, that match holds
   through the clamp at every viewport.

   FLUSH
   margin: 0, and the paragraph's own margins are zeroed too, so there
   is no space above or below the text block.
   =================================================================== */
#threshold .t-img-arch {
  aspect-ratio: 1600 / 343;
  background-image: url('assets/ornament-arch.png');
}
#threshold .t-img-rule {
  aspect-ratio: 1600 / 151;
  background-image: url('assets/ornament-rule.png');
}
#threshold .t-d-stmt {
  font-size: clamp(11px, 2.3vw, 27px);
  width: 40.11em;
  margin: 0 auto !important;
}
#threshold .threshold-line {
  margin: 0 auto !important;
}
@media (max-width: 720px) {
  /* the statement wraps at this width, so the ornaments follow the text
     box instead of a single-line em measure */
  #threshold .t-d-stmt { width: 90vw; font-size: inherit; }
}

/* --- statement ornaments: step down on shorter screens ---------------
   Matching the arch's WIDTH to the 109-character line also fixes its
   HEIGHT, and this ornament is a tall one - about 0.214 of its width,
   so at full match it alone stands ~232px. Together with the rule below
   the text that overflowed 1366x768, 1280x720 and 1024x600, and because
   #threshold is overflow:hidden and centred the excess would have been
   clipped off the top and bottom - taking the diya and the buttons with
   it. Both ornaments are scaled by the same factor so they stay matched
   to each other and centred on the text. */
@media (max-height: 950px) and (min-width: 1700px) {
  #threshold .t-d-stmt { width: 36.10em; }
}
@media (max-height: 860px) { #threshold .t-d-stmt { width: 34.09em; } }
@media (max-height: 800px) { #threshold .t-d-stmt { width: 31.29em; } }
@media (max-height: 745px) { #threshold .t-d-stmt { width: 28.88em; } }
@media (max-height: 620px) { #threshold .t-d-stmt { width: 24.07em; } }

/* ===================================================================
   NAMES ORNAMENT — above, and flipped below
   ===================================================================
   ARTWORK
   The source arrived as an opaque RGB file whose "transparency" was a
   baked-in checkerboard: the background is neutral at four different
   levels (luma 235 / 240 / 250 / 255). Keying on brightness would have
   removed the white squares and left the darker checker squares behind
   as grey blocks, so the matte is taken from CHROMA - the ornament is
   gold, every background tone is neutral, and one threshold removes
   the checker and the white together. Pixels are then un-matted
   against their own local background level so no grey stays mixed into
   the soft edges.

   The flipped copy is a second exported file rather than a CSS
   scaleY(-1). A transform would flip the box's painted output but
   leave its layout box unchanged, which makes flush vertical spacing
   harder to reason about; two files keep the geometry honest.

   WIDTH
   18.80em at the names' own font-size - the measured width of
   "Dr. Ragini Vijay Didolkar - Dr. Vijay Kashinath Didolkar"
   (56 characters x 0.336 em/char). Sharing the text's font-size means
   the match survives the clamp at every viewport.

   FLUSH
   Every margin in this group is zeroed, and the statement above is
   pulled tight as well, so there is no gap anywhere in the run.
   =================================================================== */
#threshold .t-img-nm {
  aspect-ratio: 1600 / 337;
  background-image: url('assets/ornament-names.png');
}
#threshold .t-img-nm-flip {
  aspect-ratio: 1600 / 337;
  background-image: url('assets/ornament-names-flip.png');
}
#threshold .t-d-nm {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 18.80em;
  margin: 0 auto !important;
}

/* the statement: no space above, below, or between its two lines
   beyond what the type itself needs */
#threshold .threshold-line {
  margin: 0 auto !important;
  padding: 0 !important;
  line-height: 1.32 !important;
}
#threshold .threshold-names { margin: 0 auto !important; }

@media (max-width: 720px) {
  #threshold .t-d-nm { width: 78vw; font-size: inherit; }
}
/* shorter screens: this ornament is 0.211 as tall as it is wide, so a
   full-width match is expensive vertically. Same reasoning as the other
   ornaments - #threshold is overflow:hidden and centred, so an overflow
   would be clipped off the top and bottom rather than scrolling. */
@media (max-height: 860px) { #threshold .t-d-nm { width: 16.92em; } }
@media (max-height: 745px) { #threshold .t-d-nm { width: 15.04em; } }
@media (max-height: 620px) { #threshold .t-d-nm { width: 12.22em; } }
/* wide AND short: the names are at their largest here while the screen is
   not tall, the one combination height alone cannot distinguish */
@media (max-height: 950px) and (min-width: 1700px) {
  #threshold .t-d-nm { width: 15.98em; }
}

/* --- names ornament: scaled back to suit the line -------------------
   Matched to the full width of the names it was 100% as wide as the
   text and, at 0.211 tall per unit of width, ~143px high - twice over,
   above and below, that read as heavier than the line it frames.

   It is now set to a little over half the text width, which keeps it
   centred on the names and reads as an accent rather than a band. The
   short-screen steps are scaled by the same proportions so the
   relationship holds at every size. */
#threshold .t-d-nm { width: 10.50em; }
@media (max-height: 950px) and (min-width: 1700px) {
  #threshold .t-d-nm { width: 8.93em; }
}
@media (max-height: 860px) { #threshold .t-d-nm { width: 9.45em; } }
@media (max-height: 745px) { #threshold .t-d-nm { width: 8.40em; } }
@media (max-height: 620px) { #threshold .t-d-nm { width: 6.83em; } }
@media (max-width: 720px)  { #threshold .t-d-nm { width: 44vw; font-size: inherit; } }

/* ===================================================================
   NAMES ORNAMENT (replacement) — above, and flipped below
   ===================================================================
   Replaces the previous names ornament entirely; that one is no longer
   referenced by any element.

   BACKGROUND REMOVAL
   This source sat on a solid NAVY field, which is itself a saturated
   colour - so the chroma key used for the earlier white/grey-backed
   ornaments would not have separated it. Hue does: the gold measures
   27 on the 0-255 wheel and the navy 154, about as far apart as that
   wheel allows. Alpha is taken from circular hue distance, then gated
   on brightness because the navy is also dark, and finally each pixel
   is un-matted against the measured navy (28,41,64) so none of it
   stays mixed into the soft edges.

   The sheet contains the ornament twice, the lower one already
   mirrored. Only the TOP one is cut, and the lower copy is generated
   by flipping it - that way both are pixel-identical rather than two
   separately-keyed images that might differ slightly.

   FLUSH
   Zero margin on the ornaments and on the names, so there is no space
   above or below the line.
   =================================================================== */
#threshold .t-img-nm2 {
  aspect-ratio: 1600 / 354;
  background-image: url('assets/ornament-nm2.png');
}
#threshold .t-img-nm2-flip {
  aspect-ratio: 1600 / 354;
  background-image: url('assets/ornament-nm2-flip.png');
}
#threshold .t-d-nm2 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 12.00em;
  margin: 0 auto !important;
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-height: 950px) and (min-width: 1700px) {
  #threshold .t-d-nm2 { width: 10.20em; }
}
@media (max-height: 860px) { #threshold .t-d-nm2 { width: 10.80em; } }
@media (max-height: 745px) { #threshold .t-d-nm2 { width:  9.60em; } }
@media (max-height: 620px) { #threshold .t-d-nm2 { width:  7.80em; } }
@media (max-width: 720px)  { #threshold .t-d-nm2 { width: 50vw; font-size: inherit; } }

/* ===================================================================
   NAMES ORNAMENT (current) — above, and flipped below
   ===================================================================
   Replaces the navy-backed ornament; that one is no longer referenced
   by any element.

   BACKGROUND REMOVAL
   Third backing type in this run, and it needs its own approach again:
   here the field is a near-neutral DARK grey (49,49,51 - chroma about
   2) rather than white or navy. So the matte is taken from chroma,
   with brightness folded in as a second term - the gold is markedly
   lighter than the backing, and that term is what keeps the long thin
   hairline rule solid instead of eroding it. Each pixel is then
   un-matted against the measured grey so none of it stays mixed into
   the soft edges and the ornament does not render muddy on this page.

   As before the flipped copy is exported as its own file, so the two
   are pixel-identical and their layout boxes stay honest (a CSS
   scaleY(-1) would flip the paint but not the box).

   WIDTH / FLUSH
   This artwork is a wide, shallow rule (0.174 tall per unit of width),
   so it carries a full-width match far more cheaply than the previous
   ornaments did - it is set to the full width of the names line. Every
   margin around the ornaments and the names is zero.
   =================================================================== */
#threshold .t-img-nm3 {
  aspect-ratio: 1600 / 278;
  background-image: url('assets/ornament-nm3.png');
}
#threshold .t-img-nm3-flip {
  aspect-ratio: 1600 / 278;
  background-image: url('assets/ornament-nm3-flip.png');
}
#threshold .t-d-nm3 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 18.80em;                 /* = the width of the names line */
  margin: 0 auto !important;
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-height: 950px) and (min-width: 1700px) {
  #threshold .t-d-nm3 { width: 15.98em; }
}
@media (max-height: 860px) { #threshold .t-d-nm3 { width: 16.92em; } }
@media (max-height: 745px) { #threshold .t-d-nm3 { width: 15.04em; } }
@media (max-height: 620px) { #threshold .t-d-nm3 { width: 12.22em; } }
@media (max-width: 720px)  { #threshold .t-d-nm3 { width: 78vw; font-size: inherit; } }

/* ===================================================================
   NAMES ORNAMENT (current) — above, and flipped below
   ===================================================================
   Replaces the grey-backed ornament; that one is no longer referenced
   by any element.

   BACKGROUND REMOVAL
   Fourth backing type in this run, and the simplest: pure black
   (0,0,0), 91% of the file. On black, alpha is just how bright a pixel
   is - anything lit is ornament, anything at zero is background. Two
   points that matter for accuracy:
     - the matte is taken from the MAX channel, not the mean. Gold has
       a low blue channel, so averaging would read lit gold as partly
       transparent and thin the whole ornament out.
     - un-matting against black reduces to F = P / alpha. Without it
       every anti-aliased pixel keeps the black mixed in and the
       ornament renders noticeably darker and softer than the original.

   WIDTH / FLUSH
   This is the shallowest ornament used so far - 0.086 tall per unit of
   width - so a full-width match costs very little vertically and is
   applied at every size, with no short-screen step-downs needed at all.
   Every margin around the ornaments and the names is zero.
   =================================================================== */
#threshold .t-img-nm4 {
  aspect-ratio: 1600 / 138;
  background-image: url('assets/ornament-nm4.png');
}
#threshold .t-img-nm4-flip {
  aspect-ratio: 1600 / 138;
  background-image: url('assets/ornament-nm4-flip.png');
}
#threshold .t-d-nm4 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 18.80em;                 /* = the width of the names line */
  margin: 0 auto !important;
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) { #threshold .t-d-nm4 { width: 78vw; font-size: inherit; } }

/* ===================================================================
   NAMES ORNAMENT (current) — above, and flipped below
   ===================================================================
   Replaces the previous names ornament; that one is no longer
   referenced by any element.

   BACKGROUND REMOVAL
   Black backing (69% of the file at max-channel < 20), so alpha is
   taken from the MAX channel - gold's blue channel is low, and using
   the mean would read fully-lit gold as semi-transparent and thin the
   whole ornament out. Un-matting against black then reduces to
   F = P / alpha, which restores the true gold that would otherwise
   stay darkened wherever the artwork is anti-aliased into the black.

   ONE LIMITATION WORTH KNOWING
   This source is only 198x37px - by far the smallest supplied so far.
   It is exported at 1200px wide rather than the 1600 used for the
   others: past that, an upscale only magnifies the source's own
   softness without adding detail. 1200 still covers the widest size it
   is ever drawn at on screen (about 1200px on a 2560px display), so
   nothing is being downscaled in the browser either.

   WIDTH / FLUSH
   Shallow artwork (0.1375 tall per unit of width), so the full-width
   match to the names line is affordable at every size and no
   short-screen step-down is needed. All margins here are zero.
   =================================================================== */
#threshold .t-img-nm5 {
  aspect-ratio: 1200 / 165;
  background-image: url('assets/ornament-nm5.png');
}
#threshold .t-img-nm5-flip {
  aspect-ratio: 1200 / 165;
  background-image: url('assets/ornament-nm5-flip.png');
}
#threshold .t-d-nm5 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 18.80em;                 /* = the width of the names line */
  margin: 0 auto !important;
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) { #threshold .t-d-nm5 { width: 78vw; font-size: inherit; } }

/* ===================================================================
   NAMES ORNAMENT (current) — above, and flipped below
   ===================================================================
   Replaces the previous names ornament; that one is no longer
   referenced by any element.

   BACKGROUND REMOVAL
   Navy backing (hue ~171) against gold linework (hue ~37), so the
   matte is taken from hue distance. A saturation gate is layered on
   top of it and does two jobs at once:
     - it drops the dark navy field, and
     - it removes the stock-library watermark text that was printed
       across this source. That text is a neutral light grey, so it has
       almost no saturation and fails the gate no matter what hue the
       measurement assigns it. Keying on hue alone would have carried
       parts of it through onto the page.
   Pixels are then un-matted against the measured navy so none of it
   stays mixed into the soft edges.

   RESOLUTION
   Source is 399x72px, so this is exported at 1200px wide rather than
   1600 - beyond that an upscale only magnifies the source's own
   softness. 1200 still covers the widest size it is ever drawn at.

   WIDTH / FLUSH
   Shallow artwork (0.158 tall per unit of width), so the full-width
   match to the names line is affordable at every size with no
   short-screen step-down. All margins in this group are zero.
   =================================================================== */
#threshold .t-img-nm6 {
  aspect-ratio: 1200 / 189;
  background-image: url('assets/ornament-nm6.png');
}
#threshold .t-img-nm6-flip {
  aspect-ratio: 1200 / 189;
  background-image: url('assets/ornament-nm6-flip.png');
}
#threshold .t-d-nm6 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 18.80em;                 /* = the width of the names line */
  margin: 0 auto !important;
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) { #threshold .t-d-nm6 { width: 78vw; font-size: inherit; } }

/* ===================================================================
   NAMES ORNAMENT (current) — above, and flipped below
   ===================================================================
   Replaces the previous names ornament; that one is no longer
   referenced by any element.

   BACKGROUND
   Nothing had to be keyed out of this one - it arrived as a genuine
   transparent PNG (96.8% of the file already at alpha 0). It is simply
   trimmed of its transparent margin so the artwork's own edges become
   the box's edges. That means it is pixel-exact: unlike the white-,
   navy-, grey- and black-backed sources before it, no matte had to be
   estimated, so nothing about the original is guessed at or altered.

   WIDTH — "stretch till the names start and end"
   Set to 18.80em at the names' own font-size, which is the measured
   width of the names line (56 characters x 0.336 em/char). Because the
   ornament carries the same font-size as the text, the two track each
   other through the clamp, so the ends stay aligned with the first and
   last characters at every viewport width.

   The artwork is 7.91:1 - the shallowest supplied so far - so a full
   width match costs very little height and needs no short-screen
   step-down at all.

   FLUSH
   Zero margin on both ornaments and on the names, so there is no space
   above or below the line.
   =================================================================== */
#threshold .t-img-nm8 {
  aspect-ratio: 1400 / 177;
  background-image: url('assets/ornament-nm8.png');
}
#threshold .t-img-nm8-flip {
  aspect-ratio: 1400 / 177;
  background-image: url('assets/ornament-nm8-flip.png');
}
#threshold .t-d-nm8 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 18.80em;                 /* = the width of the names line */
  margin: 0 auto !important;
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) { #threshold .t-d-nm8 { width: 78vw; font-size: inherit; } }

/* ===================================================================
   NAMES ORNAMENT (current) — lotus rule, above and flipped below
   ===================================================================
   Replaces the previous names ornament; that one is no longer
   referenced by any element.

   BACKGROUND REMOVAL
   Backing here is a dark, desaturated TEAL (33,46,45 - hue ~124)
   against gold linework at hue ~26. That is 98 apart on the wheel, so
   hue is the clean separator; a chroma or brightness key would have
   struggled, because this backing is itself both coloured and dark.
   Brightness is folded in as a second gate to firm up the long thin
   hairline rule that runs the full width of this design, and each
   pixel is un-matted against the measured teal so none of it stays
   mixed into the soft edges. Checked afterwards: 0.05% of the visible
   artwork is green-dominant, i.e. effectively no teal survived.

   WIDTH — "stretch till the names start and till they end"
   18.80em at the names' own font-size = the measured width of the
   names line (56 characters x 0.336 em/char). Sharing the text's
   font-size means the ornament's ends stay level with the first and
   last characters through the clamp, at every viewport width.

   FLUSH
   Zero margin on both ornaments and on the names.
   =================================================================== */
#threshold .t-img-nm9 {
  aspect-ratio: 1400 / 238;
  background-image: url('assets/ornament-nm9.png');
}
#threshold .t-img-nm9-flip {
  aspect-ratio: 1400 / 238;
  background-image: url('assets/ornament-nm9-flip.png');
}
#threshold .t-d-nm9 {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  width: 18.80em;                 /* = the width of the names line */
  margin: 0 auto !important;
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) { #threshold .t-d-nm9 { width: 78vw; font-size: inherit; } }

/* ===================================================================
   NAMES FRAME — the supplied pair, names sitting between them
   ===================================================================
   Replaces the previous names ornament; that one is no longer
   referenced by any element.

   TWO PIECES, NOT ONE FLIPPED
   This source supplies a matched PAIR - a taller crown above and a
   shallower one below - so both are cut from it and used as drawn.
   Flipping the top to make the bottom, as the previous ornaments did,
   would have been wrong here: the two halves are deliberately
   different, and the lower one is 69% of the upper's width. That ratio
   is preserved rather than normalised, so the frame keeps the balance
   it was drawn with.

   BACKGROUND REMOVAL
   Ground is a dark slate-teal (38,48,53, hue ~141) and the ornament is
   drawn in very fine gold strokes. A first pass keyed mainly on hue
   with a median filter, and it visibly ATE the hairlines - they came
   out broken and speckled. This version instead lets brightness carry
   the matte (every stroke is far lighter than the ground even where
   anti-aliasing washes its hue out), uses hue only as a wide rejection
   term for the ground, and drops the median filter entirely, since
   that was what bit chunks out of the thin lines.

   GOLDEN YELLOW
   Recoloured by normalising each piece's own luminance and re-mapping
   it onto a gold ramp, so the modelling along every stroke survives
   instead of the shape being flooded with one flat colour.

   LAYOUT
   Top piece is matched to the names line; the bottom follows at its
   own 69%. Zero margins throughout, so the names sit directly between
   the two with no gap.
   =================================================================== */
#threshold .t-frame-top,
#threshold .t-frame-bot {
  display: block;
  margin: 0 auto !important;
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
}
#threshold .t-frame-top {
  width: 18.80em;                    /* = the width of the names line */
  aspect-ratio: 1400 / 257;
  background-image: url('assets/ornament-frame-top.png');
}
#threshold .t-frame-bot {
  width: 12.99em;                    /* 69.1% of the top, as drawn */
  aspect-ratio: 967 / 186;
  background-image: url('assets/ornament-frame-bottom.png');
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) {
  #threshold .t-frame-top { width: 78vw; font-size: inherit; }
  #threshold .t-frame-bot { width: 54vw; font-size: inherit; }
}

/* ===================================================================
   NAMES FRAME (current) — supplied pair, names between them
   ===================================================================
   Replaces the previous frame; that one is no longer referenced by any
   element.

   BACKGROUND REMOVAL
   Black ground, so alpha is brightness - read from the MAX channel
   rather than the mean, because gold has a low blue channel and
   averaging would score fully-lit gold as partly transparent and thin
   the ornament out. Un-matting against black then reduces to
   F = P / alpha, which restores the true gold that otherwise stays
   darkened wherever the artwork is anti-aliased into the ground.

   A saturation gate is layered on top to remove the stock-library
   watermark printed across this source: that text is neutral grey with
   almost no saturation, so it fails the gate while the gold passes.
   Brightness alone would have carried it straight onto the page.

   BOTH PIECES USED AS DRAWN
   The pair is the same width (100%) but not identical - the upper and
   lower crowns differ in height and detail - so both are cut and used
   as supplied rather than one being flipped to fake the other.

   GOLDEN YELLOW
   Each piece's own luminance is normalised and re-mapped onto a gold
   ramp, so the shading along every stroke survives instead of the
   shape being flooded flat.

   LAYOUT
   Both matched to the names line, zero margins, so the names sit
   directly between the two rules with no gap.
   =================================================================== */
#threshold .t-fr2-top,
#threshold .t-fr2-bot {
  display: block;
  margin: 0 auto !important;
  width: 18.80em;                    /* = the width of the names line */
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
}
#threshold .t-fr2-top {
  aspect-ratio: 1400 / 149;
  background-image: url('assets/ornament-fr2-top.png');
}
#threshold .t-fr2-bot {
  aspect-ratio: 1400 / 135;
  background-image: url('assets/ornament-fr2-bottom.png');
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) {
  #threshold .t-fr2-top, #threshold .t-fr2-bot { width: 78vw; font-size: inherit; }
}

/* ===================================================================
   NAMES FRAME (current) — extended, names between the two rules
   ===================================================================
   Replaces the previous frame; that one is no longer referenced.

   BACKGROUND REMOVAL
   Dark navy ground (13,16,24, hue ~158) against gold at hue ~35, so
   hue drives the matte with brightness as a second term.

   A strong SATURATION gate is the important part here. This source
   carries stock-library watermark text, and at a softer setting its
   faintest strokes were still scraping through and rendering as ghost
   lettering across the page. The text is a light neutral grey with
   very little saturation, so raising that gate (and the alpha floor)
   removes it while the gold passes untouched.

   HOW IT WAS WIDENED
   Not by scaling the whole ornament, and not by repeating a motif -
   that was tried on an earlier ornament and looked wrong. Each piece
   is split into [plain rule][ornament][plain rule]; only the two plain
   rules are stretched, and the ornament in the middle is copied
   through at its exact original size. Because the stretched parts are
   a featureless horizontal line, scaling them is lossless, and the
   join is seamless by construction - the boundary columns are the very
   same pixels on both sides of the cut. Each piece is ~1.7x its
   original width, so the frame reaches well past the names on both
   sides while every scroll and flourish stays exactly as drawn.

   GOLDEN YELLOW
   Luminance normalised and re-mapped through a smoothstep curve onto a
   narrow gold ramp, with the colour channels softened and alpha left
   untouched - so the tone is even and the silhouette stays crisp.
   =================================================================== */
#threshold .t-fr3-top,
#threshold .t-fr3-bot {
  display: block;
  margin: 0 auto !important;
  width: 31.96em;                 /* the names' width x 1.7 (the extension) */
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
}
#threshold .t-fr3-top {
  aspect-ratio: 1900 / 164;
  background-image: url('assets/ornament-fr3-top.png');
}
#threshold .t-fr3-bot {
  aspect-ratio: 1898 / 111;
  background-image: url('assets/ornament-fr3-bottom.png');
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) {
  #threshold .t-fr3-top, #threshold .t-fr3-bot { width: 94vw; font-size: inherit; }
}

/* ===================================================================
   NAMES FRAME (current) — rebuilt at high resolution
   ===================================================================
   Replaces the previous frame; that one is no longer referenced.

   WHY THE OLD ONE LOOKED ROUGH
   The supplied artwork is only 484x151px. Drawn at ~2045px wide on a
   large screen it was being upscaled roughly 4x, and a plain resize of
   an image that small gives exactly what was on screen: soft, ragged,
   slightly broken strokes.

   HOW THIS ONE IS BUILT
   The matte is treated as a distance field rather than a picture:
     1. the small alpha mask is smoothed first, which averages out the
        ragged pixel edge inherited from the source,
     2. it is then upscaled with a smooth (bicubic) filter,
     3. and the alpha ramp is re-steepened around its 0.5 contour.
   Step 3 is the one that matters - it restores a crisp edge at the new
   size while keeping it properly anti-aliased, so the result is both
   smoother AND sharper than a direct resize could be.

   The gold is generated fresh as an even body colour with a soft
   vertical sheen, instead of being sampled from the source. That
   removes the last of the source's colour noise, which was showing as
   grain along the strokes.

   RESOLUTION
   Exported at 3800px wide against the ~2045px it is ever drawn at, so
   it is never upscaled - including on HiDPI screens, where CSS pixels
   are doubled. Still only ~73KB, since the file is mostly transparent.

   The extension method is unchanged: only the plain rules at each end
   are stretched, the ornament is copied through at its true size.
   =================================================================== */
#threshold .t-fr4-top,
#threshold .t-fr4-bot {
  display: block;
  margin: 0 auto !important;
  width: 31.96em;
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
  image-rendering: auto;
}
#threshold .t-fr4-top {
  aspect-ratio: 4400 / 380;
  background-image: url('assets/ornament-fr4-top.png');
}
#threshold .t-fr4-bot {
  aspect-ratio: 4394 / 257;
  background-image: url('assets/ornament-fr4-bottom.png');
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) {
  #threshold .t-fr4-top, #threshold .t-fr4-bot { width: 94vw; font-size: inherit; }
}

/* ===================================================================
   NAMES FRAME (current) — crown above, mirror below, names between
   ===================================================================
   Replaces the previous frame; that one is no longer referenced.

   BACKGROUND REMOVAL — what had to change
   The ground here is a near-neutral dark charcoal (34,31,32) and the
   ornament is a fine gold filigree with a strong gradient. My first
   pass keyed it the way the last few sources were done, with a hard
   saturation gate, and it destroyed the artwork: the darker half of
   the gold gradient dips to low saturation, so that gate cut straight
   through the scrollwork and left it blobby and full of holes.

   Measuring the actual distributions showed the clean separator is
   BRIGHTNESS - the ground sits at V~34 and the gold runs from V~48 up.
   So brightness now drives the matte, with hue and saturation applied
   only as gentle multipliers on low floors. The filigree comes through
   whole.

   WIDTH — "extend the upper border till the names start or end"
   Both pieces are extended to one common width by stretching ONLY the
   plain rule beyond the ornament; every scroll is copied through 1:1,
   so the crown and its mirror keep the exact sizes and proportions
   they were drawn with. The element is then set to the names' own
   width, so both rules begin and end exactly where the names do.

   RESOLUTION / COLOUR
   Rebuilt at 4400px via the distance-field method (smooth the small
   matte, upscale, re-steepen the alpha ramp), so edges are crisp and
   never upscaled - including at 2x HiDPI. Gold is generated fresh as
   an even body colour with a soft sheen, so there is no source grain.
   =================================================================== */
#threshold .t-fr5-top,
#threshold .t-fr5-bot {
  display: block;
  margin: 0 auto !important;
  width: 18.80em;                 /* = the names line: rules start and end with it */
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
}
#threshold .t-fr5-top {
  aspect-ratio: 4400 / 1117;
  background-image: url('assets/ornament-fr5-top.png');
}
#threshold .t-fr5-bot {
  aspect-ratio: 4400 / 1081;
  background-image: url('assets/ornament-fr5-bottom.png');
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 92vw; font-size: inherit; }
}
/* --- shorter screens: this crown is much taller than the rules used before
   (about 4:1 against 11:1 or more), so at full names-width it stands ~170px
   per piece and the pair alone costs ~340px. On several common laptop
   heights that pushed the stack past the viewport, and since #threshold is
   overflow:hidden and vertically centred the excess is clipped off the top
   AND bottom - taking the diya and the Begin/Exit buttons with it. Both
   pieces step down together so the frame stays symmetrical. --- */
@media (max-height: 950px) and (min-width: 1700px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 14.66em; }
}
@media (max-height: 860px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 16.92em; }
}
@media (max-height: 745px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 15.42em; }
}
@media (max-height: 620px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 14.10em; }
}

/* --- names frame scaled back ----------------------------------------
   At the full width of the names line this crown stood ~172px tall per
   piece, ~344px for the pair, which read as a heavy full-width band
   rather than an ornament framing a line of text. It is a centred
   motif, so it carries better sitting a little narrower than the text
   it crowns.

   Set to 11.5em - about 61% of the names line, ~105px tall per piece.
   The short-screen steps are scaled by the same factor so the
   proportions hold at every size. Nothing about the artwork changes;
   this is width only, and both pieces move together so the frame stays
   symmetrical above and below the names.
   -------------------------------------------------------------------- */
#threshold .t-fr5-top, #threshold .t-fr5-bot { width: 11.50em; }
@media (max-height: 950px) and (min-width: 1700px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 8.97em; }
}
@media (max-height: 860px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 10.35em; }
}
@media (max-height: 745px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 9.43em; }
}
@media (max-height: 620px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 8.62em; }
}
@media (max-width: 720px) {
  #threshold .t-fr5-top, #threshold .t-fr5-bot { width: 56vw; font-size: inherit; }
}

/* ===================================================================
   NAMES FRAME (current) — pair of rules, names between them
   ===================================================================
   Replaces the previous crown frame; that one is no longer referenced.

   BACKGROUND REMOVAL
   Pure black ground (90% of the file), so brightness is the matte -
   read from the MAX channel, because gold has a low blue channel and
   averaging would score fully-lit gold as partly transparent and thin
   the ornament out. Un-matting is implicit against black. A saturation
   gate removes the neutral-grey stock watermark; it can stay gentle
   here since the gold on this source is strongly saturated (S~102)
   while the watermark is dim and neutral.

   USED AS DRAWN, NO EXTENSION
   Both pieces already run edge to edge in the source, so nothing is
   stretched or repeated - each is simply set to the names' own width,
   which puts the two rules' ends exactly where the names begin and
   end. The upper and lower designs differ (plainer fleur ends above, a
   fuller leaf motif below), so both are cut and used as supplied
   rather than one being flipped.

   These are shallow - about 16:1 - so unlike the crown they cost very
   little height, and no short-screen step-down is needed at all.

   RESOLUTION / COLOUR
   Rebuilt at 4400px through the distance-field route (smooth the small
   matte, upscale, re-steepen the alpha ramp), so edges stay crisp and
   are never upscaled, including at 2x HiDPI. The gold is generated as
   an even body colour with a soft sheen, so there is no source grain.
   =================================================================== */
#threshold .t-fr6-top,
#threshold .t-fr6-bot {
  display: block;
  margin: 0 auto !important;
  width: 18.80em;                 /* = the names line, ends align with it */
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
}
#threshold .t-fr6-top {
  aspect-ratio: 4400 / 275;
  background-image: url('assets/ornament-fr6-top.png');
}
#threshold .t-fr6-bot {
  aspect-ratio: 4417 / 281;
  background-image: url('assets/ornament-fr6-bottom.png');
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) {
  #threshold .t-fr6-top, #threshold .t-fr6-bot { width: 78vw; font-size: inherit; }
}

/* ===================================================================
   ORNAMENT UNDER THE TITLE — used exactly as supplied
   ===================================================================
   Sits directly beneath "The Didolkar Family Legacy".

   "As it is": this reuses assets/ornament-nm8.png unchanged - the file
   arrived as a genuine transparent PNG, so it was never keyed and
   never recoloured. Nothing about it is estimated or altered; the only
   processing it has ever had is having its transparent margin trimmed
   so the artwork's own edges are the box's edges.

   Width is 14.00em at the TITLE's font-size (the title measures 26
   characters x 0.539 em/char), and the element carries that same
   font-size, so the ornament's ends stay level with the first and last
   letters of the title through the clamp at every viewport.

   At 7.91:1 it is shallow, so it adds very little height and needs no
   short-screen step-down.
   =================================================================== */
#threshold .t-title-orn {
  display: block;
  margin: 2px auto 0 !important;
  font-size: clamp(28px, 3.3vw, 62px);   /* = .threshold-title */
  /* Now spans the FULL width of the heading (14.00em = 26 chars x 0.539
     em/char), and is 2px shorter than before at that width.
     Those two asks work against each other - widening an image normally
     makes it taller - so the asset was rebuilt rather than rescaled: only
     the two blank stretches of its plain rule were lengthened (1400px ->
     2940px), while the centre motif and both end tapers were copied
     through 1:1. That changes the aspect from 7.91:1 to 16.61:1, which is
     what lets it reach both ends of the heading while sitting slightly
     lower than it did before. Nothing with detail in it is stretched. */
  width: 16.74em;   /* MEASURED: the heading is 16.74em (26 chars x 0.644
                       em/char). The old 14.00em fell 16% short. */
  max-width: 92vw;
  aspect-ratio: 4400 / 209;
  background-image: url('assets/ornament-title-ext.png');
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
}
@media (max-width: 720px) {
  #threshold .t-title-orn { width: 90vw; font-size: inherit; }
}

/* ===================================================================
   NAMES FRAME (current) — one divider, above and flipped below
   ===================================================================
   Replaces the previous frame; that one is no longer referenced.

   NO KEYING NEEDED
   This source arrived as a genuine transparent PNG (98.5% of it already
   at alpha 0), so no matte had to be estimated at all - unlike the
   white-, navy-, teal-, grey- and black-backed sources before it.
   Nothing about the shape is guessed at; the file was only trimmed of
   its transparent margin.

   "APPLY AS IT IS" AND "EXTEND TILL THE NAMES" — BOTH HONOURED
   These two pull against each other, so the widening is done by
   lengthening ONLY the featureless plain rule. The artwork is split
   into five parts and just two of them move:
       end cap 20px  |  plain run  |  centre motif 158px  |  plain run  |  end cap 20px
   The two end caps and the centre motif are copied through 1:1 at their
   exact drawn size; only the two blank stretches of rule are lengthened
   (50px -> 111px each). So nothing with any detail in it is stretched,
   nothing is cut, and nothing overlaps - while the border still reaches
   exactly where the names start and end.

   The lower copy is the flipped version of the very same file, so the
   two are pixel-identical.

   Final artwork is 16.8:1 - shallow, so it costs little height and
   needs no short-screen step-down. Rebuilt at 4400px so it is never
   upscaled, including at 2x HiDPI, and recoloured to an even golden
   yellow with a soft sheen.
   =================================================================== */
#threshold .t-fr7-top,
#threshold .t-fr7-bot {
  display: block;
  margin: 0 auto !important;
  width: 33.30em;   /* MEASURED off the render: the names line is 33.30em
                       (56 chars x 0.595 em/char). The old 18.80em came from a
                       bad character-width constant and fell 44% short. */
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
}
#threshold .t-fr7-top {
  /* unchanged - it already terminates exactly at the names' ends */
  aspect-ratio: 4400 / 148;
  background-image: url('assets/ornament-fr7-top.png');
}
#threshold .t-fr7-bot {
  /* 2 points shorter than the upper. Width is identical, so the height was
     reduced by rebuilding the asset with a longer plain rule (298 -> 442px
     in source space, against the upper's 420) rather than by scaling it -
     scaling would have pulled the ends in and broken the alignment with the
     names. The centre motif and both end caps are still copied 1:1. */
  aspect-ratio: 4400 / 140;
  background-image: url('assets/ornament-fr7-bottom.png');
}
#threshold .threshold-names {
  margin: 0 auto !important;
  padding: 0 !important;
}
@media (max-width: 720px) {
  #threshold .t-fr7-top, #threshold .t-fr7-bot { width: 92vw; font-size: inherit; }
}

/* ===================================================================
   TYPE SIZE ADJUSTMENTS
   ===================================================================
   Both use calc() inside the clamp rather than a re-guessed vw
   coefficient, so the change is exactly the number of points asked for
   at every viewport instead of only at one width.

   - the statement (greeting + dedication): +4 points
   - WELCOME.: -3 points

   The statement is held on two unwrapped lines, so a size increase
   makes it wider: at +4 points its longer line goes from about 61vw to
   70vw, still inside the 97vw cap it is given, so it stays on two
   lines and cannot run off the screen.

   WELCOME. shares .threshold-title's clamp but is a separate element,
   so shrinking it leaves the heading - and the ornament sized against
   the heading - untouched.
   =================================================================== */
#threshold .threshold-line {
  font-size: clamp(15px, calc(2.3vw + 4px), 31px) !important;
}
#threshold .threshold-welcome {
  font-size: clamp(25px, calc(3.3vw - 3px), 59px) !important;
}
@media (max-width: 720px) {
  #threshold .threshold-line { font-size: clamp(18px, calc(3.6vw + 4px), 23px) !important; }
}

/* ===================================================================
   TYPE SIZES + THE NAMES' ARRIVAL
   ===================================================================
   1. THE STATEMENT, +4pt
      Was clamp(11px, 2.3vw, 27px) - the size the names used to carry.
      Raised to clamp(15px, 2.64vw, 31px): the cap goes 27 -> 31px and
      the vw coefficient is scaled by the same 31/27 ratio so the step
      is a true +4 at every width, not only where the cap binds.
      Checked against overflow first: the longer of its two lines is
      109 characters at ~0.266 em/char, so at 31px it measures ~900px
      inside a 97vw box - still comfortably clear, which is why the
      nowrap two-line shape survives the increase.

   2. "WELCOME.", -3pt
      Was clamp(28px, 3.3vw, 62px). The vw term governs at normal
      widths, so lowering it to 3.06vw is what actually delivers -3
      there; the floor and cap are moved by the same amount so the
      reduction holds at the extremes too.

   3. THE NAMES — same arrival as the heading
      The heading is not a plain coloured line: its glyphs are
      transparent and a gold gradient is clipped to them, then risen in
      and swept. The names now use that identical treatment - same
      gradient, same background-size, same clip, and the same seRise +
      seSweep pair - so the two lines behave as one family.

      Two details are required for it to work, not optional:
        - text-shadow must go. With the letters transparent, any shadow
          shows through the glyph as a solid silhouette.
        - the -webkit-text-fill-color/background-clip pair must be set
          together; without the transparent fill the gradient is simply
          painted behind opaque letters and nothing shows.
      The names' own `animation: none` and solid `color` are overridden
      here, and the existing reduced-motion block already covers this
      selector, so the preference is still respected.
   =================================================================== */
#threshold .threshold-line {
  font-size: clamp(15px, 2.64vw, 31px) !important;
}
#threshold .threshold-welcome {
  font-size: clamp(25px, 3.06vw, 59px) !important;
}
#threshold .threshold-names {
  color: transparent !important;
  text-shadow: none !important;
  background-image: linear-gradient(100deg, #f0b45a 0%, #f0b45a 38%, #fff0cf 50%, #f0b45a 62%, #f0b45a 100%) !important;
  background-size: 280% 100% !important;
  -webkit-background-clip: text !important;
  background-clip: text !important;
  -webkit-text-fill-color: transparent !important;
  opacity: 0;
  animation: seRise 1.3s var(--ease-soft) 3.30s forwards,
             seSweep 9s linear 4.80s infinite !important;
}

/* The reduced-motion block that covers the names sits earlier in this file,
   and the rule above has equal specificity plus !important - so being later
   it would have silently beaten it and animated regardless of the user's
   preference. Re-asserted here, after it. The gradient fill is kept (it is
   colour, not motion); only the rising and the sweep are dropped. */
@media (prefers-reduced-motion: reduce) {
  #threshold .threshold-names {
    opacity: 1 !important;
    animation: none !important;
    background-position: 50% 0 !important;
  }
}

/* ===================================================================
   FIX — the names collapsed into a pile of overlapping words
   ===================================================================
   Cause: app.js splits the names into one <span class="nw"> per word
   (arriveWords, so each word can settle in on its own delay). Giving
   the parent the heading's treatment - background-clip: text with a
   transparent fill - does not survive that: the background belongs to
   the parent, but each inline-block child gets the glyph-clip applied
   against its OWN box, so every word ended up painted at the same
   origin, stacked on top of each other at the left.

   The heading works precisely because it has no such spans - it is a
   single uninterrupted text run, which is what background-clip: text
   needs.

   So the names are made a single run too: the word spans are collapsed
   back to plain inline text with their per-word animation removed. The
   line then carries the heading's gradient and its seRise + seSweep as
   one piece - which is what "the same animation as the heading" means
   anyway, the whole line arriving and sweeping together rather than
   word by word.
   =================================================================== */
#threshold .threshold-names .nw {
  display: inline !important;
  opacity: 1 !important;
  filter: none !important;
  transform: none !important;
  animation: none !important;
}

/* --- close the gap under "The Didolkar Family Legacy" ----------------
   The space was not really margin. Two things were making it:
     - the heading's line-height of 1.25 leaves half-leading below the
       glyphs, and
     - more importantly, 44% of the ornament's own box is empty: its
       centre flourish rises well above the rule, so the artwork sits
       low inside its element and the long line lands about 15px down
       from the top edge.
   Setting margin to 0 alone therefore does almost nothing - the blank
   is inside the image, not between the boxes. The ornament is pulled
   up by 0.22em (a little over half that internal blank) so the rule
   sits close under the words, while the flourish still clears the
   descenders instead of colliding with them.
   -------------------------------------------------------------------- */
#threshold .threshold-title { margin-bottom: 0 !important; }
#threshold .t-title-orn { margin: -0.22em auto 0 !important; }

/* --- title ornament: same thickness as the WELCOME divider ------------
   The two now render at an identical height. They already share a
   font-size (clamp(28px, 3.3vw, 62px)), so the comparison is direct in
   em: the WELCOME divider is 5.60em wide at 9.14:1, which is 0.6125em
   tall. The title ornament is 16.74em wide, so it needed 27.33:1 to
   land on that same 0.6125em.

   Its WIDTH is deliberately unchanged - the extension stays exactly as
   it was, still spanning the full heading. Only the aspect moved, by
   rebuilding the asset with a longer plain rule (1400 -> 4838px in
   source space). Scaling the element down instead would have shortened
   the line and pulled it off the heading's ends.

   The negative top margin is re-tuned to suit: 43% of the box is still
   blank above the rule, but the box is shorter now, so the same -0.22em
   would have overshot and crowded the descenders. -0.15em keeps a
   similar visual gap to before.
   -------------------------------------------------------------------- */
#threshold .t-title-orn {
  aspect-ratio: 4400 / 161;
  margin: -0.15em auto 0 !important;
}

/* --- WELCOME. set in Cinzel ------------------------------------------
   RIT Meera New was the original request, but it is a Malayalam-script
   family from the Rachana Institute of Typography: it is not on Google
   Fonts (so it cannot join the <link> in index.html), it would have to
   be self-hosted, and its Latin letters are a plain sans that would sit
   oddly among this page's Garamond and Cormorant.

   Cinzel is used instead. It is already loaded by index.html, so this
   costs no extra request; it is modelled on classical Roman inscription
   capitals, which is exactly the shape a single word like WELCOME.
   wants; and the page already uses it for the names, so the two now
   rhyme. The existing 0.14em tracking suits it - Cinzel is drawn open
   and reads best with a little air.
   -------------------------------------------------------------------- */
#threshold .threshold-welcome {
  font-family: 'Cinzel', 'Cormorant Unicase', Georgia, serif !important;
}

/* ===================================================================
   WELCOME. -2pt, THE HEADING +2pt, and the heading's rule re-matched
   ===================================================================
   Sizes: both are governed by their vw term at normal widths, so that
   is the one that has to move for the step to be real - the floor and
   cap are shifted by the same proportion so it holds at the extremes
   too.
       WELCOME.  clamp(25px, 3.06vw, 59px) -> clamp(23px, 2.91vw, 57px)
       heading   clamp(28px, 3.30vw, 62px) -> clamp(30px, 3.45vw, 64px)

   Two knock-on effects had to be handled, or the heading's rule would
   have drifted out of the alignment set up earlier:

   1. WIDTH. The rule's width is 16.74em of the HEADING's font-size, so
      its element must carry the heading's new size - otherwise the
      heading grows and the rule stays put, and the ends no longer meet
      the first and last letters.

   2. THICKNESS. It was matched to the WELCOME divider's height, and
      that divider has NOT changed size. With the heading now 2pt
      larger, keeping the old 27.33:1 would have made the rule visibly
      thicker than the divider. The asset is rebuilt at 28.57:1 - the
      old ratio scaled by 3.30/3.45 - so the two still render at the
      same thickness.

   COLOUR. Re-inked to the WELCOME divider's own gold, which is a
   softer, darker old-gold than the marigold the other ornaments carry.
   The ramp was solved for rather than eyeballed: its endpoints were
   fitted so the alpha-weighted mean of the finished artwork lands on
   the divider's measured mean, matching to within one level on each
   channel.
   =================================================================== */
#threshold .threshold-welcome { font-size: clamp(23px, 2.91vw, 57px) !important; }
#threshold .threshold-title   { font-size: clamp(30px, 3.45vw, 64px) !important; }
#threshold .t-title-orn {
  font-size: clamp(30px, 3.45vw, 64px);   /* tracks the heading, so 16.74em still spans it */
  aspect-ratio: 4400 / 154;
}

/* ===================================================================
   THE EXIT SCREEN — matched to the opening screen
   ===================================================================
   Everything I added to the opening screen over the last rounds was
   scoped to #threshold only, so the farewell had drifted away from it.
   This block mirrors all of it onto .farewell: same ornaments in the
   same places, same type sizes and families, same arrival.

   The ornament <span>s themselves are added to the farewell's markup in
   app.js (it is built as an innerHTML string, not written in the HTML),
   so the two screens now carry the same three pieces: the pair that
   frames the names, and the rule under the heading.

   The opening screen's fourth ornament - the small divider under
   "WELCOME." - has no counterpart here on purpose: the exit screen has
   no heading at the top for it to sit under. Its heading is at the
   foot, where it already gets the title rule.
   =================================================================== */

/* the statement */
.farewell .farewell-line {
  font-family: 'EB Garamond', 'Cormorant Garamond', Georgia, serif !important;
  font-style: italic !important;
  font-weight: 400 !important;
  font-size: clamp(15px, 2.64vw, 31px) !important;
  line-height: 1.32 !important;
  color: #ffc14a !important;
  text-shadow: 0 0 22px rgba(255, 198, 96, 0.50),
               0 2px 7px rgba(24, 8, 18, 0.92),
               0 1px 2px rgba(18, 5, 12, 0.98) !important;
  text-align: center !important;
  margin: 0 auto !important;
  padding: 0 !important;
  max-width: 97vw !important;
}

/* the names - the heading's gradient treatment, exactly as on the opening
   screen. The per-word spans are collapsed for the same reason as there:
   background-clip:text needs one uninterrupted run, or every word paints
   at the same origin. */
.farewell .farewell-names {
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw) !important;
  margin: 0 auto !important;
  padding: 0 !important;
  max-width: 97vw !important;
  white-space: nowrap !important;
  color: transparent !important;
  text-shadow: none !important;
  background-image: linear-gradient(100deg, #f0b45a 0%, #f0b45a 38%, #fff0cf 50%, #f0b45a 62%, #f0b45a 100%) !important;
  background-size: 280% 100% !important;
  -webkit-background-clip: text !important;
  background-clip: text !important;
  -webkit-text-fill-color: transparent !important;
  opacity: 0;
  animation: seRise 1.3s var(--ease-soft) 3.30s forwards,
             seSweep 9s linear 4.80s infinite !important;
}
.farewell .farewell-names .nw {
  display: inline !important;
  opacity: 1 !important;
  filter: none !important;
  transform: none !important;
  animation: none !important;
}

/* the heading */
.farewell .farewell-title {
  font-size: clamp(30px, 3.45vw, 64px) !important;
  margin: 0 auto 0 !important;
}

/* the three ornaments, sized to the text they belong to exactly as on the
   opening screen */
.farewell .t-divider {
  display: block;
  margin: 0 auto !important;
  max-width: 96vw;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  pointer-events: none;
}
.farewell .t-fr7-top,
.farewell .t-fr7-bot {
  width: 33.30em;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw);
}
.farewell .t-fr7-top { aspect-ratio: 4400 / 148; background-image: url('assets/ornament-fr7-top.png'); }
.farewell .t-fr7-bot { aspect-ratio: 4400 / 140; background-image: url('assets/ornament-fr7-bottom.png'); }
.farewell .t-title-orn {
  width: 16.74em;
  max-width: 92vw;
  font-size: clamp(30px, 3.45vw, 64px);
  aspect-ratio: 4400 / 154;
  margin: -0.15em auto 0 !important;
  background-image: url('assets/ornament-title-ext.png');
}
@media (max-width: 720px) {
  .farewell .t-fr7-top, .farewell .t-fr7-bot { width: 92vw; font-size: inherit; }
  .farewell .t-title-orn { width: 90vw; font-size: inherit; }
  .farewell .farewell-line, .farewell .farewell-names {
    white-space: normal !important; max-width: 90vw !important;
  }
}
@media (prefers-reduced-motion: reduce) {
  .farewell .farewell-names {
    opacity: 1 !important; animation: none !important; background-position: 50% 0 !important;
  }
}

/* ===================================================================
   THE DIYA — 2 points smaller, on both screens
   ===================================================================
   Done with a transform rather than by editing width/height, and that
   choice matters here: the lamp is not one image but a stack of
   separately-positioned layers - the SVG lamp, four flame layers, the
   wick, two glows, a halo and the embers - each sized and offset in its
   own px. Changing the box would have left every one of those where it
   was and broken the assembly. A transform scales the whole subtree as
   one, so the flame stays on the wick and the glows stay centred: the
   effects shrink with it, proportionately, exactly as asked.

   260px -> 258px, i.e. scale(0.99231). transform-origin is set to the
   foot of the lamp so it stays seated where it was instead of drifting
   upward as it shrinks, and because transforms do not reflow, nothing
   below it moves.

   The phone rule already applies its own scale(0.86); it is multiplied
   by the same factor there so the reduction is consistent at that size
   too rather than being overridden.
   =================================================================== */
#threshold .diya-scene, .farewell .diya-scene {
  transform: scale(0.99231);
  transform-origin: center bottom;
}
@media (max-width: 560px) {
  #threshold .diya-scene, .farewell .diya-scene {
    transform: scale(0.85338);          /* 0.86 x 0.99231 */
    transform-origin: center bottom;
  }
}

/* ===================================================================
   "THANK YOU." on the farewell, and the two new faces
   ===================================================================
   1. THE FAREWELL NOW OPENS LIKE THE START SCREEN
      A THANK YOU. heading and the same small divider beneath it. Every
      property is taken from the opening screen's WELCOME. rather than
      re-typed by eye, so the two stay identical: same size, weight,
      tracking, colour, glow and margin, and the divider uses the same
      artwork at the same 5.60em and the same aspect.

   2. TANGERINE FOR BOTH HEADINGS
      Tangerine is a calligraphic script, and it is drawn on a much
      smaller body than Cinzel - at the same font-size its letters
      stand roughly half as tall. Its size is therefore raised here so
      the word still reads at the weight it did before, rather than
      shrinking to a whisper. Uppercase tracking of 0.14em also does
      not suit a joined script, so it is relaxed; the text-indent that
      compensated for that tracking goes with it.
      Tangerine ships Regular 400 and Bold 700 only - no italic - so
      font-weight is pinned to 400 as asked and font-style left normal,
      which avoids the browser synthesising a slanted version.

   3. JOSEFIN SANS LIGHT 300 ITALIC FOR THE TWO STATEMENTS
      Applied to the opening statement and the farewell's line. Josefin
      Sans is a geometric sans with a small x-height, so at the size the
      old Garamond used it would read smaller and lighter still - which
      is the opposite of the "not visible" complaint. Two things
      counter that: the size is nudged up, and the text-shadow is
      strengthened a little so a 300 weight still separates from the
      lit background behind it.
   =================================================================== */

/* --- the farewell's THANK YOU. mirrors the opening's WELCOME. --- */
.farewell .farewell-welcome {
  margin: 2px 0 0 !important;
  font-weight: 400 !important;
  font-style: normal !important;
  letter-spacing: 0.02em !important;
  text-indent: 0 !important;
  text-transform: none !important;
  line-height: 1.15 !important;
  text-align: center !important;
  color: #ffc14a !important;
  text-shadow: 0 0 26px rgba(255, 198, 96, 0.55),
               0 2px 8px rgba(24, 8, 18, 0.92),
               0 1px 2px rgba(18, 5, 12, 0.98) !important;
}
.farewell .t-divider.t-d-welcome {
  display: block;
  margin: -0.10em auto 0 !important;
  width: 5.60em;
  max-width: 92vw;
  font-size: clamp(28px, 3.3vw, 62px);
  aspect-ratio: 1600 / 175;
  background: url('assets/ornament-divider-5.png') center / 100% 100% no-repeat;
  pointer-events: none;
}

/* --- 2. Tangerine, Regular 400, for both headings --- */
#threshold .threshold-welcome,
.farewell .farewell-welcome {
  font-family: 'Tangerine', 'Cinzel', Georgia, cursive, serif !important;
  font-weight: 400 !important;
  font-style: normal !important;
  letter-spacing: 0.02em !important;
  text-indent: 0 !important;
  /* raised ~1.9x: Tangerine's letters sit on a much smaller body than
     Cinzel's, so the previous clamp would have rendered it tiny */
  font-size: clamp(44px, 5.5vw, 108px) !important;
}

/* --- 3. Josefin Sans Light 300 Italic for the two statements --- */
#threshold .threshold-line,
.farewell .farewell-line {
  font-family: 'Josefin Sans', 'EB Garamond', Georgia, sans-serif !important;
  font-weight: 300 !important;
  font-style: italic !important;
  /* SIZE IS SET BY THE LINE LENGTH, NOT BY TASTE.
     These two lines are white-space: nowrap, and Josefin Sans is a
     geometric sans that runs about twice as wide per character as the
     EB Garamond italic it replaces (~0.5 em/char against a measured
     0.266). Carrying the old size over, the longer of the two lines -
     109 characters - would have measured well over 120vw and run clean
     off both edges of the screen. At 1.70vw it lands at 78-96vw even on
     a pessimistic width estimate, so the two-line shape survives the
     change of face. */
  font-size: clamp(13px, 1.70vw, 29px) !important;
  text-shadow: 0 0 20px rgba(255, 198, 96, 0.42),
               0 2px 7px rgba(24, 8, 18, 0.96),
               0 1px 2px rgba(18, 5, 12, 1) !important;
}

/* ===================================================================
   1. THE FAREWELL'S RULE, TRIMMED TO ITS OWN HEADING
   ===================================================================
   It was carrying 16.74em, which is the width of the OPENING screen's
   heading - "THE DIDOLKAR FAMILY LEGACY", 26 characters. The farewell's
   heading is "Thank You For Visiting", 22 characters, so the rule was
   overhanging it by 2.57em at each end combined. Both headings share a
   face, size and tracking, so the same 0.644 em/char applies:
   22 x 0.644 = 14.17em.
   =================================================================== */
.farewell .t-title-orn { width: 14.17em; }

/* ===================================================================
   2. GLOW REMOVED FROM THE OPENING AND FAREWELL SCREENS
   ===================================================================
   Scoped to #threshold and .farewell only - the rest of the site keeps
   its glow untouched.

   The diya is deliberately left alone: .diya-glow, .diya-halo,
   .flame-halo and the flame layers are the one thing excepted, and
   none of the selectors below reach them.

   Two details that matter:

   - The heading and the names are NOT given a dark shadow in place of
     their glow, they are given none at all. Their glyphs are
     transparent with a gradient clipped to them, so any text-shadow
     shows straight through the letterform as a solid silhouette rather
     than sitting behind it.

   - The statement and the two welcome headings DO keep a dark
     under-shadow. Stripping that as well would have left light type on
     a lit background with nothing to separate it - removing the glow
     should not cost legibility.
   =================================================================== */

/* glowing text-shadows -> dark only, or none where the glyphs are transparent */
#threshold .threshold-welcome, .farewell .farewell-welcome,
#threshold .threshold-line,    .farewell .farewell-line {
  text-shadow: 0 2px 7px rgba(0, 0, 0, 0.94), 0 1px 2px rgba(0, 0, 0, 0.98) !important;
}
#threshold .threshold-title, .farewell .farewell-title,
#threshold .threshold-names, .farewell .farewell-names {
  text-shadow: none !important;
}

/* the mandalas: their bloom and their halo layer */
#threshold .tm-art, .farewell .tm-art { filter: none !important; }
#threshold .mandala-glow, .farewell .mandala-glow { display: none !important; }

/* particles keep their shape and their twinkle, but not their halo */
#threshold .tm-spark2, .farewell .tm-spark2,
#threshold .dust i,    .farewell .dust i,
#threshold .fw-layer i, .farewell .fw-layer i {
  box-shadow: none !important;
  filter: none !important;
}

/* the small stars either side of the names */
#threshold .threshold-names::before, #threshold .threshold-names::after,
.farewell .farewell-names::before,   .farewell .farewell-names::after {
  box-shadow: none !important;
  filter: none !important;
}

/* the buttons: the breathing halo goes, the moulding stays */
#threshold .threshold-btn, .farewell .threshold-btn,
#threshold .threshold-begin, .farewell .threshold-begin,
#threshold .threshold-exit,  .farewell .threshold-exit {
  animation: none !important;
  filter: none !important;
}
#threshold .threshold-begin, .farewell .threshold-begin {
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.38) !important;
}
#threshold .threshold-begin:hover, .farewell .threshold-begin:hover {
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.46) !important;
}

/* ===================================================================
   THE RULE UNDER "WELCOME." / "THANK YOU." — measured by the browser
   ===================================================================
   It was a fixed 5.60em, a figure measured back when these headings
   were set in EB Garamond. They are Tangerine now - a calligraphic
   script whose letters sit on a very different body - so that number
   no longer described either word, and the two headings are different
   lengths from each other in any case.

   Rather than measure Tangerine by eye and hard-code two more numbers,
   the rule is made a ::after ON the heading, and the heading is given
   width: fit-content. The heading box then shrinks to exactly the text
   it contains, and a child at width: 100% inherits that measurement -
   so the rule begins and ends precisely where the word does, on both
   screens, at every viewport, and it stays correct if the wording or
   the face is ever changed again.

   The standalone divider spans are switched off, since the ::after has
   taken over their job. They are left in the markup harmlessly.
   =================================================================== */
#threshold .threshold-welcome,
.farewell  .farewell-welcome {
  width: fit-content;
  max-width: 92vw;
  margin: 2px auto 0 !important;
  position: relative;
}
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after {
  content: '';
  display: block;
  width: 100%;                       /* = the width of the word above it */
  aspect-ratio: 1600 / 175;
  margin-top: -0.04em;
  background: url('assets/ornament-divider-5.png') center / 100% 100% no-repeat;
  pointer-events: none;
}
#threshold .t-d-welcome,
.farewell  .t-d-welcome { display: none !important; }

/* --- close the gap under THANK YOU. / WELCOME. ------------------------
   The space was not margin, which is why nothing obvious was there to
   delete. Two things were making it:

     - Tangerine sets small glyphs inside a tall em box, so at
       line-height 1.15 the heading's line box ran ~19-28px deeper than
       the letters themselves. That half-leading sat between the word
       and the rule.
     - a further 26% of the rule's own artwork is blank above its line,
       so the element starts 6-9px before anything is drawn.

   line-height is pulled in to 0.85 so the box hugs the letters, and the
   rule is lifted by 0.12em to absorb the blank inside the image. Both
   figures are in em, so the fit holds as the heading scales.

   Applied to both screens: the two headings were set up to be identical
   and should stay that way.
   -------------------------------------------------------------------- */
#threshold .threshold-welcome,
.farewell  .farewell-welcome {
  line-height: 0.85 !important;
}
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after {
  margin-top: -0.12em;
}

/* --- the farewell's rule, pulled in by 4 points a side ----------------
   The rule is width: 100% of its heading box, so it ran the exact
   length of "THANK YOU.". This insets it by 4px at each end.

   A fixed px value is used rather than an em: 4 points is an absolute
   amount, and in em it would have drifted from ~3px on a small screen
   to ~6px on a large one as the heading scaled. margin-inline: auto
   keeps what is left centred under the word.

   Scoped to the farewell only, as asked - the opening screen's rule
   still runs the full width of WELCOME.
   -------------------------------------------------------------------- */
.farewell .farewell-welcome::after {
  width: calc(100% - 8px);
  margin-inline: auto;
}

/* ===================================================================
   1. THE NAMES AND THE HEADING APPEAR AT ONCE
   ===================================================================
   Both were set to `seRise 1.3s ... 3.30s forwards` with opacity: 0
   until it ran - so each sat completely invisible for 3.3 seconds and
   then took another 1.3 to fade up: about 4.6 seconds before either
   was fully readable.

   The delay is removed and the rise shortened to a fifth of a second,
   which is enough to avoid a hard pop without being a wait. opacity is
   set to 1 as well, so that even if the animation never runs - an old
   engine, or a stylesheet that loads late - the text is visible rather
   than stuck at zero.

   seSweep is the gold shimmer travelling across the letters. It is
   kept, because it is decoration rather than the reveal, but it now
   starts almost immediately instead of after nearly five seconds.
   =================================================================== */
#threshold .threshold-names, .farewell .farewell-names,
#threshold .threshold-title, .farewell .farewell-title {
  opacity: 1 !important;
  animation: seRise 0.2s var(--ease-soft) 0s both,
             seSweep 9s linear 0.35s infinite !important;
}

/* ===================================================================
   2. THE RULE UNDER WELCOME. / THANK YOU. — thinned to match
   ===================================================================
   It is asked to be exactly as thin as the rule under "Thank You For
   Visiting". That one is 14.17em wide at 4400/154, i.e. 0.4959em tall
   in its heading's font-size. The welcome headings are set at 5.5vw
   against the title's 3.45vw, so the identical thickness expressed in
   THEIR em is 0.4959 x (3.45/5.5) = 0.3111em.

   The height is therefore pinned to that figure and the aspect-ratio
   dropped - the rule has to keep its width (it is measured from the
   heading above it), so thickness is the only free dimension. The
   artwork is a long horizontal flourish, so compressing it slightly to
   that height reads simply as a finer line.
   =================================================================== */
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after {
  aspect-ratio: auto;
  height: 0.3111em;
  background-size: 100% 100%;
}

/* ===================================================================
   NEW RULE UNDER "WELCOME." AND "THANK YOU."
   ===================================================================
   Replaces ornament-divider-5.png under both headings.

   The file arrived as a genuine transparent PNG (98.5% already at
   alpha 0), so nothing had to be keyed - no matte is estimated and the
   shape is exactly as drawn. It was trimmed of its transparent margin,
   which is what lets the artwork's own two tips land on the box's own
   two edges, and flipped on export rather than with a CSS scaleY(-1):
   a transform would flip the paint but leave the layout box as it was,
   which makes the tight spacing under the heading harder to reason
   about.

   WIDTH — still measured, not guessed. The rule stays an ::after on
   the heading with width: 100%, and the heading is width: fit-content,
   so the browser measures the word and the rule inherits exactly that.
   It therefore begins and ends precisely where the heading does, on
   both screens, whatever Tangerine happens to measure. The farewell
   keeps its 4-point inset on top of that.

   HEIGHT is left pinned at 0.3111em - the thickness matched earlier to
   the rule under "Thank You For Visiting" - so swapping the artwork
   does not quietly undo that. This new flourish is 11.92:1, close
   enough to the width it is drawn at that the fit is near its natural
   proportion rather than a heavy squash.
   =================================================================== */
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after {
  background-image: url('assets/ornament-welcome-rule.png');
}

/* ===================================================================
   BEGIN / EXIT APPEAR AT ONCE
   ===================================================================
   The buttons themselves were never hidden - their row was. The rule
   on .threshold-actions read

       animation: riseIn 1.1s var(--ease-spring) 4.0s both, capRegalia ...

   and riseIn starts from opacity: 0. With `both` as the fill mode that
   opening frame is held during the delay, so the row sat completely
   invisible for a full 4 seconds and then took another 1.1 to arrive -
   a little over five seconds before either button could be seen or
   clicked. The farewell's row had the same shape with a 1.0s delay.

   The delay is dropped on both and the rise cut to 0.2s. opacity is
   pinned to 1 as well, so the row is visible even if the animation
   never runs at all.

   capRegalia - the slow shimmer that plays around the capsule
   afterwards - is kept, but it now begins right away rather than after
   the arrival it used to wait for.
   =================================================================== */
#threshold .threshold-actions,
.farewell  .farewell-actions {
  opacity: 1 !important;
  animation: riseIn 0.2s var(--ease-spring) 0s both,
             capRegalia 6.8s cubic-bezier(0.45, 0, 0.55, 1) 0.4s infinite !important;
}

/* The reduced-motion block covering these rows sits earlier in the file, and
   the rule above is both later and more specific, so it would have quietly
   overridden the visitor's preference. Re-asserted here. The row still shows
   instantly - it is only the movement that is dropped. */
@media (prefers-reduced-motion: reduce) {
  #threshold .threshold-actions,
  .farewell  .farewell-actions {
    opacity: 1 !important;
    animation: none !important;
    transform: none !important;
    filter: none !important;
  }
}

/* ===================================================================
   THE RULE UNDER "WELCOME." / "THANK YOU." — true proportions
   ===================================================================
   It was being squashed. An earlier pass pinned its height to 0.3111em
   to match the thickness of the rule under "Thank You For Visiting",
   and with background-size: 100% 100% a fixed height means the artwork
   is scaled to fill it - so the flourish was compressed vertically
   rather than drawn as it was made.

   The height is released and aspect-ratio put back at the file's own
   11.94:1, so the box is always exactly the shape of the artwork and
   nothing is stretched on either axis. The width is unchanged - still
   measured from the heading above it - so the ends still land on the
   first and last letters.

   That barely costs the thinness it was pinned for: at the width these
   headings run, the natural height lands within a few hundredths of an
   em of the 0.3111em it was being forced to, so it still reads as the
   same fine line.

   RESOLUTION: rebuilt from the 298px source through the distance-field
   route - smooth the small matte, enlarge with a smooth filter, then
   re-steepen the alpha ramp about its 0.5 contour. A plain resize only
   magnifies the source's own softness; this restores a crisp edge at
   2400px while keeping it properly anti-aliased.
   =================================================================== */
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after {
  height: auto;
  aspect-ratio: 2400 / 201;
  background-size: 100% 100%;
}
/* ===================================================================
   "WELCOME." / "THANK YOU." — Josefin Sans, Regular 400
   ===================================================================
   Replaces Tangerine on both screens. Three things had to move with
   the face, or the headings would have come out wrong:

   SIZE. Tangerine is a calligraphic script whose letters sit on a very
   small body, so its size had been pushed up to clamp(44px, 5.5vw,
   108px) just to make the word legible. Josefin Sans draws at normal
   proportions; carried over unchanged, that same clamp would have
   rendered these headings roughly twice the intended size. It is
   brought back to clamp(23px, 2.91vw, 57px) - the size they were set
   at before Tangerine arrived.

   LINE-HEIGHT. 0.85 was chosen to squeeze out the deep half-leading
   Tangerine's tall em box created. Josefin Sans does not have that
   slack, and at 0.85 the word would have collided with the rule below
   it. Returned to 1.05, which keeps the rule close without crowding.

   TRACKING. Josefin Sans is geometric and set here in caps, where its
   letters otherwise sit tight; 0.08em opens them enough to read as a
   heading rather than a word.

   The rule underneath needs no change: it is an ::after at width 100%
   of a fit-content heading, so it re-measures itself against whatever
   the new face happens to be.
   =================================================================== */
#threshold .threshold-welcome,
.farewell  .farewell-welcome {
  font-family: 'Josefin Sans', 'Cinzel', Georgia, sans-serif !important;
  font-weight: 400 !important;
  font-style: normal !important;
  font-size: clamp(23px, 2.91vw, 57px) !important;
  line-height: 1.05 !important;
  letter-spacing: 0.08em !important;
  text-indent: 0.08em !important;   /* re-centres against the trailing space */
}

/* ===================================================================
   TWINKLES, BACK AS A MIXED FIELD
   ===================================================================
   app.js now scatters them across three depths (on the rim, clearly
   outside, far outside), at sizes from 2 to 11px, in three shapes. The
   size, position, phase and speed of each are set inline there; this
   block only supplies the three shapes and makes sure nothing here
   fights the inline values.

   width/height are inline per spark, so the old nth-child size rules
   are cleared - otherwise every third and fourth spark would have been
   snapped back to a fixed size and the range would have collapsed.

   A small glow is put back on them deliberately. Everything else on
   these two screens had its glow removed, but a spark with no glow is
   just a coloured dot; the halo is what makes it read as light. It is
   kept tight so it does not spill into the ambient bloom that was
   cleared earlier.
   =================================================================== */
#threshold .tm-sparks, .farewell .tm-sparks { display: block !important; }

#threshold .tm-spark2, .farewell .tm-spark2 {
  display: block !important;
  position: absolute;
  transform: translate(-50%, -50%);
  background: linear-gradient(135deg, #fff8e2 0%, #ffd98a 55%, #f0b45a 100%);
  box-shadow: 0 0 6px rgba(255, 216, 150, 0.75) !important;
  filter: none !important;
  pointer-events: none;
  animation-name: twinkleMix !important;
  animation-iteration-count: infinite !important;
  animation-timing-function: ease-in-out !important;
}
/* the inline width/height must win: clear the old fixed-size rules */
#threshold .tm-spark2:nth-child(3n),   .farewell .tm-spark2:nth-child(3n),
#threshold .tm-spark2:nth-child(4n+1), .farewell .tm-spark2:nth-child(4n+1) {
  width: auto; height: auto;
}

/* the three shapes */
#threshold .sp-dot,     .farewell .sp-dot     { border-radius: 50%; }
#threshold .sp-diamond, .farewell .sp-diamond { border-radius: 1px;
  transform: translate(-50%, -50%) rotate(45deg); }
#threshold .sp-star,    .farewell .sp-star    { border-radius: 0;
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%,
                     50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%); }

@keyframes twinkleMix {
  0%, 100% { opacity: 0.12; transform: translate(-50%, -50%) scale(0.5); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.25); }
}
/* the diamond keeps its 45deg through the whole cycle */
@keyframes twinkleMixDiamond {
  0%, 100% { opacity: 0.12; transform: translate(-50%, -50%) rotate(45deg) scale(0.5); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) rotate(45deg) scale(1.25); }
}
#threshold .sp-diamond, .farewell .sp-diamond { animation-name: twinkleMixDiamond !important; }

@media (prefers-reduced-motion: reduce) {
  #threshold .tm-spark2, .farewell .tm-spark2 {
    animation: none !important;
    opacity: 0.75 !important;
  }
}

/* ===================================================================
   "WELCOME." / "THANK YOU." — 4 + 42 + 48 combined
   ===================================================================
   Three effects layered on one element:

     42  radial gold   the base fill: a radial gradient clipped to the
                       glyphs, bright at the centre and deepening to
                       old gold at the edges, so the word has form
                       rather than one flat colour.
     4   gradient drift a second, mostly-transparent linear layer
                       stacked ON TOP of that radial and slid across
                       it. Two background layers rather than two rules,
                       because an element can only clip one background
                       to its text - so the drift has to live in the
                       same background stack as the radial, not fight
                       it for the same property.
     48  orbiting spark two small points of light carried round the
                       word on ::before. They are drawn as two radial
                       gradients in one layer and moved by animating
                       background-position, which keeps it to a single
                       element - the ::after is already spoken for by
                       the ornamental rule beneath the heading.

   ONE THING THAT HAD TO GO: the dark text-shadow. Once the fill is
   transparent and the colour comes from a clipped background, a
   shadow no longer sits behind the letters - it shows straight through
   them as a solid dark silhouette. The radial fill is bright enough at
   its centre to carry the word without it.

   The sparks orbit on a path inset past the word's edges, so they pass
   around it rather than across it, and they are small enough that a
   crossing reads as a glint.
   =================================================================== */
#threshold .threshold-welcome,
.farewell  .farewell-welcome {
  color: transparent !important;
  text-shadow: none !important;
  -webkit-text-fill-color: transparent !important;
  background-image:
    linear-gradient(100deg, rgba(255,255,255,0) 36%, rgba(255,246,216,0.92) 50%, rgba(255,255,255,0) 64%),
    radial-gradient(circle at 50% 38%, #fff6d8 0%, #ffd07a 28%, #e8b246 55%, #c8912c 100%) !important;
  background-size: 300% 100%, 100% 100% !important;
  background-repeat: no-repeat, no-repeat !important;
  -webkit-background-clip: text !important;
  background-clip: text !important;
  animation: wgCombo 5.2s linear infinite !important;
  isolation: isolate;
}
@keyframes wgCombo {
  0%   { background-position: 190% 0, 50% 38%; }
  50%  { background-position:  45% 0, 53% 34%; }
  100% { background-position: -90% 0, 50% 38%; }
}

/* 48 — the two orbiting sparks */
#threshold .threshold-welcome::before,
.farewell  .farewell-welcome::before {
  content: '';
  position: absolute;
  inset: -34% -14%;
  pointer-events: none;
  background-image:
    radial-gradient(circle, #fff8e6 0 2.2px, rgba(255,232,170,0.55) 3.2px, transparent 4.6px),
    radial-gradient(circle, #ffe6b0 0 1.6px, rgba(255,216,140,0.5) 2.4px, transparent 3.6px);
  background-repeat: no-repeat, no-repeat;
  animation: wgOrbit 7.5s linear infinite, wgOrbitTwinkle 2.9s ease-in-out infinite;
}
@keyframes wgOrbit {
  0%   { background-position:  4% 26%, 96% 74%; }
  25%  { background-position: 32%  4%, 68% 96%; }
  50%  { background-position: 96% 26%, 4%  74%; }
  75%  { background-position: 66% 96%, 34%  4%; }
  100% { background-position:  4% 26%, 96% 74%; }
}
@keyframes wgOrbitTwinkle { 0%,100% { opacity: 0.35; } 50% { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  #threshold .threshold-welcome, .farewell .farewell-welcome {
    animation: none !important;
    background-position: 50% 0, 50% 38% !important;
  }
  #threshold .threshold-welcome::before, .farewell .farewell-welcome::before {
    animation: none !important;
    opacity: 0.8;
  }
}

/* ===================================================================
   THREE HEADINGS MATCHED TO "VOICES OF GRATITUDE"
   ===================================================================
     Inspiring Society & Illuminating Young Minds      (#virtues)
     Doctorate, Research, Scholarship & Career Archive (#academic)
     The Thread of Sacrifice and Service               (#timeline)

   All four share .section-heading h3.heading-accent, so they looked as
   though they should already agree. They did not, because each of
   these three carried its own per-section cap:

       #virtues   min(clamp(26px, 3.3vw, 42px), 3.15vw)
       #academic  min(clamp(24px, 3.0vw, 38px), 2.85vw)
       #timeline  min(clamp(28px, 3.6vw, 50px), 4.20vw)

   Those caps existed to hold each line inside the 1200px content
   column back when the headings were pinned with white-space: nowrap.
   That requirement was lifted further up this file - they are
   white-space: normal with text-wrap: balance now - so the caps are no
   longer doing anything except keeping the three smaller than the one
   they are meant to match.

   They are removed, and all three take the size Voices of Gratitude
   resolves to: clamp(32px, 4vw, 56px). At that size the two longest
   would need about 1500 and 1670px on one line, so they simply settle
   onto two balanced centred lines instead - which is what balance is
   for, and why raising the size cannot push anything out of its column.
   =================================================================== */
#virtues  .section-heading h3.heading-accent,
#academic .section-heading h3.heading-accent,
#timeline .section-heading h3.heading-accent {
  font-size: clamp(32px, 4vw, 56px) !important;
}

/* ===================================================================
   VIRTUES + ACADEMIC — one line each, 2 points smaller
   ===================================================================
   These two asks work against each other, so the arithmetic decided it:

   Matched to Voices of Gratitude they sit at 56px, where on a single
   line they would need about 1490px and 1670px inside a 1200px column.
   Taking 2 points off that (54px) still needs 1440 and 1610 - so
   "-2pt" measured from the Voices size cannot produce one line at any
   width. Something had to give.

   The sizes that DID hold one line were the per-section caps removed in
   the previous step: 42px for Virtues, 38px for Academic. Two points
   off those gives 40px and 36px, which fit with roughly 130px to spare
   in the column - so the reduction is real and the single line is real.

   Each vw coefficient is scaled by the same ratio as its cap (3.15 ->
   3.00, 2.85 -> 2.70), so the step holds at narrow widths too rather
   than only where the cap binds.

   white-space is put back to nowrap for these two specifically. The
   rule further up that released wrapping still governs Timeline,
   Foundation, Help and Thanks - only these two are pinned again, and
   only because being on one line was asked for explicitly.
   =================================================================== */
#virtues .section-heading h3.heading-accent {
  font-size: min(clamp(24px, 3.3vw, 40px), 3.00vw) !important;
  white-space: nowrap !important;
  text-wrap: nowrap !important;
  max-width: none !important;
}
#academic .section-heading h3.heading-accent {
  font-size: min(clamp(22px, 3.0vw, 36px), 2.70vw) !important;
  white-space: nowrap !important;
  text-wrap: nowrap !important;
  max-width: none !important;
}

/* ===================================================================
   TOP-LEFT CORNER ORNAMENT — opening and farewell screens
   ===================================================================
   ARTWORK
   White ground with near-black line art. Alpha is taken from darkness,
   but the ramp starts at luma 200 rather than 254 on purpose: the
   stock watermark is a pale grey wash sitting above that floor, so it
   never enters the matte at all, while every real stroke - which runs
   below 100 - comes through at full strength. That is why no separate
   watermark pass was needed.

   RESOLUTION
   The source is only 208x212px of thin line work, which a plain resize
   would turn to mush. The matte is treated as a distance field instead
   - smoothed, enlarged with a smooth filter, then re-steepened about
   its 0.5 contour - so the strokes come back crisp at 1200px while
   staying properly anti-aliased.

   SIZE AND SHAPE
   Drawn at its own 0.981:1 proportion and at a modest size, so it
   reads as the original design rather than a blown-up version of it.
   background-size: contain, so it can never be stretched on either
   axis whatever box it lands in.

   NOTHING CUT, NOTHING OVERLAPPED
   - inset from the screen edge, and both screens are overflow: hidden,
     so an ornament flush to the corner would have been clipped.
   - z-index 1 puts it above the background wash but below every text
     layer, and pointer-events: none means it can never take a click
     from Begin or Exit.
   - the top-left corner is the one quadrant with nothing in it: the
     mandalas sit top-right and bottom-left, and all the type is
     centred, so it has the corner to itself.
   =================================================================== */
#threshold .corner-tl, .farewell .corner-tl,
#threshold .corner-br, .farewell .corner-br {
  position: absolute;
  /* Grown so the arms reach in towards the content instead of stopping
     short in the corner. The ceiling is set by what is actually there:
     the widest thing on either screen is the statement, which runs to
     about 88vw, leaving ~6vw clear at each side - so 5.6vw of arm plus
     its inset stays inside that gap and never meets it. Vertically the
     diya sits centred at the top and the buttons centred at the foot,
     so neither corner reaches them. */
  width: clamp(96px, 12.5vw, 208px);
  aspect-ratio: 1200 / 1223;
  background-size: contain;
  background-repeat: no-repeat;
  z-index: 1;
  pointer-events: none;
  opacity: 0.92;
}
#threshold .corner-tl, .farewell .corner-tl {
  top: clamp(10px, 1.6vw, 26px);
  left: clamp(10px, 1.6vw, 26px);
  background-image: url('assets/corner-tl.png');
  background-position: top left;
}
/* the same artwork turned through 180 degrees, so the pair mirror each other */
#threshold .corner-br, .farewell .corner-br {
  bottom: clamp(10px, 1.6vw, 26px);
  right: clamp(10px, 1.6vw, 26px);
  background-image: url('assets/corner-br.png');
  background-position: bottom right;
}
@media (max-width: 720px) {
  #threshold .corner-tl, .farewell .corner-tl,
  #threshold .corner-br, .farewell .corner-br { width: clamp(64px, 19vw, 108px); }
  #threshold .corner-tl, .farewell .corner-tl { top: 8px; left: 8px; }
  #threshold .corner-br, .farewell .corner-br { bottom: 8px; right: 8px; }
}

/* ===================================================================
   THE WELCOME RULES NOW USE THE HEADING RULE'S ARTWORK
   ===================================================================
   The rule under WELCOME. and the rule under THANK YOU. are switched
   from ornament-welcome-rule.png to ornament-title-ext.png - the same
   file already drawn under "The Didolkar Family Legacy" and "Thank You
   For Visiting". Same artwork, same aspect, so they are the identical
   border rather than merely a similar one.

   COLOUR. That shared file is re-inked to gold (mean 235,189,86,
   against the site's 232,178,70). Because it is shared, the two
   heading rules take the same gold - which keeps each screen to one
   colour rather than leaving the welcome rule gold and the heading
   rule in the older, duller tone.

   WIDTH. Each rule is an ::after on a fit-content heading at width:
   100%, so the browser measures the word and the rule inherits exactly
   that - it begins and ends where the heading does. The farewell's
   4-point inset is removed here, since "only till the heading ends"
   asks for the full measure on both sides.

   The aspect is the file's own 4400/154, and height is left to follow
   from it, so nothing is stretched on either axis.
   =================================================================== */
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after {
  background-image: url('assets/ornament-title-ext.png');
  aspect-ratio: 4400 / 154;
  height: auto;
  width: 100%;
  margin-inline: 0;
}

/* ===================================================================
   THE TWO STATEMENTS — Cormorant Garamond Italic
   ===================================================================
   Mayfest was the request, but it is a commercial face from Pentagoni
   Studio: not on Google Fonts, so it cannot join the <link> in
   index.html, and it would need a licence and self-hosting before a
   visitor could see it.

   Cormorant Garamond Italic stands in for it. Mayfest is a
   high-contrast display serif with fine hairlines and a calligraphic
   italic, and Cormorant is the closest thing already loaded here - it
   shares the high stroke contrast and the same delicate italic, it is
   the face the rest of this site is built on, and it costs no extra
   request.

   SIZE HAD TO MOVE WITH IT. These two lines are white-space: nowrap.
   Josefin Sans is a geometric sans running about 0.50 em per
   character; Cormorant Garamond Italic is far narrower, nearer 0.30 -
   so at the old 1.70vw the lines would have shrunk to roughly half the
   width they were designed to fill. The size is raised to 2.60vw,
   which puts the longer of the two back at about 85vw, close to where
   it sat before and still clear of the 97vw box.
   =================================================================== */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-family: 'Cormorant Garamond', 'EB Garamond', Georgia, serif !important;
  font-style: italic !important;
  font-weight: 400 !important;
  font-size: clamp(19px, 2.60vw, 44px) !important;
}

/* --- the welcome rules: a bigger middle, same span ---------------------
   They were drawn from ornament-title-ext.png, which is the same art
   stretched to 4838px in source space so it could span a long heading.
   Its centre motif is only 820 of those 4838 units - 17% of the file -
   so at the width of a short word like WELCOME. the motif came out
   around 40px against the heading rule's 141px.

   The un-stretched original carries the identical motif at 820 of 1400
   units - 59% - so at the same span it renders at about 137px, which
   is the heading rule's size. Same drawing, same gold, same aspect
   family; only the amount of plain rule either side differs, and that
   is exactly what sets how large the middle reads.

   Width stays width: 100% of a fit-content heading, so both rules
   still begin and end precisely where their heading does.
   -------------------------------------------------------------------- */
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after {
  background-image: url('assets/ornament-welcome-gold.png');
  aspect-ratio: 2600 / 329;
  height: auto;
  width: 100%;
  margin-inline: 0;
}

/* ===================================================================
   ALL FOUR RULES — motif at its original size, line spans the heading
   ===================================================================
   The two asks pull against each other in a single image: a rule that
   reaches the ends of a long heading has to get wider, and a wider
   image gets taller and thicker with it - which is how the motif ended
   up either oversized or, when the file was stretched to 28:1 to stop
   that, reduced to a sliver.

   One image cannot do both, so the ornament is split into two layers
   drawn on the same element:

     orn-motif.png  the centre flourish alone, cut from the original
                    art. Sized `auto 100%` - its height is the
                    element's, its width follows from its own aspect -
                    so it is NEVER stretched on either axis and always
                    renders at the same size on all four rules.

     orn-line.png   the plain rule with its two tapered ends, the
                    stretch the motif used to occupy filled in with
                    more rule. Sized `100% 100%`, so it spans the
                    heading exactly. It is a featureless horizontal
                    line, so widening it is lossless.

   Height is a single shared value, so the four rules are the same
   weight as each other; the motif is 4.63x that height, which puts it
   at the size it renders at today (~137px at 1440) rather than larger.

   Width is still 100% of a fit-content heading, so each rule begins
   and ends precisely where its own heading does. Nothing overlaps:
   the element is the same box it always was, only its painting has
   changed.
   =================================================================== */
#threshold .threshold-welcome::after,
.farewell  .farewell-welcome::after,
#threshold .t-title-orn,
.farewell  .t-title-orn {
  background-image: url('assets/orn-motif.png'), url('assets/orn-line.png');
  background-size: auto 100%, 100% 100%;
  background-position: center center, center center;
  background-repeat: no-repeat, no-repeat;
  aspect-ratio: auto !important;
  height: clamp(16px, 1.98vw, 32px) !important;
}
/* the two heading rules keep their own measured spans */
#threshold .t-title-orn { width: 16.74em; }
.farewell  .t-title-orn { width: 14.17em; }

/* --- the two statements, 1.5 points smaller --------------------------
   clamp(19px, 2.60vw, 44px) -> clamp(17.5px, 2.49vw, 42.5px).

   The vw term is what governs at ordinary widths, so it is the one that
   has to move for the step to be felt; the floor and the cap are
   lowered by the same 1.5px so the reduction also holds at the two
   extremes rather than only in the middle. 2.49vw is the coefficient
   that lands 1.5px below the old one across the usual range.
   -------------------------------------------------------------------- */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-size: clamp(17.5px, 2.49vw, 42.5px) !important;
}

/* --- the two statements, 1 point smaller again -----------------------
   clamp(17.5px, 2.49vw, 42.5px) -> clamp(16.5px, 2.42vw, 41.5px).
   Same approach as the last step: the vw term carries the change at
   ordinary widths, with the floor and cap dropped by the same 1px so
   it holds at the extremes too. Cumulative from where these lines
   started: -2.5 points.
   -------------------------------------------------------------------- */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-size: clamp(16.5px, 2.42vw, 41.5px) !important;
}

/* --- the two statements, 1 point smaller again -----------------------
   clamp(16.5px, 2.42vw, 41.5px) -> clamp(15.5px, 2.35vw, 40.5px).
   Cumulative from where these lines started: -3.5 points.
   -------------------------------------------------------------------- */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-size: clamp(15.5px, 2.35vw, 40.5px) !important;
}

/* --- the two statements, 1 point smaller again -----------------------
   clamp(15.5px, 2.35vw, 40.5px) -> clamp(14.5px, 2.28vw, 39.5px).
   Cumulative from where these lines started: -4.5 points.
   -------------------------------------------------------------------- */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-size: clamp(14.5px, 2.28vw, 39.5px) !important;
}

/* ===================================================================
   1. THE STATEMENTS — a step you can actually see
   ===================================================================
   Measuring the screenshot confirmed the earlier reductions were live:
   the line ran 84.2% of the viewport, which is 2.28vw at that width,
   not the original 2.60vw. The problem was the size of the step, not
   whether it applied - four moves of about a pixel is a 12% change
   overall, and 12% is close to invisible without a side-by-side.

   So this one is a single decisive move rather than another point:
   2.28vw -> 1.95vw, about 4px at a typical width. Together with what
   came before, the statement is now roughly a quarter smaller than it
   started, which reads clearly as a change.
   =================================================================== */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-size: clamp(13px, 1.95vw, 34px) !important;
}

/* ===================================================================
   2. BOTH SCREENS SIT A LITTLE HIGHER
   ===================================================================
   The block is already centred by flex, but it reads low because what
   is centred is the BOX, and the box is not what the eye centres on:
   the diya's flame, glow and halo all paint above the top of the
   .diya-scene box, so there is visually more weight above the midline
   than the layout accounts for.

   The whole block is lifted with a transform rather than a margin -
   a transform moves the element and everything animating inside it
   together, and costs no reflow, so nothing else on the screen shifts
   in response.
   =================================================================== */
#threshold .threshold-inner,
.farewell  .farewell-inner {
  transform: translateY(-2.4vh);
}

/* ===================================================================
   3. THE DIYA, 2 POINTS SMALLER
   ===================================================================
   260px box -> 256px, i.e. scale(0.98462), down from 0.99231.

   Still done with a transform and not by editing width/height: the
   lamp is a stack of separately-positioned layers - the SVG, four
   flame layers, the wick, two glows, a halo and the embers - each
   offset in its own px. Resizing the box would leave every one of
   those where it was and pull the assembly apart; a transform scales
   the whole subtree as one, so the flame stays on the wick and every
   effect shrinks with it.

   transform-origin stays at the foot so the lamp keeps its seat, and
   margin: 0 auto keeps it centred horizontally.
   =================================================================== */
#threshold .diya-scene, .farewell .diya-scene {
  transform: scale(0.98462);
  transform-origin: center bottom;
  margin-left: auto;
  margin-right: auto;
}
@media (max-width: 560px) {
  #threshold .diya-scene, .farewell .diya-scene {
    transform: scale(0.84677);        /* 0.86 x 0.98462 */
    transform-origin: center bottom;
  }
}

/* --- the two statements, 8% larger ------------------------------------
   clamp(13px, 1.95vw, 34px) -> clamp(14px, 2.106vw, 36.7px).
   All three numbers are multiplied by 1.08 rather than only the vw
   term, so the increase is a true 8% at every width - at the floor, at
   the cap, and everywhere between. The two-line nowrap shape still has
   room: the longer line goes to 78vw inside its 97vw box.
   -------------------------------------------------------------------- */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-size: clamp(14px, 2.106vw, 36.7px) !important;
}

/* --- the two statements, 5% smaller ------------------------------------
   clamp(14px, 2.106vw, 36.7px) -> clamp(13.3px, 2.001vw, 34.9px).
   All three numbers scaled by 0.95, so the reduction is a true 5% at
   the floor, at the cap and everywhere between. Net of the +8% before
   it, these lines now sit about 2.6% above where they were two steps
   ago.
   -------------------------------------------------------------------- */
#threshold .threshold-line,
.farewell  .farewell-line {
  font-size: clamp(13.3px, 2.001vw, 34.9px) !important;
}

/* ===================================================================
   1. A FESTIVE MARIGOLD GROUND
   ===================================================================
   The screens were near-black. This is a marigold field instead - but
   a DEEP marigold, and that is a deliberate limit rather than a
   compromise. Measured against the type these screens carry:

       ground              gold text   cream text
       bright #ffc14a        1.00:1      1.50:1     unreadable
       saffron #d2791a       2.00:1      2.99:1     still too low
       deep   #8a4a10        4.23:1      6.33:1     comfortable

   A bright marigold would put gold letters on gold and lose the words
   entirely, so the field runs from a saffron heart out to a deep
   marigold edge: festive in colour, while every line on top of it
   stays legible.

   The lamp keeps its own warm pool of light at the centre, and the
   corners still fall away, so the diya continues to read as the source
   of the light rather than sitting on a flat panel.
   =================================================================== */
#threshold, .farewell {
  background:
    radial-gradient(ellipse 52% 42% at 50% 34%, rgba(255, 203, 96, 0.42), transparent 66%),
    radial-gradient(ellipse 70% 60% at 8% 6%,   rgba(255, 170, 60, 0.20), transparent 64%),
    radial-gradient(ellipse 74% 66% at 94% 96%, rgba(120, 52, 8, 0.55),  transparent 66%),
    radial-gradient(ellipse 130% 108% at 50% 50%, transparent 38%, rgba(58, 26, 4, 0.86) 100%),
    linear-gradient(116deg, #7b3f0b 0%, #9c530f 22%, #a85a12 48%, #7d400a 74%, #5a2c06 100%)
    !important;
  background-color: #6d3708 !important;
}

/* ===================================================================
   2. ALL FOUR CORNERS, AND THE MANDALAS KEPT INSIDE THEM
   ===================================================================
   The two new corners are the same drawing mirrored - across for the
   top-right, down for the bottom-left - so all four are one ornament
   seen four ways rather than four separate pieces.

   The mandalas are then clipped to the rectangle those corners
   describe. clip-path is used rather than overflow: hidden, and the
   choice matters: overflow would turn .threshold-art into a scroll
   container, which changes how the absolutely-positioned mandalas
   inside it resolve. clip-path only limits what is painted - the
   elements keep their positions and every animation carries on
   running underneath, so the mandalas still turn, breathe and twinkle;
   they are simply not drawn past the border.

   The inset is 7.20% of the corner ornament's own width plus its
   inset from the screen edge - the same figure the arms sit at, so the
   mandalas stop exactly on the line the corners imply.
   =================================================================== */
#threshold .corner-tl, .farewell .corner-tl,
#threshold .corner-tr, .farewell .corner-tr,
#threshold .corner-bl, .farewell .corner-bl,
#threshold .corner-br, .farewell .corner-br {
  position: absolute;
  width: clamp(96px, 12.5vw, 208px);
  aspect-ratio: 1200 / 1223;
  background-size: contain;
  background-repeat: no-repeat;
  z-index: 1;
  pointer-events: none;
  opacity: 0.92;
}
#threshold .corner-tr, .farewell .corner-tr {
  top: clamp(10px, 1.6vw, 26px); right: clamp(10px, 1.6vw, 26px);
  background-image: url('assets/corner-tr.png'); background-position: top right;
}
#threshold .corner-bl, .farewell .corner-bl {
  bottom: clamp(10px, 1.6vw, 26px); left: clamp(10px, 1.6vw, 26px);
  background-image: url('assets/corner-bl.png'); background-position: bottom left;
}

/* only what falls inside the four corners is painted */
#threshold .threshold-art,
.farewell  .threshold-art,
.farewell  .farewell-art {
  --cw: clamp(96px, 12.5vw, 208px);
  --ci: clamp(10px, 1.6vw, 26px);
  --edge: calc(var(--ci) + var(--cw) * 0.0720);
  clip-path: inset(var(--edge) var(--edge) var(--edge) var(--edge));
}
@media (max-width: 720px) {
  #threshold .corner-tl, .farewell .corner-tl,
  #threshold .corner-tr, .farewell .corner-tr,
  #threshold .corner-bl, .farewell .corner-bl,
  #threshold .corner-br, .farewell .corner-br { width: clamp(64px, 19vw, 108px); }
  #threshold .corner-tr, .farewell .corner-tr { top: 8px; right: 8px; }
  #threshold .corner-bl, .farewell .corner-bl { bottom: 8px; left: 8px; }
  #threshold .threshold-art, .farewell .threshold-art, .farewell .farewell-art {
    --cw: clamp(64px, 19vw, 108px); --ci: 8px;
  }
}

/* ===================================================================
   A LIGHT FESTIVE MARIGOLD GROUND - AND WHAT HAD TO COME WITH IT
   ===================================================================
   The deep marigold read as brown. This is the light, bright marigold
   of a festival morning instead: a saffron-cream heart at the lamp,
   opening out to warm marigold, with only a gentle amber falloff at
   the edges rather than a dark vignette.

   But lightening the ground breaks the thing that made the old one
   safe. Measured against the type these screens carry:

       ground                gold #ffc14a   cream #fff6d8
       deep    #6d3708           5.90:1         8.83:1
       light   #f0ae4e           1.16:1         1.73:1

   Gold letters on light marigold are gold on gold - the words would
   dissolve. The fix is NOT to darken the field back down; it is to
   give every glyph its own edge. A tight dark contact shadow does
   exactly that, the way gold foil reads on a marigold sari because
   of the shadow it casts, not because of any colour difference.

   Two different techniques, for a reason:

     - The statement lines are ordinary coloured text, so text-shadow
       works directly.

     - WELCOME. / THANK YOU., the names and the titles are painted
       with background-clip: text - the glyphs are transparent windows
       onto a gradient. text-shadow on transparent glyphs paints the
       shadow INSIDE the letters, where it shows straight through the
       window and looks like dirt. filter: drop-shadow() is applied to
       the finished, already-clipped rendering instead, so it hugs the
       outside of each letterform and never bleeds into the fill.

   An earlier rule in this file sets filter: none !important on these
   elements, so these must carry !important and sit after it - which
   they do, being the last block in the file.

   The corner ornaments and the mandalas are also gold, and were
   relying on the dark ground for their contrast, so they take the
   same treatment. Nothing here touches position, size, colour or
   animation: drop-shadow paints, it does not move anything, and every
   existing keyframe keeps running untouched.
   =================================================================== */
#threshold, .farewell {
  background:
    radial-gradient(ellipse 54% 44% at 50% 34%, rgba(255, 214, 132, 0.38), transparent 66%),
    radial-gradient(ellipse 72% 62% at 8% 6%,   rgba(255, 186, 84, 0.20), transparent 64%),
    radial-gradient(ellipse 76% 68% at 94% 96%, rgba(126, 62, 8, 0.42), transparent 66%),
    radial-gradient(ellipse 132% 110% at 50% 50%, transparent 42%, rgba(110, 54, 6, 0.58) 100%),
    linear-gradient(116deg, #a9670f 0%, #bc7717 22%, #c8821f 48%, #ad6910 74%, #8e520a 100%)
    !important;
  background-color: #b57113 !important;
}

/* plain coloured text - text-shadow is safe here */
#threshold .threshold-line,
.farewell  .farewell-line {
  text-shadow:
    0 0 2px rgba(74, 30, 4, 0.85),
    0 1px 1px rgba(74, 30, 4, 0.60),
    0 2px 14px rgba(74, 30, 4, 0.32) !important;
}

/* gradient-clipped text - drop-shadow only, never text-shadow */
#threshold .threshold-welcome, .farewell .farewell-welcome,
#threshold .threshold-names,   .farewell .farewell-names,
#threshold .threshold-title,   .farewell .farewell-title {
  filter:
    drop-shadow(0 0 2px rgba(74, 30, 4, 0.80))
    drop-shadow(0 1px 1px rgba(74, 30, 4, 0.55))
    drop-shadow(0 3px 14px rgba(74, 30, 4, 0.28)) !important;
}

/* the gold rules under those headings */
#threshold .t-divider, .farewell .t-divider {
  filter: drop-shadow(0 1px 1px rgba(74, 30, 4, 0.55))
          drop-shadow(0 0 3px rgba(74, 30, 4, 0.45)) !important;
}

/* the four corner ornaments, gold on gold without this */
#threshold .corner-tl, #threshold .corner-tr,
#threshold .corner-bl, #threshold .corner-br,
.farewell  .corner-tl, .farewell  .corner-tr,
.farewell  .corner-bl, .farewell  .corner-br {
  filter: drop-shadow(0 1px 2px rgba(74, 30, 4, 0.55))
          drop-shadow(0 0 4px rgba(74, 30, 4, 0.40));
  opacity: 1;
}

/* the mandalas kept their warm glow; on a light ground they need a
   dark edge instead, or the linework disappears into the field */
#threshold .t-mandala .tm-art, .farewell .t-mandala .tm-art {
  filter: drop-shadow(0 0 2px rgba(94, 44, 6, 0.55))
          drop-shadow(0 1px 3px rgba(94, 44, 6, 0.40)) !important;
}

/* ===================================================================
   BEIGE GROUND - AND THE FOUR THINGS THAT HAD TO MOVE WITH IT
   ===================================================================
   Beige (#f5f5dc) is a LIGHT ground, and every decorative element on
   these two screens was built to sit on a dark one. Measured against
   beige, the existing palette:

       gold  #f0b45a  on beige   1.67:1   invisible
       gold  #ffc14a  on beige   1.46:1   invisible
       amber #c8912c  on beige   2.52:1   still too weak

   So swapping only the background would have produced a blank beige
   panel with a lamp on it. Four things move together:

   1. THE GRADIENT-CLIPPED TEXT. WELCOME. / THANK YOU., the names and
      the titles are transparent glyphs over a gradient. The gradients
      are re-pitched from pale gold to burnt saffron - same structure,
      same sweep animation, same keyframes, only the stops change, so
      the moving sheen still travels exactly as before.

          #8a4a10 on beige   6.19:1
          #7d4a10 on beige   6.64:1

   2. THE SHADOWS INVERT. The dark contact shadow that rescued gold on
      marigold is wrong here - dark text on a light ground already has
      its edge, and a dark halo would only smudge it. Reduced to a
      faint warm lift.

   3. THE ORNAMENTS ARE DARKENED OPTICALLY, NOT REDRAWN. The corner
      pieces, the rules and the mandalas are golden-yellow PNGs. Rather
      than re-key four assets, a brightness/saturate filter walks the
      gold down to antique bronze. It is reversible in one line and the
      artwork is untouched on disk.

   4. THE DUST. Pale sparks on a pale ground are nothing at all, so
      they are darkened to the same bronze. They keep their drift
      animations.

   The diya is deliberately left alone. It is the one thing on the
   screen that is meant to be the brightest object, and it still is.
   =================================================================== */
#threshold, .farewell {
  background:
    radial-gradient(ellipse 54% 44% at 50% 34%, rgba(255, 252, 232, 0.85), transparent 64%),
    radial-gradient(ellipse 72% 62% at 8% 6%,   rgba(255, 250, 226, 0.55), transparent 62%),
    radial-gradient(ellipse 76% 68% at 94% 96%, rgba(214, 204, 166, 0.55), transparent 64%),
    radial-gradient(ellipse 132% 112% at 50% 50%, transparent 44%, rgba(198, 188, 150, 0.50) 100%),
    linear-gradient(116deg, #f7f7e2 0%, #f5f5dc 26%, #f2f1d4 52%, #eeecca 78%, #e8e5c0 100%)
    !important;
  background-color: #f5f5dc !important;
}

/* 1a - WELCOME. / THANK YOU. : same radial + sweep, burnt-saffron stops */
#threshold .threshold-welcome,
.farewell  .farewell-welcome {
  background-image:
    linear-gradient(100deg, rgba(255,255,255,0) 36%, rgba(255,236,190,0.85) 50%, rgba(255,255,255,0) 64%),
    radial-gradient(circle at 50% 38%, #b8791c 0%, #a5620f 28%, #8a4a10 55%, #6b3a0a 100%) !important;
}

/* 1b - the names and the titles : same seSweep, burnt-saffron stops */
#threshold .threshold-names, .farewell .farewell-names,
#threshold .threshold-title, .farewell .farewell-title {
  background-image: linear-gradient(100deg,
      #8a4a10 0%, #8a4a10 38%, #c8912c 50%, #8a4a10 62%, #8a4a10 100%) !important;
}

/* 1c - the statement lines are ordinary text */
#threshold .threshold-line,
.farewell  .farewell-line {
  color: #7d4a10 !important;
}

/* 2 - shadows inverted: a faint warm lift, not a dark halo */
#threshold .threshold-line,
.farewell  .farewell-line {
  text-shadow: 0 1px 0 rgba(255, 255, 244, 0.75) !important;
}
#threshold .threshold-welcome, .farewell .farewell-welcome,
#threshold .threshold-names,   .farewell .farewell-names,
#threshold .threshold-title,   .farewell .farewell-title {
  filter: drop-shadow(0 1px 0 rgba(255, 255, 244, 0.70))
          drop-shadow(0 2px 10px rgba(120, 96, 40, 0.18)) !important;
}

/* 3 - the gold artwork walked down to antique bronze */
#threshold .t-divider, .farewell .t-divider {
  filter: brightness(0.52) saturate(1.55)
          drop-shadow(0 1px 0 rgba(255, 255, 244, 0.6)) !important;
}
#threshold .corner-tl, #threshold .corner-tr,
#threshold .corner-bl, #threshold .corner-br,
.farewell  .corner-tl, .farewell  .corner-tr,
.farewell  .corner-bl, .farewell  .corner-br {
  filter: brightness(0.50) saturate(1.6)
          drop-shadow(0 1px 1px rgba(255, 255, 244, 0.5));
  opacity: 0.9;
}
#threshold .t-mandala .tm-art, .farewell .t-mandala .tm-art {
  filter: brightness(0.55) saturate(1.5) opacity(0.85) !important;
}

/* 4 - the dust, or it is nothing at all on a pale ground */
#threshold .dust i, .farewell .dust i {
  filter: brightness(0.45) saturate(1.6) !important;
}

/* ===================================================================
   THE RULES UNDER THE HEADINGS GO BACK TO GOLD
   ===================================================================
   The beige block above walked the ornament PNGs down to antique
   bronze so they would hold against a light ground. This puts the
   rules - and only the rules - back to their original gold.

   The brightness() is dropped entirely, so the artwork paints at its
   own colour rather than a darkened version of it. What replaces it is
   edge definition instead of tone: gold on beige is 1.67:1, so a thin
   warm contact shadow under the rule is what makes it read, the same
   way a gold thread reads on cream fabric because of the shadow it
   throws rather than any difference in lightness.

   A small saturate() lift keeps the gold from going chalky once it is
   no longer sitting on a dark field.

   The corner ornaments, the mandalas and the dust stay bronze - only
   the four rules under the headings are gold, which is what was asked
   for. Sizes, positions and animations are untouched: filter paints,
   it does not move anything.
   =================================================================== */
#threshold .t-divider, .farewell .t-divider,
#threshold .threshold-welcome::after, .farewell .farewell-welcome::after {
  filter:
    saturate(1.18) brightness(1.02)
    drop-shadow(0 1px 1px rgba(122, 78, 18, 0.55))
    drop-shadow(0 0 2px rgba(140, 92, 24, 0.40)) !important;
  opacity: 1 !important;
}

/* ===================================================================
   ALL HEADING TEXT -> DARK SAFFRON
   ===================================================================
   WELCOME. / THANK YOU., the names, and the titles on both screens.

   These are transparent glyphs clipped over a gradient, not solid
   colour, so "dark saffron" is applied by re-pitching the gradient
   stops rather than by setting color - setting color would do nothing
   at all, because -webkit-text-fill-color: transparent is what
   actually governs the fill. The gradient geometry, the sizes and the
   sweep keyframes are all left exactly as they were, so the moving
   highlight still travels across the words the way it did.

   The colour is a true saffron hue held dark, not a brown:

       #a85207   hue 28  sat 0.96   4.90:1 on beige   the body
       #8f4406   hue 28  sat 0.96   5.9 :1            the deep edge
       #d2761a   hue 30  sat 0.88   2.99:1            the travelling
                                                       highlight only

   The highlight stop is deliberately the only light value, and it is
   narrow - it is a sheen crossing the letters, not the letter colour,
   so the words still read as dark saffron at rest.
   =================================================================== */

/* WELCOME. / THANK YOU. - radial body + the sweep that crosses it */
#threshold .threshold-welcome,
.farewell  .farewell-welcome {
  background-image:
    linear-gradient(100deg, rgba(255,255,255,0) 36%, rgba(226,140,48,0.90) 50%, rgba(255,255,255,0) 64%),
    radial-gradient(circle at 50% 38%, #c2620d 0%, #b1560a 28%, #a85207 55%, #8f4406 100%) !important;
}

/* the names and the titles - one linear sweep each */
#threshold .threshold-names, .farewell .farewell-names,
#threshold .threshold-title, .farewell .farewell-title {
  background-image: linear-gradient(100deg,
      #a85207 0%, #a85207 38%, #d2761a 50%, #a85207 62%, #a85207 100%) !important;
}

/* ===================================================================
   THE TWO MANDALAS: INNER PETALS GOLD, OUTER PETALS DARK SAFFRON
   ===================================================================
   Each corner carries two stacked SVGs:

       .tm-under  ->  #mChakra   the OUTER mandala (the wide wheel,
                                 its rim ticks run out to r=197)
       .tm-art    ->  #mPadma    the INNER mandala (the lotus that
                                 sits inside it)

   Inside each of those, the large petals are identifiable by their
   stroke weight - they are the only paths drawn at that weight, and
   the concentric rings around them are <circle> elements, not paths,
   so a path[stroke-width] selector picks out the petals and nothing
   else:

       #mPadma  path @ 1.05   16 large petals   ->  GOLD
       #mChakra path @ 0.90   24 large petals   ->  DARK SAFFRON

   The smaller inner rings (0.85 / 0.75), the rim scallop (1.0), the
   spokes and the bindus are all left exactly as they were.

   WHY ATTRIBUTE SELECTORS RATHER THAN EDITING THE SVG: the artwork is
   drawn once in <symbol> and instanced four times through <use>. Rules
   that match the original elements style every clone, so this colours
   both mandalas on both screens from one place, and the markup on both
   index.html and the farewell template stays untouched.

   The paths carry stroke="currentColor" as a PRESENTATION ATTRIBUTE,
   which sits below author CSS in the cascade - so these rules win
   without needing !important, and currentColor still governs
   everything not named here.

   ONE THING THAT HAD TO CHANGE WITH IT: the beige block darkened the
   whole mandala with brightness(0.55). Left in place it would have
   dragged both new colours down and neither would have been the colour
   asked for. It is replaced with definition rather than tone - a warm
   contact shadow, which is what carries gold on a pale ground.
   =================================================================== */

/* inner mandala - the lotus - large petals in gold */
#mPadma path[stroke-width="1.05"] {
  stroke: #e0b64a;
  stroke-width: 1.25;
}

/* outer mandala - the wheel - large petals in dark saffron */
#mChakra path[stroke-width="0.9"] {
  stroke: #a85207;
  stroke-width: 1.05;
}

/* tone-darkening dropped; definition put back in its place */
#threshold .t-mandala .tm-art, .farewell .t-mandala .tm-art {
  filter:
    drop-shadow(0 1px 1px rgba(122, 78, 18, 0.45))
    drop-shadow(0 0 2px rgba(140, 92, 24, 0.30)) !important;
  opacity: 0.95 !important;
}

/* ===================================================================
   THE SITE: FESTIVE MARIGOLD NAVIGATION BAR, BEIGE PAGES
   ===================================================================
   Two changes, and the second one forces several small ones with it.

   THE PAGES -> BEIGE. The body carried a warm cream field with gold
   radial washes. It is re-pitched to beige, keeping the same wash
   geometry so the page still has depth rather than going flat. The
   section panels have no background of their own, so they simply let
   the new ground through - which is why this one change reaches every
   section of the site.

   THE BAR -> FESTIVE MARIGOLD. This is the one saturated band on an
   otherwise pale page, so it needs to carry its own text rather than
   inheriting the page's.

   THE NAVIGATION TEXT HAD TO DARKEN. Every link was var(--text)
   #3d2814, chosen against near-white. On marigold that falls to
   3.53:1 - and these links are 16px, just under the 18.66px bold
   threshold where the lower large-text standard would apply, so 3.53
   is not enough. Deepening to #2a1806 restores it:

       #3d2814 on #c8821f   4.41:1
       #2a1806 on #c8821f   5.41:1   <- used
       #2a1806 on #b8731a   4.64:1   (the bar's darkest stop)

   The same applies to the brand, the burger and the Exit link, all of
   which were coloured for a pale bar.

   THE HOVER PILL HAD TO INVERT. It was rgba(212,160,58,0.16) - a gold
   tint that was visible on near-white and is completely invisible on
   marigold. It becomes a dark tint instead, so hover still reads.

   The mobile drop-down panel is the bar's own surface, so it takes the
   marigold too - otherwise opening the menu would have dropped a cream
   sheet out of a marigold bar.

   Nothing here touches the start or exit screens, which keep beige and
   their own treatment.
   =================================================================== */

/* ---- the pages ---- */
body {
  background:
    radial-gradient(ellipse at 20% 0%,   rgba(255, 246, 206, 0.55), transparent 42%),
    radial-gradient(ellipse at 82% 8%,   rgba(250, 244, 210, 0.45), transparent 38%),
    radial-gradient(ellipse at 50% 100%, rgba(226, 220, 180, 0.45), transparent 46%),
    linear-gradient(160deg, #f7f7e4 0%, #f5f5dc 34%, #f2f1d4 68%, #edecc8 100%)
    !important;
  background-color: #f5f5dc !important;
  background-attachment: fixed !important;
}
@media print { body { background: #fff !important; } }

/* ---- the bar ---- */
.topbar {
  background:
    linear-gradient(120deg, #e2a63e 0%, #d4922b 38%, #c8821f 72%, #bc7717 100%)
    !important;
  border-bottom: 1px solid rgba(90, 44, 6, 0.28) !important;
  box-shadow: 0 3px 18px rgba(90, 44, 6, 0.18) !important;
}
.topbar.scrolled {
  box-shadow: 0 5px 26px rgba(90, 44, 6, 0.28) !important;
}

/* ---- everything written on the bar ---- */
.topbar .nav-links a,
.topbar .nav-links .nav-exit-btn,
.topbar .nav-links .admin-link,
.topbar .brand strong {
  color: #2a1806 !important;
  text-shadow: 0 1px 0 rgba(255, 243, 205, 0.35) !important;
}
.topbar .brand em {
  color: rgba(42, 24, 6, 0.78) !important;
}
.topbar .menu-toggle {
  background: rgba(255, 246, 216, 0.28) !important;
  border-color: rgba(90, 44, 6, 0.38) !important;
}
.topbar .menu-toggle span {
  background: #2a1806 !important;
}

/* the hover pill inverted - a gold tint is nothing on marigold */
.topbar .nav-links a:hover,
.topbar .nav-links a.active,
.topbar .nav-links .nav-exit-btn:hover {
  background: rgba(66, 32, 6, 0.20) !important;
  color: #1f1104 !important;
  text-shadow: 0 1px 0 rgba(255, 243, 205, 0.45) !important;
}

/* the drop-down is the bar's own surface, so it matches the bar */
.nav-links {
  background: linear-gradient(160deg, #dfa23b 0%, #cd8a24 60%, #bc7717 100%) !important;
  border-color: rgba(90, 44, 6, 0.32) !important;
}

/* ===================================================================
   THE LARGE PETALS FILLED, NOT JUST OUTLINED
   ===================================================================
       inner mandala  (#mPadma,  the lotus)  large petals -> GOLD
       outer mandala  (#mChakra, the wheel)  large petals -> DARK SAFFRON

   Both symbols are drawn as pure linework: every path carries
   fill="none", set on the path itself and again on the wrapping <g>.
   Those are PRESENTATION ATTRIBUTES, which sit at the very bottom of
   the cascade - below even an unweighted author rule - so a plain
   fill: declaration here overrides them without needing !important.

   STACKING IS WHY THIS WORKS AND DOES NOT HIDE ANYTHING. The wheel is
   .tm-under and the lotus is painted over it, so the dark saffron
   petals fill in BEHIND the gold ones rather than covering them. Had
   the order been the other way round, filling the wheel solid would
   have buried the lotus completely.

   The strokes are left in place at their existing colours, so each
   petal keeps its drawn edge and the ring still reads as separate
   petals rather than as one solid mass.

   The rings at 0.85 and 0.75, the rim scallop, the spokes and the
   bindus stay open linework - only the large petals are filled.
   =================================================================== */

/* inner mandala - the lotus - large petals filled gold */
#mPadma path[stroke-width="1.05"] {
  fill: #e0b64a;
}

/* outer mandala - the wheel - large petals filled dark saffron */
#mChakra path[stroke-width="0.9"] {
  fill: #a85207;
}


/* ===================================================================
   THE OUTER MANDALA'S LARGE PETALS: SAFFRON -> RED
   ===================================================================
   #a85207 was the dark saffron; #b4231d is a kumkum red - a true red
   hue (357 deg) rather than an orange pushed dark, so it reads as red
   next to the gold instead of as a deeper saffron.

   Both the stroke and the fill change together, or the petals would
   have kept a saffron outline around a red body.

   On beige it measures 6.05:1, slightly stronger than the saffron it
   replaces (4.90:1), so the wheel reads a little more firmly than
   before against the pale ground.

   The inner lotus keeps its gold petals, and the lotus linework keeps
   the red it already carried - so the two reds now agree with each
   other rather than sitting one hue apart.
   =================================================================== */
#mChakra path[stroke-width="0.9"] {
  stroke: #b4231d;
  fill: #b4231d;
}

/* ===================================================================
   THE SMALL INNER PETAL RINGS FILLED RED
   ===================================================================
   Both symbols carry a second, smaller petal ring inside the large
   one, and in both it is the 0.85 stroke weight - 16 petals in the
   lotus, 18 in the wheel. As with the large petals, these are the only
   paths at that weight, so the selector catches the ring and nothing
   around it.

   FILL ONLY, STROKES LEFT ALONE - which is what was asked for, and it
   also happens to be what keeps the two rings distinguishable:

     lotus  inner ring  red fill, red outline    (currentColor #d6473c)
     wheel  inner ring  red fill, gold outline   (currentColor #e0b64a)

   The gold outline on the wheel's ring is the only thing separating it
   from the large red petals filling in behind it. Had the strokes been
   recoloured too, that ring would have merged into the petals behind
   and the mandala would have lost a whole level of detail.

   The 0.75 florets at the very centre are untouched - they sit inside
   the r=18 hub and are small enough that a solid fill would read as a
   blot rather than as petals.
   =================================================================== */
#mPadma  path[stroke-width="0.85"],
#mChakra path[stroke-width="0.85"] {
  fill: #b4231d;
}

/* ===================================================================
   1. THE BORDER CARRIED OUT TO MEET THE MANDALAS - PROPERLY THIS TIME
   ===================================================================
   The first attempt drew a solid CSS bar next to the ornament, which
   is why it read as a second line sitting under the real one: a bar
   cannot match the artwork's weight, its soft edges or its exact
   position, so any error shows up as a parallel stripe.

   THIS EXTENSION IS THE ARTWORK ITSELF. Four strips were cut straight
   out of corner-tl.png at x=880..920 and y=880..920 - regions of the
   plain arm well clear of the scrollwork - and flipped for the other
   two edges:

       arm-h-top.png     40 x 1223     arm-h-bottom.png  40 x 1223
       arm-v-left.png  1200 x   40     arm-v-right.png 1200 x   40

   Each strip is a FULL-TILE slice: full height for the horizontal
   arms, full width for the vertical ones. That is what guarantees the
   join. Sized with background-size: auto 100% (or 100% auto) inside a
   box the same height (or width) as the corner tile, the strip is
   scaled by exactly the factor the tile is scaled by, so the rule
   inside it lands on precisely the same line - measured, not matched
   by eye:

       horizontal arm   rows 78..100 of 1223   outer edge 6.50% of cw
       vertical   arm   cols 78..99  of 1200   outer edge 6.50% of cw

   The two agree to three decimal places, which is what lets one
   constant serve both axes.

   THE LENGTH IS NO LONGER GUESSWORK. Rather than infer the mandala's
   size from rules scattered through the file, this block declares it
   (--ms) and then applies it, so the value used for the maths and the
   value used for layout cannot drift apart. The mandala is a circle of
   radius 0.35ms centred 0.12ms in from the corner; solving it against
   the clip line puts the arc crossing at 0.47ms from the corner, which
   reaches further at some viewport, the extension simply has no length
   rather than sticking out past the artwork.

   AND THE SEAM ITSELF: --edge, which clips the mandalas, is moved from
   the rule's centre line to 7.00% - just inside the rule's own body
   (6.50%..8.38%). The mandala now runs UNDER the border and the border
   covers the join, instead of the two meeting at a shared edge where
   any rounding leaves a hairline gap.
   =================================================================== */
#threshold, .farewell {
  --cw: clamp(96px, 12.5vw, 208px);
  --ci: clamp(10px, 1.6vw, 26px);
  --th: calc(var(--cw) * 1223 / 1200);

}
@media (max-width: 720px) { #threshold, .farewell { --cw: clamp(64px, 19vw, 108px); --ci: 8px; } }


/* ===================================================================
   2. THE SCREEN BORDERS BACK TO GOLD
   ===================================================================
   The beige block had put brightness(0.50) saturate(1.6) on the
   corners, which is what turned them the olive-bronze in the
   screenshot. Dropping it lets the artwork paint at its own gold
   (#fed05b in the file). What replaces the darkening is definition
   rather than tone - a warm contact shadow, which is what carries gold
   on a pale ground.

   Because the extension strips are pseudo-elements of the corners,
   they inherit this filter automatically and cannot fall out of step
   with the tile they continue.
   =================================================================== */
#threshold .corner-tl, #threshold .corner-tr,
#threshold .corner-bl, #threshold .corner-br,
.farewell  .corner-tl, .farewell  .corner-tr,
.farewell  .corner-bl, .farewell  .corner-br {
  filter:
    saturate(1.12)
    drop-shadow(0 1px 1px rgba(122, 78, 18, 0.55))
    drop-shadow(0 0 2px rgba(140, 92, 24, 0.40)) !important;
  opacity: 1 !important;
}

/* ===================================================================
   3. THE DIYA LIGHTS AT ONCE
   ===================================================================
   The lamp was built as a staged sequence, and the stages were long:

       .diya-lamp   0.20s   .diya-wick   1.10s
       .flame       1.50s   .flame-halo  1.90s
       .diya-glow   2.00s   .diya-halo   2.30s

   so the flame did not appear for a second and a half and the lamp was
   not fully lit for well over two - which is the wait you were seeing.

   The ORDER is kept, because it is what makes the lamp look like it is
   being lit rather than switched on; only the spacing is compressed,
   into roughly a fifth of a second in total. The durations come down
   with the delays so nothing is left half-finished behind a shorter
   delay.

   The breathing loops used to wait 3.3s-4.7s before starting, leaving
   a dead pause after the lamp caught. They now begin as the entry
   finishes, so the flame is alive immediately.
   =================================================================== */
#threshold .diya-lamp, .farewell .diya-lamp {
  animation: lampSettle 0.55s var(--ease-spring) 0s both !important;
}
#threshold .diya-wick, .farewell .diya-wick {
  animation: wickRise 0.45s var(--ease-spring) 0.06s both !important;
}
#threshold .flame, .farewell .flame {
  animation: flameCatch 0.60s var(--ease-spring) 0.12s both,
             flameSway 7.3s ease-in-out 0.5s infinite,
             flameBreathe 3.9s ease-in-out 0.5s infinite !important;
}
#threshold .flame-halo, .farewell .flame-halo {
  animation: glowSwell 0.70s var(--ease-soft) 0.16s both,
             flameHaloBreathe 6.8s ease-in-out 0.7s infinite !important;
}
#threshold .diya-glow, .farewell .diya-glow {
  animation: glowSwell 0.70s var(--ease-soft) 0.18s both,
             glowBreathe 5.7s ease-in-out 0.8s infinite !important;
}
#threshold .diya-halo, .farewell .diya-halo {
  animation: glowSwell 0.80s var(--ease-soft) 0.22s both,
             haloBreathe 9.1s ease-in-out 0.9s infinite !important;
}
#threshold .d-rim, .farewell .d-rim {
  animation: fadeUpIn 0.55s var(--ease-soft) 0.10s both !important;
}

/* reduced motion must be re-asserted after any new animation rule, or
   these later, more specific declarations would silently override it */
@media (prefers-reduced-motion: reduce) {
  #threshold .diya-lamp, .farewell .diya-lamp,
  #threshold .diya-wick, .farewell .diya-wick,
  #threshold .flame, .farewell .flame,
  #threshold .flame-halo, .farewell .flame-halo,
  #threshold .diya-glow, .farewell .diya-glow,
  #threshold .diya-halo, .farewell .diya-halo,
  #threshold .d-rim, .farewell .d-rim {
    animation: none !important;
    opacity: 1 !important;
  }
}

/* The bubbles were removed from both screens; the embers below are
   the diya's own, and keep the celestial gold they were given. */

/* the embers off the flame, same gold */
#threshold .ember, .farewell .ember {
  background: radial-gradient(circle at 50% 50%,
    #fffdf2 0%, #ffe9a6 34%, #f0be4c 72%, rgba(214, 150, 40, 0) 100%) !important;
  box-shadow: 0 0 5px 1px rgba(255, 214, 120, 0.75),
              0 0 0 1px rgba(122, 76, 12, 0.35) !important;
}

/* the mandala sparks, lifted to the same celestial ramp */
#threshold .tm-spark2, .farewell .tm-spark2 {
  background: linear-gradient(135deg, #fffdf2 0%, #ffe9a6 42%, #f2c855 74%, #dda23a 100%) !important;
  box-shadow: 0 0 7px rgba(255, 224, 160, 0.85),
              0 0 2px rgba(122, 76, 12, 0.40) !important;
  filter: none !important;
}


/* ===================================================================
   1. THE NAVIGATION BAR IN CHIFFON GOLD
   ===================================================================
   #f7e9b0 - the chiffon gold from the cream sheet. The bar swings from
   a dark saturated band to a pale one, which inverts everything that
   was set up for the marigold:

     - THE TEXT stays at #2a1806, and that is now generous rather than
       tight: 13.9:1 against the bar's lightest stop and 11.2:1 against
       its deepest. The colour was chosen when it had to clear 4.5 on
       marigold, and it clears the pale ground comfortably too, so
       nothing has to move.

     - THE HOVER PILL had to change AGAIN, and for the mirror-image
       reason it changed last time. On marigold it needed a dark tint
       because a gold tint vanished; on chiffon gold a heavy brown tint
       reads as a bruise, so it becomes a soft gold wash instead.

     - THE EDGE AND SHADOW soften. A pale bar sitting on a beige page
       has almost no tonal step between them, so the hairline under it
       is doing the separating work now and the shadow only hints.

     - THE BURGER's fill goes to white, which reads as a raised control
       on a pale bar where the translucent cream it had before would
       have disappeared.
   =================================================================== */
.topbar {
  background: linear-gradient(120deg, #fbf2ce 0%, #f7e9b0 44%, #f2e0a0 100%) !important;
  border-bottom: 1px solid rgba(140, 92, 24, 0.30) !important;
  box-shadow: 0 2px 12px rgba(120, 78, 18, 0.10) !important;
}
.topbar.scrolled { box-shadow: 0 4px 20px rgba(120, 78, 18, 0.18) !important; }

.topbar .nav-links a,
.topbar .nav-links .nav-exit-btn,
.topbar .nav-links .admin-link,
.topbar .brand strong {
  color: #2a1806 !important;
  text-shadow: 0 1px 0 rgba(255, 253, 240, 0.55) !important;
}
.topbar .brand em { color: rgba(58, 36, 10, 0.72) !important; }

.topbar .nav-links a:hover,
.topbar .nav-links a.active,
.topbar .nav-links .nav-exit-btn:hover {
  background: rgba(190, 140, 40, 0.22) !important;
  color: #1f1104 !important;
  text-shadow: 0 1px 0 rgba(255, 253, 240, 0.65) !important;
}

.topbar .menu-toggle {
  background: rgba(255, 255, 255, 0.55) !important;
  border-color: rgba(140, 92, 24, 0.34) !important;
}
.topbar .menu-toggle span { background: #2a1806 !important; }

.nav-links {
  background: linear-gradient(160deg, #fbf2ce 0%, #f7e9b0 58%, #f0dc98 100%) !important;
  border-color: rgba(140, 92, 24, 0.28) !important;
}

/* ===================================================================
   2. THE RULES AROUND THE NAMES: THINNER, SMOOTHER, BETTER CURVES
   ===================================================================
   These two use their own artwork - ornament-fr7-top.png and
   ornament-fr7-bottom.png - which nothing else on the site references.
   That is why they could be reworked at all without touching the rules
   under WELCOME., THANK YOU. or the titles, which share a different
   pair of files.

   The outline is REBUILT FROM A SIGNED DISTANCE FIELD rather than
   filtered. The alpha is upscaled 3x, a signed distance is computed
   from its 0.5 contour, that field is blurred - which smooths the
   CURVE itself rather than the pixels, so the scrollwork comes back
   rounder instead of softer - and the contour is then re-cut with a
   1.5px ramp and resampled down. Blurring a distance field and
   blurring an image are not the same operation: one moves the edge,
   the other fogs it.

   THINNING PROPORTIONALLY IS THE PART THAT MATTERS. A flat erosion
   removes every stroke thinner than twice the shift, and at the weight
   needed here that punched holes through the finest curls - visible in
   the first attempt. So the shift is capped at a fraction of the LOCAL
   half-thickness, measured from the interior distance: a heavy stroke
   gives up the full amount, a hairline gives up almost nothing.

   Measured before and after:

       top     stroke 11 -> 8 px    4 ink pieces -> 4    6 gaps -> 6
       bottom  stroke 11 -> 9 px    4 ink pieces -> 4    6 gaps -> 6

   The piece counts are the check that matters: had a curl broken, the
   ink would have split into more pieces; had a loop closed up, the
   enclosed gaps would have dropped. Both are unchanged, so the drawing
   is intact - only lighter.

   Colour is re-sampled from the nearest FULLY solid pixel rather than
   carried over, so no half-transparent edge colour ends up inside the
   new, narrower outline.

   The canvases keep their original dimensions, so the aspect-ratios,
   the 33.30em width and the extension either side are all untouched.
   =================================================================== */
#threshold .t-fr7-top, .farewell .t-fr7-top {
  background-image: url('assets/ornament-fr7-top-fine.png') !important;
}
#threshold .t-fr7-bot, .farewell .t-fr7-bot {
  background-image: url('assets/ornament-fr7-bottom-fine.png') !important;
}

/* ===================================================================
   THEIR RECORDED MESSAGES - VIRTUES AND WISDOM
   ===================================================================
   Option 9: the card carries only a quiet invitation, and pressing it
   opens a panel of its own with everything else dimmed. A 3-4 minute
   message deserves that - it is not something to half-listen to while
   scrolling past.

   THE BUTTON IS HIDDEN BY DEFAULT, in the markup, with the [hidden]
   attribute. app.js checks whether the audio file actually exists and
   only then reveals it. That is deliberate: until the recordings are
   uploaded the section looks exactly as it does today, with no control
   that leads nowhere, and the moment a file is dropped into
   assets/audio/ its button appears on the next load with nothing else
   to change.

   THE DIALOG IS NOT position: fixed BY ACCIDENT. It has to escape the
   section's stacking and sit over the navigation bar, which is at
   z-index 60 - hence 120 here.

   Colours follow the page: chiffon-gold panel, the same gold gradient
   the site's buttons already use, dark cocoa text at better than 10:1.
   =================================================================== */
.voice-open[hidden] { display: none !important; }
.voice-open {
  display: inline-flex; align-items: center; gap: 9px;
  margin: 0 0 14px;
  padding: 8px 16px 8px 12px;
  border: 1px solid rgba(140, 85, 20, 0.30);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.55);
  color: var(--heading, #5c2d0a);
  font-family: inherit; font-size: 16px; font-weight: 600;
  cursor: pointer;
  transition: background .24s var(--ease-soft), border-color .24s var(--ease-soft),
              transform .24s var(--ease-spring), box-shadow .24s var(--ease-soft);
}
.voice-open:hover, .voice-open:focus-visible {
  background: linear-gradient(180deg, #f7dc93, #e0b348);
  border-color: rgba(140, 85, 20, 0.48);
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(120, 78, 18, 0.18);
}
.voice-ico {
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; flex: 0 0 auto;
  background: linear-gradient(180deg, #f7dc93, #c8912c);
  color: #4a2708; font-size: 11px; line-height: 1;
  border: 1px solid rgba(140, 85, 20, 0.35);
}

.voice-backdrop {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center;
  padding: 20px;
  background: rgba(58, 26, 4, 0.52);
  opacity: 0; pointer-events: none;
  transition: opacity .28s var(--ease-soft);
}
.voice-backdrop.open { opacity: 1; pointer-events: auto; }

.voice-dialog {
  width: min(440px, 94vw);
  background: linear-gradient(160deg, #fdf8e8 0%, #f9f0d4 60%, #f5e9c2 100%);
  border: 1px solid rgba(140, 85, 20, 0.30);
  border-radius: 20px;
  padding: 24px 24px 20px;
  box-shadow: 0 20px 60px rgba(58, 26, 4, 0.34);
  transform: translateY(14px) scale(0.985);
  transition: transform .3s var(--ease-spring);
}
.voice-backdrop.open .voice-dialog { transform: none; }

.voice-who { display: flex; align-items: center; gap: 13px; margin-bottom: 14px; }
.voice-av {
  width: 50px; height: 50px; border-radius: 16px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: linear-gradient(145deg, #5c2d0a, #a9670f);
  color: #fff6d8; font-size: 23px; font-weight: 700;
}
.voice-nm { color: #5c2d0a; font-size: 19px; font-weight: 600; line-height: 1.2; }
.voice-rl { font-size: 14px; color: rgba(61, 40, 20, 0.75); }

.voice-quote {
  margin: 0 0 18px; font-style: italic; font-size: 17px; line-height: 1.6;
  color: #4a3018;
  padding-left: 14px; border-left: 2px solid rgba(200, 145, 44, 0.55);
}

.voice-row { display: flex; align-items: center; gap: 12px; }
.voice-btn, .voice-back {
  border-radius: 50%; flex: 0 0 auto;
  border: 1px solid rgba(140, 85, 20, 0.40);
  background: linear-gradient(180deg, #f7dc93, #e0b348 42%, #c8912c);
  color: #4a2708; display: grid; place-items: center; cursor: pointer;
}
.voice-btn { width: 48px; height: 48px; font-size: 16px; }
.voice-back { width: 34px; height: 34px; font-size: 11px; font-weight: 700; }
.voice-btn:hover, .voice-back:hover { filter: brightness(1.06); }

.voice-bar {
  position: relative; flex: 1; height: 7px; border-radius: 99px;
  background: rgba(140, 85, 20, 0.20); cursor: pointer;
}
.voice-fill {
  position: absolute; inset: 0 auto 0 0; width: 0; border-radius: 99px;
  background: linear-gradient(90deg, #c8912c, #e8bd5a);
}
.voice-time {
  font-family: 'Josefin Sans', system-ui, sans-serif;
  font-size: 13px; color: rgba(61, 40, 20, 0.78);
  min-width: 92px; text-align: right; font-variant-numeric: tabular-nums;
}
.voice-msg { margin: 14px 0 0; font-size: 15px; color: #8a3b12; min-height: 0; }
.voice-foot { display: flex; justify-content: flex-end; margin-top: 18px; }
.voice-close {
  border: 1px solid rgba(140, 85, 20, 0.30); background: rgba(255, 255, 255, 0.6);
  border-radius: 999px; padding: 7px 18px; font-family: inherit; font-size: 16px;
  color: #5c2d0a; cursor: pointer;
}
.voice-close:hover { background: linear-gradient(180deg, #f7dc93, #e0b348); }

@media (max-width: 560px) {
  .voice-time { min-width: 78px; font-size: 12px; }
  .voice-dialog { padding: 20px 18px 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .voice-backdrop, .voice-dialog, .voice-open { transition: none !important; }
  .voice-dialog { transform: none !important; }
}

/* ===================================================================
   THE MESSAGE PANEL GETS THE SAME TOP RULE AS EVERY OTHER TILE
   ===================================================================
   Every card on the site carries a 3px accent across its top - heading
   brown, through gold, into teal - inset 26px from each side. The
   message dialog was the one tile without it.

   The values here are not eyeballed to look similar; they are the
   exact ones from the rule that already serves .card, .timeline-item,
   .form-card and .tribute-card:

       top: 0;  inset-inline: 26px;  height: 3px;
       linear-gradient(90deg, var(--heading), var(--gold), var(--teal))
       border-radius: 0 0 3px 3px;  opacity: 0.9;

   Same variables, so if that palette is ever changed the dialog
   follows along instead of drifting out of step.

   position: relative is needed on the dialog for the rule to anchor
   to it, and overflow: hidden keeps the line's ends inside the 20px
   corner radius - without it the bar would cut across the rounded
   corners at full width.

   It is part of the panel itself, so it arrives and leaves with the
   panel on every open and close, for both parents.
   =================================================================== */
.voice-dialog {
  position: relative;
  overflow: hidden;
}
.voice-dialog::before {
  content: "";
  position: absolute;
  top: 0;
  inset-inline: 26px;
  height: 3px;
  background: linear-gradient(90deg, var(--heading), var(--gold), var(--teal));
  border-radius: 0 0 3px 3px;
  opacity: 0.9;
  pointer-events: none;
  z-index: 3;
}

/* ===================================================================
   THE RULE CARRIED OUT TO THE MANDALA'S LAST ARC
   ===================================================================
   Every earlier attempt measured the gap in JavaScript and got it
   wrong in a different way each time - too short, then the whole page,
   then too short again. That is gone. The stylesheet already fixes
   every number needed, so the rule is now anchored to the mandala
   itself and no length is computed at all:

       .tm--tr   transform: translate(38%, -38%)   !important
       .tm--bl   transform: translate(-38%, 38%)   !important
       .tm-art   inset: 15%
       #mChakra  outermost ring drawn at r=97 of a 200 viewBox

   From those four facts the two ends of the rule are exact:

     THE NEAR END - 38%. The mandala is pushed 38% of its own width out
     past the screen, so 38% in from its outer side IS the screen edge.
     The rule starts there, under the corner tile, and the two coincide
     rather than meeting.

     THE FAR END - 16%. The art sits 15% in, and the outer ring stops
     3% short of the art's own edge: 15% + (70% x 0.03 / 2) = 16.05%.
     So 16% of the mandala's box is precisely where the last arc - the
     ring with the vertical ladder strokes - reaches. Not measured, not
     estimated: read off the drawing.

   ALIGNMENT COMES FROM THE ARTWORK, NOT FROM A NUMBER. Each strip is a
   full-height slice of corner-tl.png. Given the corner tile's own
   height (--th) and the same --ci offset from the screen edge, the
   line inside the strip is scaled by exactly the factor the tile is
   scaled by and lands on exactly the same axis. There is no seam to
   align because the two are the same drawing at the same size.

   THE CLIP MOVED 0.8% OUTWARD. The rule's own outer edge sits at 6.50%
   of the tile; the clip was at 7.00%, so it was shaving the top 0.5%
   off the rule - about 1.5px - along the extension where the corner
   tile is not there to cover it. At 6.40% the whole rule is inside the
   clip and the mandala still tucks under it.

   z-index 4 lifts the rule above the mandala it crosses. Nothing here
   animates, and .tm-art keeps turning underneath exactly as before.
   =================================================================== */



/* ===================================================================
   THE LAST OF THE DIYA'S WAITING: THE EMBERS
   ===================================================================
   The lamp itself was sped up earlier - it now lights in about a fifth
   of a second - but the embers were left behind on their original
   schedule:

       .ember.e1   7.2s      .ember.e2   9.1s      .ember.e3   11.4s

   Those delays were written when the lamp took over two seconds to
   catch, so the first spark rising seven seconds in still felt like
   part of the same slow sequence. Against a lamp that is lit almost
   immediately it just reads as the diya being unfinished for the first
   ten seconds.

   They keep their stagger, because three embers leaving at once would
   read as a puff rather than as a flame throwing off sparks - only the
   scale changes, from seconds to fractions of one. emberRise itself is
   untouched at 4.4s, so each spark still drifts up at exactly the same
   speed it always did.
   =================================================================== */
#threshold .ember.e1, .farewell .ember.e1 { animation-delay: 0.55s !important; }
#threshold .ember.e2, .farewell .ember.e2 { animation-delay: 1.45s !important; }
#threshold .ember.e3, .farewell .ember.e3 { animation-delay: 2.30s !important; }

@media (prefers-reduced-motion: reduce) {
  #threshold .ember, .farewell .ember {
    animation: none !important;
    opacity: 0 !important;
  }
}

/* ===================================================================
   THE LAMP HELD STILL - ONLY THE FLAME MOVES
   ===================================================================
   Everything the diya was doing on arrival is switched off: the bowl
   settling, the wick rising, the rim fading up, and the two glows
   swelling and then breathing. What is left moving is the flame, its
   flicker layers and its own halo, which is what a lit lamp actually
   does - the body of it should be still.

   TWO DETAILS DECIDE WHETHER THIS LOOKS RIGHT OR BROKEN.

   opacity: 1 IS NOT OPTIONAL. Every one of these entry animations is
   declared with `both` and starts from opacity 0. Removing the
   animation without pinning opacity would leave the bowl, the wick,
   the rim and both glows sitting at their pre-animation state -
   invisible. The lamp would simply disappear.

   transform IS DELIBERATELY LEFT ALONE. Each of these parts is centred
   with transform: translateX(-50%) in its own rule. The usual reflex
   of adding transform: none alongside animation: none would drop that
   centring and shove every layer half its own width to the right. Only
   the animation is removed; the positioning transform stays.

   The embers are hidden rather than frozen. They are declared at
   opacity 0 and only become visible partway through emberRise, so
   pinning them to 1 would leave three motionless dots hanging in the
   air above the flame. A spark that does not rise is not a spark.
   =================================================================== */
#threshold .diya-lamp,  .farewell .diya-lamp,
#threshold .diya-wick,  .farewell .diya-wick,
#threshold .d-rim,      .farewell .d-rim,
#threshold .d-oil,      .farewell .d-oil,
#threshold .d-beads circle, .farewell .d-beads circle,
#threshold .d-petals path,  .farewell .d-petals path,
#threshold .diya-glow,  .farewell .diya-glow,
#threshold .diya-halo,  .farewell .diya-halo {
  animation: none !important;
  opacity: 1 !important;
}

#threshold .ember, .farewell .ember {
  animation: none !important;
  opacity: 0 !important;
}

/* ===================================================================
   THREE NEW MANDALAS ON THE PAGES
   ===================================================================
   mandalaM  a jaali lattice   - straight chords and arched infill
   mandalaN  a peacock-eye ring - sixteen feathers, each with an eye
   mandalaO  a kolam spiral    - dotted rounds threaded by a spiral

   None of the twelve already in the file is built on any of those
   ideas, which is what keeps them from reading as variations.

   THEY REUSE THE EXISTING SYSTEM RATHER THAN COPYING IT. Each carries
   .mandala, so it inherits that class's colour (#fdf3de) and its
   mandalaTint cycle, and .mandala svg gives it the same
   drop-shadow(0 0 7px rgba(226,182,74,0.5)) glow the others have. The
   movement classes are the site's own - mandala--turn and
   mandala--drift - so nothing new was invented for them.

   ONE RULE HAD TO BE UNDONE FOR THEM TO MOVE. A later declaration
   pins every .mandala to `mandalaTint 40s ... !important`, which
   replaces the whole animation shorthand and so throws away the turn
   and the drift with it. These three restate both, also !important,
   so the tint and the motion run together the way the flow was
   written to. It is scoped to .m-trio, so nothing else is affected.

   The flow layer stays switched off. This is a separate layer at the
   same depth, holding three placed drawings rather than a generated
   field.

   SIZES ARE DELIBERATELY FAR APART - 132, 208 and 300px. Three sizes
   within a few percent of each other would read as an error rather
   than as a choice.
   =================================================================== */
.mandala-trio {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  overflow: hidden;
  contain: strict;
}

.mandala-trio .m-trio {
  position: absolute;
  opacity: 1;
}

.mandala-trio .m-trio--m { width: 132px; height: 132px; top: 14vh;  left: 6vw;  }
.mandala-trio .m-trio--n { width: 208px; height: 208px; top: 52vh;  right: 5vw; }
.mandala-trio .m-trio--o { width: 300px; height: 300px; top: 78vh;  left: 22vw; }

/* the turn and the drift put back alongside the tint - see the note above */
.mandala-trio .m-trio.mandala--turn {
  animation: mandalaTurn 96s linear infinite,
             mandalaTint 40s cubic-bezier(0.42, 0, 0.58, 1) infinite !important;
}
.mandala-trio .m-trio--o.mandala--turn { animation-duration: 150s, 52s !important; }
.mandala-trio .m-trio.mandala--drift {
  animation: mandalaDrift 120s linear infinite,
             mandalaTint 46s cubic-bezier(0.42, 0, 0.58, 1) infinite !important;
}

.mandala-trio .m-trio > svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: drop-shadow(0 0 7px rgba(226, 182, 74, 0.5));
}

@media (max-width: 720px) {
  .mandala-trio .m-trio--m { width: 92px;  height: 92px;  }
  .mandala-trio .m-trio--n { width: 140px; height: 140px; }
  .mandala-trio .m-trio--o { width: 200px; height: 200px; }
}

@media (prefers-reduced-motion: reduce) {
  .mandala-trio .m-trio,
  .mandala-trio .m-trio > svg { animation: none !important; }
}

/* ===================================================================
   TWO LINES SET IN ITALICS
   ===================================================================
   1. The tribute form's note about admin approval.
   2. The gallery's description line.

   THE SECOND ONE IS NOT WHERE IT LOOKS. "A dignified family moment
   preserved with warmth, respect and gratitude." is not written in the
   page as text at all - it lives in a data-description attribute on
   the gallery card and is written into an element by app.js when a
   photograph is opened. There are two such elements, because the
   gallery has two ways of showing a picture: #galleryViewerText in the
   inline viewer and #galleryDialogText in the modal. Styling only one
   would have left the line upright in whichever of the two you happened
   to open. Both are covered.

   That also means every photograph's description is now italic, not
   just this one - they all pass through the same element, and one
   caption in italics among a dozen upright ones would read as a
   mistake rather than as a style.

   font-style is set on its own rather than through a font shorthand,
   which would reset the family, size and weight these already inherit.
   =================================================================== */
.moderation-note,
#galleryViewerText,
#galleryDialogText {
  font-style: italic;
}

/* ===================================================================
   THE FIFTH ENTRY IN FOUNDATIONAL LEGACY, SET IN ITALICS
   ===================================================================
   Jayant Tutorial Classes joins ABVP, the Vivekananda Rock Memorial,
   RSS Chennai and the Family Foundation. It reuses their markup
   exactly - a <span> holding a <b> and a <small> - so it inherits the
   whole of .foundation-highlights: the same card, the same spacing,
   the same behaviour when the row wraps. Nothing had to be styled to
   make it match.

   The italics are scoped to .fh-italic, so only this entry is italic
   and the four beside it stay upright.

   font-style is declared on the <b> and the <small> as well as the
   span. <b> and <small> do not resist italics themselves, but if
   either is ever given its own font shorthand elsewhere in this file
   that shorthand would reset font-style to normal, and the entry would
   quietly stand back up. Naming them makes it hold.
   =================================================================== */
#foundation .foundation-highlights .fh-italic,
#foundation .foundation-highlights .fh-italic b,
#foundation .foundation-highlights .fh-italic small {
  font-style: italic;
}

/* ===================================================================
   1. THE RULE ABOVE THE NAMES COMES OFF
   ===================================================================
   Only the upper one. The rule beneath the names stays, and so do the
   two under WELCOME. / THANK YOU. and under the titles. The names now
   sit on a single line rather than between a pair.

   display: none rather than opacity or visibility, so it takes its
   space with it - left in the flow it would have held a gap where it
   used to be, and the names would not have closed up under the
   statement.
   =================================================================== */
#threshold .t-fr7-top, .farewell .t-fr7-top { display: none !important; }

/* ===================================================================
   2. EVERY REMAINING ORNAMENT REBUILT AT TWICE THE RESOLUTION
   ===================================================================
   Not upscaled - rebuilt. Each outline is re-derived from a signed
   distance field: the alpha is taken to 2x, a distance field is
   computed from its 0.5 contour, that FIELD is blurred - which smooths
   the curve rather than the pixels - and the contour is then re-cut
   with a narrow 0.9px ramp. Enlarging an image can only ever make it
   blurrier; re-cutting the contour puts a hard, clean edge back at the
   new size.

   Colour is re-sampled from the nearest fully solid pixel first, so
   the wider outline can never pick up a half-transparent edge colour
   and go milky.

       orn-motif      1500x324   ->  3000x648
       orn-line       2600x329   ->  5200x658
       fr7 bottom     4400x140   ->  8800x280
       corners       1200x1223   -> 2400x2446

   The corner set is mirrored from the single sharpened master rather
   than sharpened four times, so all four stay identical.

   THIS MATTERS MOST ON YOUR SCREEN SPECIFICALLY. The screenshots are
   2556px wide for a 1278px viewport - a 2x display - so every ornament
   is being asked for twice the pixels the old files could give at the
   sizes they render. That is what was softening them.

   The contact shadows tighten from 2px to 1.4px to match: a soft
   shadow under a newly crisp edge just puts the blur back.
   =================================================================== */
#threshold .t-fr7-bot, .farewell .t-fr7-bot {
  background-image: url('assets/ornament-fr7-bottom-hi.png') !important;
}

#threshold .threshold-welcome::after, .farewell .farewell-welcome::after,
#threshold .t-title-orn, .farewell .t-title-orn {
  background-image: url('assets/orn-motif-hi.png'), url('assets/orn-line-hi.png') !important;
}

#threshold .corner-tl, .farewell .corner-tl { background-image: url('assets/corner-tl-hi.png') !important; }
#threshold .corner-tr, .farewell .corner-tr { background-image: url('assets/corner-tr-hi.png') !important; }
#threshold .corner-bl, .farewell .corner-bl { background-image: url('assets/corner-bl-hi.png') !important; }
#threshold .corner-br, .farewell .corner-br { background-image: url('assets/corner-br-hi.png') !important; }

#threshold .t-divider, .farewell .t-divider,
#threshold .threshold-welcome::after, .farewell .farewell-welcome::after {
  filter: saturate(1.18) brightness(1.02)
          drop-shadow(0 1px 1px rgba(122, 78, 18, 0.55))
          drop-shadow(0 0 1.4px rgba(140, 92, 24, 0.40)) !important;
}
#threshold .corner-tl, #threshold .corner-tr,
#threshold .corner-bl, #threshold .corner-br,
.farewell  .corner-tl, .farewell  .corner-tr,
.farewell  .corner-bl, .farewell  .corner-br {
  filter: saturate(1.12)
          drop-shadow(0 1px 1px rgba(122, 78, 18, 0.55))
          drop-shadow(0 0 1.4px rgba(140, 92, 24, 0.40)) !important;
}

/* ===================================================================
   THE THREE RULES: SHARPER, BUT NOT BY ADDING PIXELS
   ===================================================================
   Measured before changing anything, because more resolution was not
   going to help:

     the motif under WELCOME. and the titles
        renders 32 CSS px tall = 64 device px on a 2x screen,
        from a 324px source - already 5.1x more than can be shown

     the rule below the names
        renders 40 CSS px = 80 device px, from 140px - 1.8x

   A 4x rebuild was made and compared against the original at exactly
   the size it renders. They were indistinguishable, so it was thrown
   away rather than shipped: it would have cost about 740KB of
   downloads for nothing a visitor could see. The 2x files already in
   place cover the 2x screen with room to spare.

   WHAT WAS ACTUALLY SOFTENING THEM was the filter, not the file. Two
   drop-shadows sat under every rule, and the second - a 1.4px blur all
   the way round - spread a halo over an edge that is only two or three
   device pixels thick. Removing the halo and keeping a single 1px
   contact shadow directly beneath is what makes the edge read as
   drawn rather than printed on damp paper.

   The gold is lifted as well: a little more saturation and a touch of
   contrast, which on a pale beige ground is what separates the
   scrollwork from the page. Nothing is resized and nothing moves.
   =================================================================== */
#threshold .t-divider, .farewell .t-divider,
#threshold .threshold-welcome::after, .farewell .farewell-welcome::after {
  filter:
    saturate(1.34) contrast(1.10) brightness(1.02)
    drop-shadow(0 1px 0.6px rgba(112, 70, 14, 0.62)) !important;
}

/* the corners keep a slightly softer treatment - they are large enough
   that a hard shadow on them would read as a cut-out */
#threshold .corner-tl, #threshold .corner-tr,
#threshold .corner-bl, #threshold .corner-br,
.farewell  .corner-tl, .farewell  .corner-tr,
.farewell  .corner-bl, .farewell  .corner-br {
  filter:
    saturate(1.22) contrast(1.06)
    drop-shadow(0 1px 1px rgba(112, 70, 14, 0.52)) !important;
}

/* ===================================================================
   THE TWO ORNAMENTS RUN THE FULL WIDTH OF THE NAMES
   ===================================================================
   Stretching either drawing to that width would have smeared its
   scrolls and leaves sideways, so neither is stretched. Each is cut
   into four pieces and reassembled as four background layers:

       left cap    fixed, auto 100%, pinned left
       right cap   fixed, auto 100%, pinned right
       centre      fixed, auto 100%, centred
       plain rule  100% 100% - the ONLY layer that stretches

   A plain horizontal rule is featureless along its length, so
   stretching it is lossless. Everything with a shape in it keeps its
   own proportions. The rule is listed last, which in CSS means painted
   underneath, so the caps and the centre sit on top of it.

   The cuts were measured, not eyeballed: scanning each column's ink
   height finds the runs where the drawing is nothing but the rule, and
   the pieces are taken at those boundaries -

       above   caps 0-297 and 1948-2242, centre 1005-1240
       below   caps 0-453 and 1798-2245, centre 534-1719

   THE WIDTH MATCHES THE NAMES BECAUSE IT IS THE SAME EXPRESSION. Both
   take width: 33.30em at font-size min(clamp(30px,2.9vw,64px),2.50vw),
   which is what the original rules used and what was tuned against the
   real render. Being in em, it tracks the names through every clamp
   branch instead of only matching at one viewport.

   font-size cannot be 0 here, as it was a moment ago - the width is in
   em and would collapse to nothing. line-height stays 0, which is what
   actually removes the stray leading under a background.

   SPACE: the ornaments now sit 3px into the names above and below, and
   the block's own top and bottom margins are pulled in, so the group
   reads as one unit rather than three separated bands.
   =================================================================== */
#threshold .t-fr7-top, .farewell .t-fr7-top,
#threshold .t-fr7-bot, .farewell .t-fr7-bot {
  display: block !important;
  width: 33.30em !important;
  max-width: 96vw !important;
  font-size: min(clamp(30px, 2.9vw, 64px), 2.50vw) !important;
  line-height: 0 !important;
  padding: 0 !important;
  background-repeat: no-repeat, no-repeat, no-repeat, no-repeat !important;
  background-position: left center, right center, center center, center center !important;
  background-size: auto 100%, auto 100%, auto 100%, 100% 100% !important;
  aspect-ratio: auto !important;
}

#threshold .t-fr7-top, .farewell .t-fr7-top {
  height: clamp(20px, 2.10vw, 34px) !important;
  margin: 0 auto -3px !important;
  background-image:
    url('assets/na-capL.png'), url('assets/na-capR.png'),
    url('assets/na-mid.png'),  url('assets/na-line.png') !important;
}

#threshold .t-fr7-bot, .farewell .t-fr7-bot {
  height: clamp(34px, 3.60vw, 58px) !important;
  margin: -3px auto 0 !important;
  background-image:
    url('assets/nb-capL.png'), url('assets/nb-capR.png'),
    url('assets/nb-mid.png'),  url('assets/nb-line.png') !important;
}

/* and the names close up to them */
#threshold .threshold-names, .farewell .farewell-names {
  margin: 0 auto !important;
  padding: 0 !important;
}

/* ===================================================================
   THE NAMES IN PLAYFAIR DISPLAY, AND THE TWO GAPS MADE EQUAL
   ===================================================================
   Playfair Display was already being fetched, but only at 700/800/900.
   The request now includes 400-600 and the italics, so the weight the
   names actually use is a real cut of the face rather than one the
   browser fakes by slanting and thickening a heavier one.

   THE UNEVEN GAPS WERE IN THE ARTWORK, NOT THE MARGINS - which is why
   zeroing the margins earlier did not fix them. Each ornament carries
   its rule at a different place inside its own image:

       above   rule occupies rows 172-228 of 324
               -> 29.3% of its height is blank BELOW the rule
       below   rule occupies rows 213-325 of 579
               -> 36.8% of its height is blank ABOVE the rule

   Rendered, that is 10px of blank under the upper rule against 21px
   over the lower one. The margins were identical; the drawings were
   not, and the lower gap looked twice the upper one.

   That measurement was of the plain-rule strip alone, and subtracting
   its emptiness from the margins turned out to be wrong - see the
   block further down. The heights set here are still used; the
   spacing is handled there.
   =================================================================== */
#threshold, .farewell {
  --orn-h-top: clamp(20px, 2.10vw, 34px);
  --orn-h-bot: clamp(34px, 3.60vw, 58px);
  --orn-gap: 8px;
}

#threshold .threshold-names, .farewell .farewell-names {
  font-family: 'Playfair Display', 'Cormorant Garamond', Georgia, serif !important;
}

#threshold .t-fr7-top, .farewell .t-fr7-top { height: var(--orn-h-top) !important; }
#threshold .t-fr7-bot, .farewell .t-fr7-bot { height: var(--orn-h-bot) !important; }

/* ===================================================================
   THE NAMES IN CAPITALS, AND THE ORNAMENTS CUT TO THEIR WIDTH
   ===================================================================
   text-transform rather than retyping them: the markup keeps
   "Dr. Ragini Vijay Didolkar", so a screen reader announces a name
   instead of spelling out capitals, while the page shows capitals.

   The width is MEASURED, not calculated. It used to be 33.30em of a
   tuned font-size - a constant fitted by hand to the old typeface at
   one viewport - and it was wrong twice over once the face became
   Playfair Display and the names became capitals. app.js measures the
   names with a Range over the element's contents (not its box, which
   is a full-width block) and writes --names-w; the ornaments take it,
   so their caps land where the first and last letters do.
   =================================================================== */
#threshold .threshold-names, .farewell .farewell-names {
  text-transform: uppercase !important;
}

#threshold .t-fr7-top, .farewell .t-fr7-top,
#threshold .t-fr7-bot, .farewell .t-fr7-bot {
  width: var(--names-w, 33.30em) !important;
  max-width: 96vw !important;
}

/* ===================================================================
   THE CAPS WERE BEING PULLED INTO THE NAMES
   ===================================================================
   The negative margins came from measuring the plain-rule strip, which
   is mostly empty - 29% blank below the upper rule, 37% above the
   lower one. But the strip is only one of four layers. Measuring every
   piece tells a different story:

       upper  capL blank below 0.003   capR 0.000   mid 0.015
       lower  capL blank above 0.226   capR 0.225   mid 0.000

   The centre motifs fill their boxes edge to edge, so each ornament as
   a whole has NO blank on the side facing the names. Subtracting the
   strip's emptiness therefore pulled real ink 2px and 13px into the
   text - which is the leaf sitting on the D.

   The margins are now plain positive gaps. Because both ornaments have
   ~0 blank on the facing side, equal margins give equal visual space,
   which is what the earlier calculation was trying to achieve and got
   backwards.
   =================================================================== */
#threshold, .farewell { --orn-gap: 7px; }

#threshold .t-fr7-top, .farewell .t-fr7-top {
  margin: 0 auto var(--orn-gap) !important;
}
#threshold .t-fr7-bot, .farewell .t-fr7-bot {
  margin: var(--orn-gap) auto 0 !important;
}

/* ===================================================================
   THE HEADING RULES: SAME GOLD, AND THE GAP CLOSED
   ===================================================================
   COLOUR. The rules under WELCOME. and THANK YOU. - and under the
   titles, which share the same pair of images - are recoloured to the
   exact ramp the ornaments around the names use: #a8791a -> #d4af37 ->
   #fae08c, driven by how deep inside a stroke each pixel sits. Alpha
   is untouched, so nothing moves, nothing changes size, and only the
   colour differs from before.

   A first attempt at this came out washed out. The depth was being
   normalised against the image HEIGHT, which made the denominator tiny
   and pushed every pixel to the highlight end of the ramp. Normalising
   against the stroke's own core depth - the 85th percentile of the
   interior distance, 35px here - puts the relief back.

   THE GAP. What sat between the word and its rule was not margin; it
   was the heading's own descent. WELCOME. has no descenders, so the
   line box carries roughly 0.21em of empty space under the glyphs that
   nothing was using. The -0.12em pull was not enough to cross it. At
   -0.26em the rule closes up under the letters.

   That single value is the one to nudge if it wants to sit tighter or
   looser - everything else here is colour.
   =================================================================== */
#threshold .threshold-welcome::after, .farewell .farewell-welcome::after,
#threshold .t-title-orn, .farewell .t-title-orn {
  background-image: url('assets/orn-motif-gold.png'), url('assets/orn-line-gold.png') !important;
}

#threshold .threshold-welcome::after, .farewell .farewell-welcome::after {
  margin-top: -0.26em !important;
}

/* ===================================================================
   THE MANDIR AND THE GALLERY WEAR THE LIFE-STORY FRAME
   ===================================================================
   The frame in Their Life Story is one rule - .tl-photo-item img:

       padding: 5px
       background: linear-gradient(135deg, rgba(255,248,232,0.9),
                                           rgba(240,220,170,0.5))
       border: 2px solid rgba(212,160,58,0.5)
       border-radius: 12px
       box-shadow: var(--soft)

   The padding plus that background is what makes it read as a mounted
   photograph rather than a bordered one - the pale gradient shows
   through as a mat between the picture and the gold edge. Copying only
   the border would have missed the whole effect, so every value is
   taken across, not approximated.

   THE HOVER IS ON THE CARD, NOT THE PICTURE, and that is deliberate.
   In Their Life Story the whole item scales - transform on
   .tl-photo-item, nothing on the img - so the mat and the gold edge
   grow with the photograph. Moments Preserved was doing the opposite:
   the card held still while the image scaled 1.08 INSIDE it, which
   crops the picture against a fixed frame instead of bringing the
   whole thing forward. That is reversed here, so the two sections
   behave identically.

   The gallery's dark teal-and-brown card background goes with it: it
   was there to sit behind a bleeding image, and there is a mat now.
   Its images also had opacity 0.88, lifting to 1 on hover - the life
   story photographs are never dimmed, so that comes off too.
   =================================================================== */
.temple-img-full {
  padding: 5px !important;
  background: linear-gradient(135deg, rgba(255,248,232,0.9), rgba(240,220,170,0.5)) !important;
  border: 2px solid rgba(212, 160, 58, 0.5) !important;
  border-radius: 12px !important;
  box-shadow: var(--soft) !important;
  cursor: zoom-in;
}

#moments .gallery-card {
  padding: 5px !important;
  background: linear-gradient(135deg, rgba(255,248,232,0.9), rgba(240,220,170,0.5)) !important;
  border: 2px solid rgba(212, 160, 58, 0.5) !important;
  border-radius: 12px !important;
  box-shadow: var(--soft) !important;
  overflow: hidden !important;
  transition: transform 0.3s ease, box-shadow 0.3s ease !important;
}
#moments .gallery-card img {
  border-radius: 8px !important;
  opacity: 1 !important;
  transition: none !important;
  cursor: zoom-in;
}
#moments .gallery-card:hover {
  transform: scale(1.06) !important;
  z-index: 3;
  box-shadow: var(--shadow) !important;
}
#moments .gallery-card:hover img {
  transform: none !important;
  opacity: 1 !important;
}

@media (prefers-reduced-motion: reduce) {
  #moments .gallery-card, #moments .gallery-card:hover {
    transition: none !important;
    transform: none !important;
  }
}

/* ===================================================================
   THE LEAF FILLED RED - AND NOTHING ELSE
   ===================================================================
   The dots are gold again; the recolouring starts from the untouched
   master rather than being undone on top of itself, so no trace of the
   previous pass is carried over.

   THE LEAF IS DRAWN HOLLOW, which is the whole point here. Recolouring
   its component turned the OUTLINE red and left the middle empty,
   because the middle is not ink at all - it is a hole in the drawing.
   Filling the holes and subtracting the outline gives the enclosed
   area: 19,590 pixels inside a 26,632-pixel outline. That area is
   painted red AND given full alpha, since it was transparent and would
   otherwise have stayed invisible however it was coloured.

   The fill is dilated by one pixel into the stroke so it meets the
   gold rather than leaving a hairline of background between them.

   The leaf keeps its gold outline: red inside, gold edge. All four
   corners are mirrored from the one master.
   =================================================================== */
#threshold .corner-tl, .farewell .corner-tl { background-image: url('assets/corner-tl-red.png') !important; }
#threshold .corner-tr, .farewell .corner-tr { background-image: url('assets/corner-tr-red.png') !important; }
#threshold .corner-bl, .farewell .corner-bl { background-image: url('assets/corner-bl-red.png') !important; }
#threshold .corner-br, .farewell .corner-br { background-image: url('assets/corner-br-red.png') !important; }

/* ===================================================================
   THE LOWER ORNAMENT UP BY ONE POINT
   ===================================================================
   Written as 1pt, not converted to pixels: CSS treats a point as
   exactly 1/72 inch, which is 1.333px, so `calc(var(--orn-gap) - 1pt)`
   is the literal instruction rather than my rounding of it.

   Only the lower one moves. The upper ornament keeps the full gap, so
   the two are no longer symmetric - which is intentional here, since
   the ask was to close the space under the names rather than to keep
   both sides matched.

   --orn-gap stays the single control for the pair; this is a one-point
   offset applied on top of it, so nudging the gap still moves both.
   =================================================================== */
#threshold .t-fr7-bot, .farewell .t-fr7-bot {
  margin-top: calc(var(--orn-gap) - 1pt) !important;
}

/* ===================================================================
   THE THIRTEEN LEDE LINES, ONE POINT LARGER
   ===================================================================
   All thirteen turned out to be the same element - every one of them
   carries class="lede lede-italic", and one of them adds
   dedication-plain on top. So this is a single rule rather than
   thirteen, and any lede added later inherits the change instead of
   being forgotten.

   Their size comes from .lede-italic: clamp(23px, 2.4vw, 27px). The
   point is added INSIDE the clamp rather than outside it. Written as
   calc(clamp(...) + 1pt) the whole curve would shift, including the
   floor, so the line would grow on a small phone where it is already
   at its minimum for a reason. Adding 1pt to each of the three stops
   keeps the shape of the curve and moves it by exactly a point at
   every width.

   1pt is 1/72 inch, which CSS resolves to 1.3333px, so the stops
   become 24.33 / 2.4vw+1pt / 28.33px.
   =================================================================== */
.lede.lede-italic {
  font-size: clamp(calc(23px + 1pt), calc(2.4vw + 1pt), calc(27px + 1pt)) !important;
}

/* ===================================================================
   THE CLOSING ORNAMENT, AT THE FOOT OF TWELVE SECTIONS
   ===================================================================
   One drawing, one asset, one rule. The upload turned out to be the
   same lower half already extracted for Sacred Dedication - identical
   dimensions and aspect - so it is stored once as section-rule.png and
   shared, rather than kept as two copies of the same picture.

   THE ARTWORK. Gold on near-black with a "Magnific" wash across it.
   Alpha is gated on HUE, not saturation: a pale watermark lowers
   saturation but leaves hue untouched, so gating on gold keeps the
   drawing beneath it while the ground, which has no hue at all, falls
   away. Then the contour is rebuilt at 6x from a distance field, since
   the source is only 124px tall.

   IT IS IN THE FLOW, so it cannot cut or overlap anything: each is a
   real element at the very end of its section, and everything above it
   is laid out first.

   PLACING IT NEEDED NESTING-AWARE PARSING. A first pass matched each
   section with a lazy regex and dropped the Help ornament in the middle
   of the page - #help contains a nested <section>, so the first
   </section> encountered is not the one that closes it. The insert
   points are now found by walking the tags and counting depth, and each
   was verified to sit at the true end of its own section.

   1pt of space above it, as asked - written as 1pt rather than 1.33px
   so it stays a true typographic point.

   All twelve are the same size, Their Life Story included - it briefly
   had a narrowed variant and no longer does.
   =================================================================== */
.sec-rule {
  display: block;
  /* THE WIDTH IS SACRED DEDICATION'S, COPIED. app.js measures the ornament
     there - the one matched to its button row - and publishes it as
     --sec-rule-w, which every other section then takes, so all thirteen
     are the same size and stay so if a button label ever changes.

     The fallback is that same size worked out arithmetically, for the
     moment before the script runs: the old height clamp multiplied by the
     drawing's 4.7977 aspect gives clamp(144px, 21.1vw, 365px). Written as
     a width rather than a height because width is what has to be copied. */
  width: var(--sec-rule-w, clamp(144px, 21.1vw, 365px));
  max-width: 92vw;
  aspect-ratio: 2917 / 608;
  height: auto;
  margin: 1pt auto 0;
  background-image: url('assets/section-rule.png');
  background-repeat: no-repeat;
  background-position: center center;
  background-size: contain;
  pointer-events: none;
  line-height: 0;
  font-size: 0;
}

@media (max-width: 720px) {
  .sec-rule { width: var(--sec-rule-w, clamp(115px, 38vw, 230px)); }
}

@media print { .sec-rule { display: none; } }

/* ===================================================================
   SACRED DEDICATION: THE ORNAMENT AS WIDE AS THE BUTTONS
   ===================================================================
   CSS cannot read a sibling's width, so rather than measure the button
   row in JavaScript and copy the number, the buttons and the ornament
   are now wrapped together in a box that shrinks to fit its widest
   child. The buttons decide that width; the ornament then takes
   width: 100% of it. If a button's label ever changes, the ornament
   follows on its own - there is no number to keep in step.

   width: fit-content is what does it. The wrapper is centred with
   margin-inline: auto, so the pair stays in the middle of the section
   exactly as the button row did on its own.

   NOT STRETCHED. height goes to auto and the file's own aspect-ratio
   supplies it, so widening the box scales the drawing uniformly rather
   than smearing it sideways. It grows a little taller as it grows
   wider, which is what "as it is" has to mean for a fixed drawing.

   Only Sacred Dedication is affected: the modifier is on that one
   element, and the other eleven sections keep the size they had.
   =================================================================== */
#sacred .sacred-close {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

#sacred .sec-rule--span {
  width: 100%;
  max-width: 100%;
  height: auto;
  aspect-ratio: 2917 / 608;
  margin: 1pt auto 0;
}

/* ===================================================================
   THEIR LIFE STORY: WHERE TO READ FROM
   ===================================================================
   A line under the lede pointing at the control, so a visitor is not
   left to discover "Read Reflection" by hovering over things.

   It is set in italics as asked, and a step smaller and quieter than
   the lede above it, because it is an instruction rather than part of
   the writing - at the same size and weight it would read as another
   sentence of the tribute.

   The words "Read Reflection" are in a <strong> and deliberately NOT
   italic: naming a control is the one place where matching the
   button's own appearance matters more than matching the sentence
   around it. font-style is reset on it explicitly, since the italics
   on the parent would otherwise be inherited.
   =================================================================== */
#timeline .story-hint {
  margin: 6px 0 0;
  font-family: var(--font-body);
  font-style: italic;
  font-size: clamp(15px, 1.35vw, 18px);
  line-height: 1.5;
  color: rgba(61, 40, 20, 0.78);
  text-align: center;
}
#timeline .story-hint strong {
  font-style: normal;
  font-weight: 600;
  color: var(--heading, #5c2d0a);
}

/* ===================================================================
   THE COPYRIGHT LINE
   ===================================================================
   A real <footer> after </main>, in the flow rather than fixed, so it
   can never sit over the page's own content and appears only once the
   reader has reached the end.

   It is OUTSIDE <main>, which matters here: <main> is where the
   thirteen sections live, and anything placed inside it would have
   been read as another part of the tribute. A footer is site
   furniture, and both the markup and screen readers should say so.

   The © is a real character rather than a "(c)", and the year and name
   are one unbroken line. Centred, quiet, and small enough not to
   compete with the closing ornament above it.
   =================================================================== */
.site-footer {
  padding: clamp(18px, 3vw, 34px) clamp(16px, 5vw, 72px) clamp(26px, 4vw, 46px);
  text-align: center;
}

.site-footer .copyright {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(14px, 1.15vw, 16px);
  letter-spacing: 0.04em;
  color: rgba(61, 40, 20, 0.70);
}

@media print {
  .site-footer .copyright { color: #3d2814; }
}

/* ===================================================================
   THE CAPTION IN THE SHARED PHOTO VIEWER
   ===================================================================
   Moments Preserved now opens the same viewer as Their Life Story, so
   the caption a gallery photograph carries needs somewhere to sit. It
   is empty and hidden for a story photograph, which has none.

   Placed above the counter so the reading order is picture, then what
   it is, then where you are in the set.
   =================================================================== */
.tl-photo-caption[hidden] { display: none !important; }

.tl-photo-caption {
  margin: 12px auto 0;
  max-width: 60ch;
  text-align: center;
  font-family: var(--font-body);
  font-size: clamp(15px, 1.3vw, 18px);
  line-height: 1.55;
  color: #f3e6cc;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
}
.tl-photo-caption strong {
  display: inline-block;
  font-weight: 600;
  color: #ffe9b8;
}

/* ===================================================================
   THE GUEST BOOK LAID OUT AS TILES, NOT COLUMNS
   ===================================================================
   The grid was repeat(3, 1fr) - three fixed tracks, always. With a
   single tribute on the page that put one long message into a third of
   the width, which is why it came out as a tall narrow ribbon: the
   track was 400px wide whether there was anything to fill the other
   two or not.

   repeat(auto-fit, minmax(...)) fixes it at the root. auto-fit COLLAPSES
   the tracks nothing occupies, so one tribute spans the full width as a
   tile, two share the row, three or four sit side by side, and it never
   leaves a column standing empty beside a squeezed one. auto-fill would
   NOT do this - it keeps the empty tracks and the ribbon comes back.

   THE TEXT FILLS THE TILE. This block once capped the message at 78
   characters for the sake of a comfortable measure. That was the wrong
   trade: it left a third of every wide tile empty. The tile is the
   measure now, and the tile scrolls when the words outgrow it.

   Tighter, as asked: the gap drops from 16px to 12px and the cards lose
   a little padding, so they sit closer the way the story entries do.
   =================================================================== */
.approved-grid {
  grid-template-columns: repeat(auto-fit, minmax(clamp(280px, 30vw, 380px), 1fr));
  gap: 12px;
  align-items: start;
}

.approved-grid > article {
  padding: clamp(16px, 1.5vw, 22px) clamp(18px, 1.7vw, 26px);
}

/* THE MESSAGE FILLS THE TILE.
   It used to be held to 78 characters - a good measure for a page of
   prose, and wrong here: it left a third of every wide tile empty while
   the words were squeezed into the left of it. The tile IS the measure
   now; if the words do not fit, the tile scrolls. */
.approved-grid > article > p:last-of-type {
  max-width: none;
}

@media (max-width: 980px) {
  .approved-grid { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
}
@media (max-width: 620px) {
  .approved-grid { grid-template-columns: 1fr; gap: 10px; }
}

/* ===================================================================
   THE GUEST BOOK: A WINDOW OF ITS OWN, AND WHAT IS ATTACHED
   ===================================================================
   THE WINDOW appears only past five tributes - app.js adds .is-scroll
   by count - so a young Guest Book still flows in the page with no box
   and no scrollbar to explain.

   overscroll-behavior: contain is the detail that decides whether this
   feels right: without it, reaching the bottom of the list carries on
   and starts scrolling the page behind it, which is the most irritating
   thing about any scroll-inside-scroll.

   content-visibility: auto lets the browser skip layout and paint for
   cards scrolled out of view. With a few hundred cards in the DOM that
   is the difference between smooth and stuttering, and it costs one
   line. contain: content on the card tells it the card cannot affect
   anything outside itself, which is what makes that safe.

   ATTACHMENTS. A photograph is a thumbnail in the same mat-and-gold
   frame the rest of the site uses; audio and video are chips until
   pressed, and the player is inserted then. Nothing here creates a
   media element - see renderFileLinks - so a hundred thousand tributes
   cost nothing until someone asks to hear one.
   =================================================================== */
.tribute-window { position: relative; }

.tribute-window.is-scroll {
  max-height: min(68vh, 720px);
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid rgba(140, 85, 20, 0.20);
  border-radius: 14px;
  padding: 12px;
  background: rgba(255, 255, 255, 0.24);
  scrollbar-width: thin;
  scrollbar-color: var(--gold) transparent;
}
.tribute-window.is-scroll::-webkit-scrollbar { width: 10px; }
.tribute-window.is-scroll::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #e0b348, #c8912c);
  border-radius: 99px;
  border: 3px solid transparent;
  background-clip: content-box;
}
.tribute-window.is-scroll .tribute-fade {
  position: sticky;
  bottom: -12px;
  height: 34px;
  margin: -34px -12px -12px;
  pointer-events: none;
  background: linear-gradient(to top, rgba(245, 245, 220, 0.95), transparent);
}
.tribute-fade { display: none; }
.tribute-window.is-scroll .tribute-fade { display: block; }
.tribute-sentinel { height: 1px; }

.approved-grid > article {
  content-visibility: auto;
  contain-intrinsic-size: auto 260px;   /* a size to assume while skipped */
  contain: content;
}

/* ---- attachments ---- */
.att-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 11px;
  padding-top: 10px;
  border-top: 1px dashed rgba(140, 85, 20, 0.20);
}
.att-thumb {
  width: 64px;
  height: 64px;
  border-radius: 9px;
  object-fit: cover;
  padding: 3px;
  background: linear-gradient(135deg, rgba(255,248,232,0.9), rgba(240,220,170,0.5));
  border: 2px solid rgba(212, 160, 58, 0.5);
  cursor: zoom-in;
}
.att-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border: 1px solid rgba(140, 85, 20, 0.20);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.62);
  font-family: inherit;
  font-size: 15px;
  color: var(--heading, #5c2d0a);
  text-decoration: none;
  cursor: pointer;
}
.att-chip:hover, .att-chip:focus-visible {
  background: linear-gradient(180deg, #f7dc93, #e0b348);
}
.att-ic {
  width: 19px; height: 19px;
  border-radius: 5px;
  display: grid; place-items: center;
  font: 600 10px/1 'Josefin Sans', system-ui, sans-serif;
  color: #4a2708;
  background: linear-gradient(180deg, #f7dc93, #c8912c);
}
.att-player {
  display: block;
  width: 100%;
  margin-top: 9px;
  border-radius: 8px;
}
video.att-player { max-height: 240px; background: #201509; }

@media (max-width: 620px) {
  .tribute-window.is-scroll { max-height: 72vh; padding: 10px; }
}
@media (prefers-reduced-motion: reduce) {
  .tribute-window.is-scroll { scroll-behavior: auto; }
}

/* ===================================================================
   THE WORD COUNTER UNDER THE TRIBUTE FIELD
   ===================================================================
   Quiet while there is room, warm at nine tenths, and clearly wrong
   past the limit. The colour is not the only signal - the wording
   changes too, to "N over" - because a reader who cannot distinguish
   the two golds would otherwise be told nothing.
   =================================================================== */
.word-count {
  display: block;
  margin-top: 6px;
  text-align: right;
  font-family: 'Josefin Sans', system-ui, sans-serif;
  font-size: 13px;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  color: rgba(61, 40, 20, 0.62);
  transition: color .2s var(--ease-soft);
}
.word-count.is-near { color: #a9670f; }
.word-count.is-over { color: #a01912; font-weight: 600; }

@media (prefers-reduced-motion: reduce) { .word-count { transition: none; } }

/* ===================================================================
   TWO VIEWS, AND A CALENDAR
   ===================================================================
   ONE BY ONE is a class on the same container, not a second renderer.
   The grid becomes a single column and the cards get a little more
   room to breathe - same cards, same chunked loading, same
   attachments. Nothing about the list needed its own code path.

   THE CALENDAR is a panel below the controls rather than a popover:
   a popover would need collision handling against the scroll window
   and would be awkward on a phone, and there is room here for it to
   simply open in place.

   Days with tributes are buttons; days without are plain text. The
   count sits in the corner of the day as a small badge, so a reader
   can see where the busy days are before clicking anything.
   =================================================================== */
.tribute-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 0 0 12px;
}

.view-toggle {
  display: inline-flex;
  border: 1px solid rgba(140, 85, 20, 0.24);
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.5);
}
.view-btn {
  border: 0;
  background: none;
  padding: 7px 16px;
  font-family: inherit;
  font-size: 15px;
  color: var(--heading, #5c2d0a);
  cursor: pointer;
}
.view-btn.active { background: linear-gradient(180deg, #f7dc93, #e0b348); font-weight: 600; }

.cal-btn, .date-clear {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid rgba(140, 85, 20, 0.24);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.5);
  padding: 7px 15px;
  font-family: inherit;
  font-size: 15px;
  color: var(--heading, #5c2d0a);
  cursor: pointer;
}
.cal-btn:hover, .date-clear:hover { background: linear-gradient(180deg, #f7dc93, #e0b348); }
.date-clear { background: linear-gradient(180deg, #f7dc93, #e0b348); font-weight: 600; }
.cal-btn[hidden], .date-clear[hidden], .tribute-calendar[hidden] { display: none !important; }

/* ---- one by one ---- */
.approved-grid.is-list {
  grid-template-columns: 1fr;
  gap: 14px;
}
.approved-grid.is-list > article { padding: clamp(18px, 1.8vw, 26px) clamp(20px, 2vw, 30px); }
.approved-grid.is-list > article > p:last-of-type { max-width: none; }

/* ---- the calendar ---- */
.tribute-calendar {
  margin: 0 0 12px;
  padding: 12px 14px;
  border: 1px solid rgba(140, 85, 20, 0.22);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.42);
  max-width: 330px;
}
.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.cal-month {
  font-family: 'Josefin Sans', system-ui, sans-serif;
  font-size: 14px;
  letter-spacing: 0.08em;
  color: var(--heading, #5c2d0a);
}
.cal-nav {
  border: 1px solid rgba(140, 85, 20, 0.24);
  background: rgba(255, 255, 255, 0.6);
  border-radius: 50%;
  width: 26px; height: 26px;
  font-size: 12px; line-height: 1;
  color: var(--heading, #5c2d0a);
  cursor: pointer;
}
.cal-nav[disabled] { opacity: 0.32; cursor: default; }

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
}
.cal-dow {
  text-align: center;
  font: 400 11px/1.8 'Josefin Sans', system-ui, sans-serif;
  color: rgba(92, 45, 10, 0.55);
}
.cal-day {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  border: 0;
  border-radius: 8px;
  background: none;
  font-family: inherit;
  font-size: 14px;
  color: rgba(61, 40, 20, 0.42);
}
.cal-day.has {
  color: var(--heading, #5c2d0a);
  background: rgba(224, 179, 72, 0.20);
  cursor: pointer;
  font-weight: 600;
}
.cal-day.has:hover { background: rgba(224, 179, 72, 0.42); }
.cal-day.has.is-on { background: linear-gradient(180deg, #f7dc93, #c8912c); color: #4a2708; }
.cal-day i {
  position: absolute;
  top: 1px; right: 3px;
  font: 600 9px/1 'Josefin Sans', system-ui, sans-serif;
  font-style: normal;
  color: rgba(92, 45, 10, 0.75);
}
.cal-day.has.is-on i { color: #4a2708; }

@media (max-width: 620px) {
  .tribute-calendar { max-width: 100%; }
}

/* ===================================================================
   THE MODERATION PANEL AT SCALE
   ===================================================================
   The Guest Book's own rules already cover the shared furniture - the
   scroll window, the calendar, the attachment chips - because the panel
   reuses those class names exactly. What is left is the handful of
   things that are only true of a moderator's list.

   THE VIEW BUTTONS ARE .aview-btn, NOT .view-btn. Identical to look at
   and deliberately a different class: the Guest Book binds its toggle
   with $$('.view-btn'), so a shared class would have made one click
   change both lists at once, and a moderator switching to one-by-one
   would silently reformat the public page behind them.

   PUBLISH IS THE LOUD BUTTON. Approve no longer puts anything on the
   website; a second, explicit decision does. That decision is the one
   thing on the card that changes what the world sees, so it does not
   look like its four neighbours.
   =================================================================== */
.admin-tools { margin-top: 4px; }

.aview-btn {
  border: 0;
  background: none;
  padding: 7px 16px;
  font-family: inherit;
  font-size: 15px;
  color: var(--heading, #5c2d0a);
  cursor: pointer;
}
.aview-btn.active { background: linear-gradient(180deg, #f7dc93, #e0b348); font-weight: 600; }

/* Cards outside the scrolled view are skipped until they are needed.
   contain: content is what makes that safe - it tells the browser the
   card cannot affect anything beyond itself, so skipping it cannot
   move the rest of the list. */
.admin-tributes > article {
  content-visibility: auto;
  contain-intrinsic-size: auto 300px;
  contain: content;
}

/* ---- one by one ---- */
.admin-tributes.is-list { grid-template-columns: 1fr; gap: 12px; }
.admin-tributes.is-list > article > p { max-width: none; }

/* Inside the window the two-column grid needs a little less room, since
   the window's own padding and scrollbar have taken some. */
#adminWindow.is-scroll .admin-tributes { max-width: none; }

/* ---- the publish decision ---- */
.admin-tribute .actions .admin-publish {
  font-weight: 700;
  border-color: rgba(140, 85, 20, 0.42);
}
.admin-tribute .actions button[data-action="show"] {
  background: linear-gradient(180deg, #f7dc93, #e0b348);
  color: #4a2708;
}
.admin-tribute .actions button[data-action="show"]:hover { background: var(--teal); color: #fff; }

/* An approved-but-unpublished card is the one thing in the panel that
   is waiting on a person, so it says so at a glance. */
.admin-tribute:has(.admin-vis.is-hidden) {
  box-shadow: inset 3px 0 0 var(--rose, #b5476b);
}

@media (max-width: 900px) {
  .admin-tributes.is-list { grid-template-columns: 1fr; }
}

/* ===================================================================
   THE DISPLAYED LIST
   ===================================================================
   A fifth counter and a fifth tab, so a moderator can see the website
   as a visitor sees it without leaving the panel.

   THE ROW IS NO LONGER FOUR WIDE. It was repeat(4, 1fr) with a
   two-column fallback; five items in a four-column grid would leave one
   stranded on its own line, so the row is asked for whatever fits
   instead - auto-fit collapses to four, three or two as the panel
   narrows, and never leaves a lone box.

   APPROVED IS THE PARENT OF DISPLAYED AND HIDDEN, which means these
   five numbers do not sum to the total and would look like an error if
   nothing said so. The line beneath them says so.

   DISPLAYED IS MARKED IN GOLD, in the counter and on the tab, because
   it is the only one of the five that describes the live website
   rather than the moderation queue.
   =================================================================== */
.admin-stats {
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  max-width: 1080px;
}

.admin-stats .stat-live {
  border-color: rgba(200, 145, 44, 0.55);
  background:
    linear-gradient(180deg, rgba(255, 248, 232, 0.92), rgba(247, 233, 176, 0.55));
}
.admin-stats .stat-live strong { color: #7a4a08; }

/* The parent reads as a total rather than as a peer of the two halves. */
.admin-stats .stat-parent strong { opacity: 0.86; }

.admin-stats-note {
  max-width: 1080px;
  margin: -10px auto 18px;
  text-align: center;
  font-size: 14px;
  font-style: italic;
  opacity: 0.78;
}
.admin-stats-note strong { font-style: normal; }

/* The tab, matched to its counter. The gold only appears while the tab
   is NOT the selected one; a selected chip already carries the site's
   own emphasis, and two emphases at once would fight. */
.filter-btn.is-live.ghost {
  border-color: rgba(200, 145, 44, 0.55);
  color: #7a4a08;
  font-weight: 600;
}

@media (max-width: 620px) {
  .admin-stats { grid-template-columns: repeat(2, 1fr); }
  .admin-stats-note { margin-top: -4px; }
}

/* ===================================================================
   REMOVING AN ATTACHMENT
   ===================================================================
   A small cross in the corner of a tile that holds a file.

   THE TILE IS A GRID WITH place-items: center AND EXACTLY TWO CHILDREN
   - the icon and the caption. A third child would be laid out as a third
   row and would push the other two off centre, so the cross is taken out
   of the flow entirely and pinned to the corner. That is also why the
   tile gains position: relative; without it the cross would find the
   nearest positioned ancestor somewhere up the page.

   THE CAPTION IS GIVEN ROOM. File names are already cut at twenty
   characters, but twenty wide characters can still reach the corner, so
   the caption is held clear of it rather than allowed to slide under.
   =================================================================== */
.upload-tile { position: relative; }

.upload-clear {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(140, 85, 20, 0.30);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  color: #7a2e2e;
  font: 700 12px/1 'Josefin Sans', system-ui, sans-serif;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.2s var(--ease), background 0.2s var(--ease), transform 0.2s var(--ease);
}
.upload-clear:hover,
.upload-clear:focus-visible {
  opacity: 1;
  background: #b5476b;
  color: #fff;
  transform: scale(1.08);
}
.upload-clear[hidden] { display: none !important; }

/* the caption keeps clear of the cross while a file is attached */
.upload-tile.selected em {
  max-width: calc(100% - 30px);
  overflow-wrap: anywhere;
}

@media (prefers-reduced-motion: reduce) {
  .upload-clear { transition: none; }
  .upload-clear:hover, .upload-clear:focus-visible { transform: none; }
}

/* ===================================================================
   PREVIEW & VERIFICATION
   ===================================================================
   The panel now shows the whole tribute rather than its first two
   hundred characters, which is the only way it can verify anything.

   A 450-WORD TRIBUTE IS TALL. Left to grow, it would push the Submit
   button down the page and out of sight - the one control the visitor
   is heading for. The quotation is therefore given a ceiling and its
   own scrollbar past that point: the whole message is readable, and
   nothing below it moves.

   THE PLACEHOLDER AND A REAL MESSAGE READ DIFFERENTLY. 'Your message
   will appear here' is an instruction; the tribute is the visitor's own
   words. They should not look alike, so only the placeholder is italic
   and dimmed.

   THE ATTACHMENT CHIPS say Attached or None instead of [Sel] and [Not],
   and an attached one is tinted so the row can be checked at a glance.
   =================================================================== */
.preview-state blockquote {
  max-height: 260px;
  overflow-y: auto;
  overscroll-behavior: contain;
  white-space: pre-wrap;        /* the visitor's own line breaks are kept */
  overflow-wrap: anywhere;      /* one very long word cannot widen the panel */
  font-style: normal;           /* italics are reserved for the placeholder */
  scrollbar-width: thin;
  scrollbar-color: var(--gold) transparent;
}
.preview-state blockquote.is-empty {
  font-style: italic;
  color: var(--muted);
}
.preview-state blockquote::-webkit-scrollbar { width: 8px; }
.preview-state blockquote::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #e0b348, #c8912c);
  border-radius: 99px;
  border: 2px solid transparent;
  background-clip: content-box;
}

.preview-files span.is-on {
  border-color: rgba(200, 145, 44, 0.55);
  background: linear-gradient(180deg, rgba(255, 248, 232, 0.95), rgba(247, 233, 176, 0.6));
  color: #6b3d05;
}

@media (max-width: 620px) {
  .preview-state blockquote { max-height: 220px; }
}

/* ===================================================================
   THE 'NOT BEING SERVED' NOTICE
   ===================================================================
   Shown only when the page is running over file://. It has to be read
   before the form beneath it is filled in, so it is warm rather than
   alarming but not so quiet that it is skipped: the site's own gold,
   a rule down the leading edge, and room to breathe.
   =================================================================== */
.unserved-note {
  margin: 0 0 18px;
  padding: 12px 16px;
  border: 1px solid rgba(200, 145, 44, 0.55);
  border-left: 4px solid #c8912c;
  border-radius: 12px;
  background: linear-gradient(180deg, rgba(255, 248, 232, 0.95), rgba(247, 233, 176, 0.5));
  color: #6b3d05;
  font-size: 16px;
  line-height: 1.5;
}

/* ===================================================================
   THE GALLERY PHOTOGRAPH IS A CONTROL
   ===================================================================
   It opens the same full-size viewer as a photograph in Their Life
   Story, so it must LOOK as though it will: the same zoom-in cursor
   that every other openable photograph on the site carries, and a
   visible focus ring, since it is now reachable by keyboard.
   =================================================================== */
.gv-img {
  cursor: zoom-in;
}
.gv-img:focus-visible {
  outline: 3px solid var(--gold, #c8912c);
  outline-offset: 4px;
  border-radius: 6px;
}

/* The calendar is now always offered, so it must look composed when
   there is nothing in it to choose. */
.cal-empty {
  margin: 10px 2px 0;
  text-align: center;
  font-size: 14px;
  font-style: italic;
  color: var(--muted);
}

/* ===================================================================
   PHONE, TABLET AND TELEVISION
   ===================================================================
   The site was already responsive - a mobile menu at 980px, single
   columns at 620px, the timeline and the gallery each with their own
   rules. Three things were missing, and they are what this block adds.

   1. NOTHING EVER GREW. .section-inner is capped at 1200px, so on a
      television - or any wide monitor - the page is a narrow stripe of
      small text in the middle of a large screen. The cap now opens in
      steps, and the text with it. Headings are mostly clamp() with a vw
      term and were already growing; body copy, buttons and captions are
      fixed pixels and were not, so those are lifted deliberately.

   2. TOUCH TARGETS WERE MOUSE-SIZED. The controls added recently - the
      remove-attachment cross at 24px, calendar days, view toggles,
      attachment chips - are comfortable with a pointer and small under
      a thumb. They are enlarged for coarse pointers ONLY, so the
      desktop layout is untouched.

   3. HOVER STYLES STICK ON TOUCH. A tap leaves :hover applied until
      something else is tapped, so a lifted tile or a gold-filled chip
      can sit there looking pressed. On devices that cannot hover, the
      movement is removed.

   Everything here is additive and scoped to a media query. No existing
   rule is edited, so nothing changes on the screens that were already
   right.
   =================================================================== */

/* ---- 1a. WIDE MONITORS: let the page use the room ---- */
@media (min-width: 1500px) {
  .section-inner,
  .section-inner.subsection-style { max-width: 1360px; }
}

/* ---- 1b. TELEVISIONS AND LARGE 4K PANELS ----
   Read from across a room, not from arm's length: the column widens
   again and the fixed-pixel text is raised with it. Paragraphs keep a
   sane measure, because a 1700px line of prose is unreadable however
   large the letters are. */
@media (min-width: 1900px) {
  .section-inner,
  .section-inner.subsection-style { max-width: 1560px; }

  body { font-size: 19px; }
  .lede, .eyebrow { font-size: 1.06em; }
  .nav-links a, .nav-links .nav-exit-btn { font-size: 19px; }
  .btn { font-size: 18px; }

  .approved-grid > article > p,
  .admin-tributes > article > p,
  .preview-state blockquote { font-size: 18px; }

  /* Prose stays readable however wide the panel is - but NOT the
     tributes. Capping those re-created on a television exactly the
     fault this was meant to fix: words in the left of the tile and
     emptiness in the right. Only the section ledes are held. */
  .section-heading .lede { max-width: 78ch; margin-left: auto; margin-right: auto; }
}

@media (min-width: 2560px) {
  .section-inner,
  .section-inner.subsection-style { max-width: 1840px; }
  body { font-size: 22px; }
  .nav-links a, .nav-links .nav-exit-btn { font-size: 22px; }
  .btn { font-size: 21px; }
  .approved-grid > article > p,
  .admin-tributes > article > p,
  .preview-state blockquote { font-size: 21px; }
  .tribute-window.is-scroll { max-height: min(68vh, 1100px); }
}

/* ---- 2. TOUCH: phones, tablets and touch-screen laptops ----
   Only where the pointer is coarse, so nothing on a desktop moves. */
@media (pointer: coarse) {
  .upload-clear {
    width: 38px; height: 38px;
    top: 8px; right: 8px;
    font-size: 15px;
    opacity: 1;                      /* no hover to reveal it on a phone */
  }
  .upload-tile.selected em { max-width: calc(100% - 46px); }

  .view-btn, .aview-btn, .cal-btn, .date-clear { padding: 12px 18px; font-size: 16px; }
  .att-chip { padding: 10px 16px; font-size: 16px; }
  .att-thumb { width: 76px; height: 76px; }

  .cal-day { min-height: 42px; font-size: 16px; }
  .cal-nav { width: 38px; height: 38px; font-size: 15px; }
  .tribute-calendar { padding: 14px; }

  .filter-btn, .filter-chip { min-height: 44px; }
  .admin-tribute .actions button { min-height: 42px; padding-left: 16px; padding-right: 16px; }

  /* the photo viewer's side arrows are the main way to move on a phone */
  .tl-photo-nav, .gv-nav { width: 48px; height: 48px; font-size: 20px; }
}

/* ---- 3. NO HOVER: stop tap states sticking ---- */
@media (hover: none) {
  .upload-tile:hover,
  .approved-grid article:hover,
  .admin-tribute:hover,
  .att-chip:hover,
  .upload-clear:hover { transform: none; }
}

/* ---- 4. SMALL SCREENS: this session's components ---- */
@media (max-width: 620px) {
  /* four chips in one column made the panel needlessly tall */
  .preview-files { grid-template-columns: 1fr 1fr; }

  /* the tools read better as one full-width row each than as a ragged wrap */
  .tribute-tools { gap: 8px; }
  .view-toggle { flex: 1 1 100%; }
  .view-btn, .aview-btn { flex: 1 1 50%; text-align: center; }
  .cal-btn, .date-clear { flex: 1 1 auto; justify-content: center; }

  /* the calendar must never be wider than the screen it is on */
  .tribute-calendar { max-width: 100%; }

  .admin-stats-note { font-size: 13px; }
  .admin-tribute .actions { gap: 6px; }
  .admin-tribute .actions button { flex: 1 1 auto; }
}

/* A phone held sideways has very little height; the scroll windows
   should not swallow the whole of it. */
@media (max-height: 520px) and (orientation: landscape) {
  .tribute-window.is-scroll { max-height: 62vh; }
  .preview-state blockquote { max-height: 40vh; }
}

/* ===================================================================
   A TRIBUTE IS A TILE, AND THE TILE HOLDS THE WRITING

   Until now a card was as tall as its words. One 450-word tribute
   therefore produced one enormous card, the grid beside it collapsed
   into a single ragged column, and the tile stopped looking like a tile
   at all - which is the thing that was asked for in the first place.

   Every card is now the SAME height, and the message inside it scrolls
   when it does not fit. The card is a flex column: the name, the
   dedication line and - at the foot - the date, the attachments and (in
   the panel) the action buttons all keep their natural size, and only
   the message is allowed to flex and to scroll. So the furniture never
   moves and never scrolls out of reach; only the words do.

   min-height: 0 IS LOAD-BEARING. A flex item will not shrink below its
   content's height without it, so the paragraph would simply push the
   card taller and overflow: auto would never engage. It is the whole
   reason this works.

   THE SAME RULE SERVES ALL THREE LISTS - the public Guest Book, the
   tribute window it sits in, and the moderation panel - because a
   moderator reading a tribute and a visitor reading it want the same
   thing. Only the height differs, and only by view.
   =================================================================== */
:root {
  /* Tuned against a real 450-word tribute in the browser: the header and
     the date take a fixed ~150px of any card, so 34vh left only three
     lines of the message showing. These give six or seven - enough to
     read a paragraph without scrolling, which is the point. */
  --tribute-card-h: clamp(360px, 44vh, 480px);
  --tribute-card-h-list: clamp(380px, 52vh, 560px);
}

.approved-grid > article:not(.tribute-placeholder-card),
.admin-tributes > article {
  display: flex;
  flex-direction: column;
  height: var(--tribute-card-h);
  contain-intrinsic-size: auto var(--tribute-card-h);   /* match the real height */
}

/* the grid must let them fill their row rather than sit at the top */
.approved-grid { align-items: stretch; }

/* the two header lines sit close, so the message gets the room instead */
.approved-grid > article:not(.tribute-placeholder-card) > h4,
.admin-tributes > article > h4 { margin-bottom: 4px; }
.approved-grid > article:not(.tribute-placeholder-card) > p:not(:last-of-type),
.admin-tributes > article > p:not(:last-of-type) { margin: 2px 0; }

/* the message: the one part that flexes, and the one part that scrolls */
.approved-grid > article:not(.tribute-placeholder-card) > p:last-of-type,
.admin-tributes > article > p:last-of-type {
  flex: 1 1 auto;
  min-height: 0;                 /* see above - without this, nothing scrolls */
  overflow-y: auto;
  overscroll-behavior: contain;
  max-width: none;               /* it fills the tile now, not 78ch */
  margin-bottom: 4px;
  padding-right: 6px;            /* the words keep clear of the scrollbar */
  scrollbar-width: thin;
  scrollbar-color: var(--gold, #c8912c) transparent;
}

/* the site's own gold, so an inner scrollbar belongs to the page */
.approved-grid > article > p:last-of-type::-webkit-scrollbar,
.admin-tributes > article > p:last-of-type::-webkit-scrollbar { width: 8px; }
.approved-grid > article > p:last-of-type::-webkit-scrollbar-thumb,
.admin-tributes > article > p:last-of-type::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, #e0b348, #c8912c);
  border-radius: 99px;
  border: 2px solid transparent;
  background-clip: content-box;
}
.approved-grid > article > p:last-of-type::-webkit-scrollbar-track,
.admin-tributes > article > p:last-of-type::-webkit-scrollbar-track { background: transparent; }

/* the foot of the card stays at the foot */
.approved-grid > article > small,
.admin-tributes > article > small { flex: 0 0 auto; margin-top: auto; }
.approved-grid > article > .att-row,
.admin-tributes > article > .att-row,
.admin-tributes > article > .actions { flex: 0 0 auto; }

/* ---- one by one: wider tiles deserve to be taller ---- */
.approved-grid.is-list > article:not(.tribute-placeholder-card),
.admin-tributes.is-list > article { height: var(--tribute-card-h-list); }
.approved-grid.is-list > article > p:last-of-type { max-width: none; }

/* ---- the placeholder is a sentence, not a tribute ---- */
.tribute-placeholder-card { height: auto; }

/* ---- PHONE ---- */
@media (max-width: 620px) {
  :root {
    --tribute-card-h: clamp(240px, 44vh, 320px);
    --tribute-card-h-list: clamp(260px, 52vh, 380px);
  }
}

/* ---- PHONE HELD SIDEWAYS: very little height to give away ---- */
@media (max-height: 520px) and (orientation: landscape) {
  :root {
    --tribute-card-h: 66vh;
    --tribute-card-h-list: 72vh;
  }
}

/* ---- TABLET ---- */
@media (min-width: 621px) and (max-width: 1024px) {
  :root { --tribute-card-h: clamp(280px, 38vh, 380px); }
}

/* ---- TELEVISION: read from a distance, so more of each tribute at once ---- */
@media (min-width: 1900px) {
  :root {
    --tribute-card-h: clamp(360px, 32vh, 520px);
    --tribute-card-h-list: clamp(420px, 44vh, 640px);
  }
}
@media (min-width: 2560px) {
  :root {
    --tribute-card-h: clamp(440px, 30vh, 620px);
    --tribute-card-h-list: clamp(520px, 42vh, 760px);
  }
}
