/* ===== AQMAR — design tokens + helpers (lift to webui/styles.css) ===== */

:root {
  /* Dark-forest palette: deep green ground, gold brand. The SPA ships in
     dark mode only — index.html hardcodes data-theme="dark" and there is no
     theme toggle, so these are the single source of truth. (The earlier
     light/earth palette was removed 2026-05-20 — it never rendered.) */
  --bg:        #0c1f0c;
  --bg-2:      #0f2510;
  --bg-3:      #142914;
  --paper:     #142914;
  --ink:       #fbf8f1;
  --ink-2:     #e7decb;
  --muted:     #9bbf94;
  --faint:     #5a7a5a;
  --divider:   #2d4a2d;
  /* --forest is the gold brand color so badge-forest, btn-primary,
     section-kickers, etc. all read as the warm accent. */
  --forest:    #fbbf24;
  --forest-2:  #d4a020;
  --olive:     #b8924a;
  --olive-2:   #d4a865;
  --gold:      #fbbf24;
  --crimson:   #ea4444;
  /* AI verification track (2026-06-10) — teal, distinct from the human
     gold/forest so the two verification dimensions never visually blur. */
  --ai:        #4cc3b0;
  --ai-dim:    rgba(76, 195, 176, 0.12);

  --font-display: "El Messiri", "Amiri", serif;
  --font-naskh:   "Amiri", serif;
  --font-body:    "IBM Plex Sans Arabic", system-ui, sans-serif;
  --font-latin-sans: "IBM Plex Sans", system-ui, sans-serif;
  --font-latin-serif: "IBM Plex Sans", "Amiri", serif;

  /* Fluid type scale — each step interpolates between a mobile min and a
     desktop max, so text resizes smoothly with the viewport (no breakpoints). */
  --text-xs:   clamp(0.72rem, 0.69rem + 0.15vw, 0.80rem);
  --text-sm:   clamp(0.80rem, 0.76rem + 0.20vw, 0.92rem);
  --text-base: clamp(0.94rem, 0.89rem + 0.25vw, 1.06rem);
  --text-lg:   clamp(1.05rem, 0.97rem + 0.40vw, 1.30rem);
  --text-xl:   clamp(1.25rem, 1.08rem + 0.85vw, 1.75rem);
  --text-2xl:  clamp(1.55rem, 1.20rem + 1.70vw, 2.55rem);
  --text-hero: clamp(2.60rem, 1.55rem + 5.20vw, 5.25rem);
}

/* When dark theme is active, the paper-grain pattern needs lighter dots
   so the texture stays visible against the near-black ground. */
html[data-theme="dark"] body::before {
  background-image:
    radial-gradient(rgba(155,191,148,0.04) 1px, transparent 1px),
    radial-gradient(rgba(155,191,148,0.03) 1px, transparent 1px);
  mix-blend-mode: screen;
  opacity: 0.5;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  direction: rtl;
  -webkit-font-smoothing: antialiased;
}
html[lang="en"] body { direction: ltr; }
::selection { background: var(--forest); color: var(--paper); }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
:focus-visible { outline: 2px solid var(--forest); outline-offset: 3px; border-radius: 4px; }

/* Paper grain */
body::before {
  content: "";
  position: fixed; inset: 0;
  background-image:
    radial-gradient(rgba(120,90,50,0.025) 1px, transparent 1px),
    radial-gradient(rgba(120,90,50,0.018) 1px, transparent 1px);
  background-size: 3px 3px, 7px 7px;
  background-position: 0 0, 1px 2px;
  pointer-events: none;
  z-index: 0;
  mix-blend-mode: multiply;
}
html[data-theme="dark"] body::before { mix-blend-mode: screen; opacity: 0.4; }

#root { position: relative; z-index: 1; min-height: 100vh; }

/* Type utilities */
.font-display { font-family: var(--font-display); }
.font-naskh   { font-family: var(--font-naskh); }
.font-body    { font-family: var(--font-body); }
.font-latin-sans  { font-family: var(--font-latin-sans); }
.font-latin-serif { font-family: var(--font-latin-serif); }

/* Color utilities */
.bg-paper     { background: var(--paper); }
.bg-page-2    { background: var(--bg-2); }
.bg-page-3    { background: var(--bg-3); }
.bg-forest    { background: var(--forest); }
.text-ink     { color: var(--ink); }
.text-ink-2   { color: var(--ink-2); }
.text-muted   { color: var(--muted); }
.text-faint   { color: var(--faint); }
.text-forest  { color: var(--forest); }
.text-olive   { color: var(--olive); }
.text-paper   { color: var(--paper); }
.border-divider { border-color: var(--divider); }
.bg-forest-grad { background: linear-gradient(110deg, var(--forest) 0%, var(--forest-2) 100%); }

/* Ornaments */
.ornament-row {
  display: flex; align-items: center; justify-content: center; gap: 14px;
}
.ornament-row::before, .ornament-row::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(to left, transparent, var(--divider) 30%, var(--divider) 70%, transparent);
}
.ornament-star {
  width: 12px; height: 12px;
  background: var(--olive);
  clip-path: polygon(50% 0%, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0% 50%, 39% 39%);
  opacity: 0.7;
}

/* Buttons */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 18px;
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 15px; font-weight: 500;
  border: 1px solid transparent;
  transition: all .15s ease;
  white-space: nowrap;
  line-height: 1;
}
.btn-primary {
  background: var(--forest); color: var(--paper);
  border-color: var(--forest);
}
.btn-primary:hover { background: var(--forest-2); }
.btn-ghost {
  background: transparent; color: var(--ink);
  border-color: var(--divider);
}
.btn-ghost:hover { background: var(--bg-3); }
.btn-text {
  background: transparent; color: var(--muted);
  padding: 6px 0;
  border-radius: 0;
}
.btn-text:hover { color: var(--ink); }

/* Form inputs */
.input, .select, textarea.input {
  width: 100%;
  padding: 10px 14px;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--divider);
  border-radius: 4px;
  outline: none;
}
.select {
  appearance: none;
  padding-inline-end: 32px;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
}
.field-label {
  font-family: var(--font-body);
  font-size: var(--text-xs); font-weight: 500;
  color: var(--muted);
  margin-bottom: 6px;
  letter-spacing: 0;
}

/* Badges */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px;
  background: var(--bg-3);
  color: var(--ink-2);
  border-radius: 999px;
  font-size: 12px; font-weight: 500;
}
.badge-forest  { background: rgba(31,61,43,0.10);   color: var(--forest); }
.badge-olive   { background: rgba(138,109,59,0.13); color: var(--olive); }

/* Cards */
.card {
  background: var(--paper);
  border: 1px solid var(--divider);
  border-radius: 6px;
}
.card-hover {
  transition: all .2s ease;
  box-shadow: 0 1px 0 rgba(0,0,0,0.02);
}
.card-hover:hover {
  transform: translateY(-2px);
  border-color: var(--olive);
  box-shadow: 0 16px 30px -22px rgba(40,30,15,0.4);
}

/* ===== Admin edit — image carousel =====
   Replaces the old 220px portrait + 3-col OCR-frames thumbnail strip with a
   single big stage that cycles through the portrait + every OCR frame for the
   row. Arrows + thumbstrip control it; the big image is clickable for max-
   screen zoom (re-uses the photoZoomed modal, now bound to carouselCurrent). */
.edit-carousel-stage {
  position: relative;
  background: #000;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--divider);
  min-height: 460px;
  display: flex; align-items: center; justify-content: center;
}
.edit-carousel-stage img {
  max-width: 100%;
  max-height: 78vh;
  object-fit: contain;
  display: block;
  cursor: zoom-in;
}
.edit-carousel-arrow {
  /* Nudged ~2cm below vertical-centre so the prev/next buttons no longer cover
     the birth date printed on the OCR poster/frame — the admin reads the date
     off this image, and centred arrows sat on top of it. */
  position: absolute; top: 70%; transform: translateY(calc(-80% + 2cm));
  width: 44px; height: 44px;
  background: rgba(0,0,0,0.55);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.3);
  border-radius: 999px;
  font-size: 22px;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition: background .15s ease;
  z-index: 2;
}
.edit-carousel-arrow:hover { background: rgba(0,0,0,0.78); }
.edit-carousel-arrow.prev { inset-inline-start: 12px; }
.edit-carousel-arrow.next { inset-inline-end: 12px; }
.edit-carousel-counter {
  position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%);
  background: rgba(0,0,0,0.55);
  color: rgba(255,255,255,0.95);
  font-family: var(--font-latin-sans);
  font-size: 12px; font-weight: 600;
  padding: 4px 10px; border-radius: 999px;
  backdrop-filter: blur(6px);
  letter-spacing: 0.05em;
  z-index: 2;
}
.edit-carousel-kind {
  position: absolute; top: 10px; inset-inline-start: 10px;
  background: rgba(31,61,43,0.85);
  color: var(--forest);
  font-family: var(--font-display);
  font-size: 10px;
  padding: 3px 9px; border-radius: 999px;
  letter-spacing: 0.18em; text-transform: uppercase;
  backdrop-filter: blur(6px);
  z-index: 2;
}
.edit-carousel-thumbs {
  margin-top: 10px;
  display: grid; gap: 6px;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
}
.edit-carousel-thumb {
  position: relative;
  aspect-ratio: 4/3;
  background: var(--bg-3);
  border-radius: 5px;
  overflow: hidden;
  border: 2px solid transparent;
  cursor: pointer;
  transition: border-color .12s ease;
  padding: 0;
}
.edit-carousel-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.edit-carousel-thumb:hover { border-color: var(--olive); }
.edit-carousel-thumb.active { border-color: var(--forest); }
/* ★ badge on the thumbnail that matches dbo.martyrs.featured_frame_path —
   tells admin "this is what's stored in the DB right now". Sits in the
   top-end corner so it doesn't overlap the active-border thick edge. */
.edit-carousel-thumb-saved {
  position: absolute; top: 2px; inset-inline-end: 2px;
  width: 16px; height: 16px;
  background: var(--forest);
  color: var(--paper);
  border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; line-height: 1;
  font-weight: 700;
  z-index: 1;
}
/* Status strip directly under the stage. Color reflects the cover-image
   state: forest gold = saved/will-save (good), olive = needs action. */
.edit-carousel-status {
  margin-top: 8px;
  font-family: var(--font-body);
  font-size: 12px;
  padding: 7px 10px;
  border-radius: 4px;
  text-align: center;
}
.edit-carousel-status.is-good {
  background: rgba(251,191,36,0.08);
  color: var(--forest);
  border: 1px solid rgba(251,191,36,0.25);
}
.edit-carousel-status.needs-action {
  background: rgba(184,146,74,0.10);
  color: var(--olive);
  border: 1px solid rgba(184,146,74,0.3);
}
@media (max-width: 1024px) {
  .edit-carousel-stage { min-height: 320px; }
}

/* Portrait placeholder (monogram) */
.portrait {
  position: relative;
  border-radius: 3px;
  overflow: hidden;
  background: linear-gradient(160deg, #cfd9c8 0%, #94ae93 60%, var(--forest-2) 100%);
  box-shadow:
    0 0 0 1px var(--divider),
    0 0 0 5px var(--paper),
    0 0 0 6px var(--divider),
    0 12px 24px -16px rgba(40,30,15,0.35);
  flex-shrink: 0;
}
.portrait.tone-olive {
  background: linear-gradient(160deg, #e8dcc0 0%, #c9b48a 70%, var(--olive-2) 100%);
}
.portrait.naked { box-shadow: none; }
.portrait img {
  width: 100%; height: 100%; object-fit: cover;
  display: block;
}
.portrait .silhouette { position: absolute; inset: 0; opacity: .18; }
.portrait .corner { position: absolute; width: 8px; height: 8px; }
.portrait .corner.tl { top:4px; left:4px; border-top:1px solid rgba(255,255,255,.5); border-left:1px solid rgba(255,255,255,.5); }
.portrait .corner.tr { top:4px; right:4px; border-top:1px solid rgba(255,255,255,.5); border-right:1px solid rgba(255,255,255,.5); }
.portrait .corner.bl { bottom:4px; left:4px; border-bottom:1px solid rgba(255,255,255,.5); border-left:1px solid rgba(255,255,255,.5); }
.portrait .corner.br { bottom:4px; right:4px; border-bottom:1px solid rgba(255,255,255,.5); border-right:1px solid rgba(255,255,255,.5); }

/* Section header */
.section-kicker {
  font-family: var(--font-body);
  font-size: var(--text-xs); font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--olive);
  text-transform: uppercase;
  margin-bottom: 10px;
}
.h1-display {
  font-size: var(--text-hero);
}
.section-title {
  font-family: var(--font-display);
  font-size: var(--text-2xl); font-weight: 500;
  color: var(--ink);
  margin: 0; line-height: 1.15;
}
.section-sub {
  font-family: var(--font-body);
  font-size: var(--text-base); color: var(--muted);
  margin-top: 10px;
  max-width: 560px;
}

/* Scrollbar */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--faint); border-radius: 999px; border: 2px solid var(--bg); }

/* Grid layouts — defaults live here so the media queries below can override
   them without !important (the defaults used to be inline styles in the HTML). */
.grid-bday       { grid-template-columns: 1.4fr 1.2fr auto; align-items: end; }
.stats-strip {
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--divider);
  border-bottom: 1px solid var(--divider);
}
.grid-filters    { grid-template-columns: 1.4fr 1fr 1fr 1fr 1fr auto; align-items: end; }
.grid-detail     { grid-template-columns: 200px 1fr; }
/* Admin edit panel: image carousel on the right (first column in RTL), form on
   the left. 2fr/3fr = ~40%/60% — picked over 50/50 and 60/40 by the user
   2026-05-25 because it gives a big carousel image without cramping the
   9 optional form fields. align-items: start prevents the form's natural
   height from stretching the image column and leaving black letterbox space. */
.grid-detail.is-admin { grid-template-columns: 2fr 3fr; align-items: start; }
.dates-strip {
  grid-template-columns: 1fr 1fr 1fr;
  border-top: 1px solid var(--divider);
  border-bottom: 1px solid var(--divider);
}
.grid-2col       { grid-template-columns: 1fr 1fr; }
.footer-grid     { grid-template-columns: 1.5fr 1fr 1fr 1fr; }
.grid-pair       { grid-template-columns: 1fr 1fr; }

/* ===== Person-detail "Media & source" block (2026-06-16) =====
   Selected video-card frame as the lead image + portrait photo beside it +
   "Watch on Telegram" button (Option B). Flex (not grid) so the layout
   collapses cleanly when a row has only a photo or only a frame: a lone
   .media-side keeps its own width instead of stretching to full width. */
.media-block { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
.media-figure {
  margin: 0;
  background: var(--paper);
  border: 1px solid var(--divider);
  border-radius: 12px;
  overflow: hidden;
}
.media-figure-lead { flex: 0 1 380px; max-width: 420px; }
.media-side { flex: 0 1 240px; display: flex; flex-direction: column; gap: 14px; }
.media-side .media-figure { width: 100%; }
/* Show the whole image — no crop, no zoom. The box height follows the image's
   natural aspect ratio instead of forcing 4:5 with object-fit:cover. */
.media-imgbox { width: 100%; background: var(--bg); }
.media-imgbox img { width: 100%; height: auto; display: block; }
.media-cap {
  padding: 9px 12px;
  font-size: var(--text-xs);
  color: var(--muted);
  text-align: center;
  border-top: 1px solid var(--divider);
}

/* "Watch on Telegram" button — dark-forest gradient card with a gold ▶ disc,
   two-line label, ↗ tail. Tokens only; the gold disc tint is mixed from
   --forest so it tracks the brand color. */
.tg-watch {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 12px;
  background: linear-gradient(110deg, var(--bg-3), var(--bg-2));
  border: 1px solid var(--divider);
  text-decoration: none;
  color: var(--ink);
  transition: transform .2s, box-shadow .2s, border-color .2s;
  box-shadow: 0 6px 18px -10px rgba(0, 0, 0, .6);
}
.tg-watch:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 26px -10px rgba(0, 0, 0, .7);
  border-color: var(--forest-2);
}
.tg-watch-ic {
  flex: 0 0 auto;
  width: 42px; height: 42px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 18px;
  color: var(--forest);
  background: color-mix(in srgb, var(--forest) 16%, transparent);
}
.tg-watch-txt { display: flex; flex-direction: column; min-width: 0; }
.tg-watch-title { font-size: var(--text-sm); font-weight: 700; line-height: 1.25; }
.tg-watch-sub { font-size: var(--text-xs); color: var(--muted); margin-top: 2px; }
.tg-watch-tail { margin-inline-start: auto; color: var(--faint); font-size: 15px; }

/* Admin registry table — scrolls sideways on screens too narrow for its columns,
   so the table never breaks the page layout on a phone. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll > table { min-width: 720px; }
/* Slimmer inline padding than Tailwind's p-3.5 (14px): with 11 columns the
   28px-per-cell padding alone ate ~300px, crushing the auto-width name
   column. Class+element selector outruns the single-class utility. (2026-06-11) */
.table-scroll th, .table-scroll td { padding-inline: 10px; }
.table-scroll::-webkit-scrollbar { height: 8px; }
.table-scroll::-webkit-scrollbar-track { background: transparent; }
.table-scroll::-webkit-scrollbar-thumb { background: var(--faint); border-radius: 999px; }

/* ===== AI verification (admin, 2026-06-10) ===== */
/* Option B stats strip — 4 cells above the admin filter card. Chosen by the
   user from webui/_preview_ai_verify.html over the in-card chips variant. */
.ai-stats-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background: var(--paper);
  border: 1px solid var(--divider);
  border-radius: 8px;
  overflow: hidden;
}
.ai-stat {
  padding: 14px 18px;
  text-align: center;
  border-inline-start: 1px solid var(--divider);
}
.ai-stat:first-child { border-inline-start: none; }
.ai-stat .v {
  font-family: var(--font-latin-sans);
  font-size: 26px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.ai-stat .l { font-size: 12px; color: var(--muted); margin-top: 2px; }
.ai-stat .v-human { color: var(--gold); }
.ai-stat .v-ai    { color: var(--ai); }
.ai-stat .v-rest  { color: var(--ink-2); }

/* AI badge in the admin table (tooltip carries ai_note) */
.ai-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ai);
  background: var(--ai-dim);
  border: 1px solid rgba(76, 195, 176, 0.35);
  border-radius: 999px;
  padding: 2px 10px;
  cursor: help;
}

/* AI audit panel at the top of the admin edit form */
.ai-note-panel {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  background: var(--ai-dim);
  border: 1px solid rgba(76, 195, 176, 0.35);
  border-radius: 8px;
  padding: 12px 16px;
}
.ai-note-panel .t { font-size: 13.5px; font-weight: 700; color: var(--ai); margin-bottom: 3px; }
.ai-note-panel .d { font-size: 12.5px; color: var(--ink-2); line-height: 1.7; }
.ai-note-panel .when { font-family: var(--font-latin-sans); font-size: 11px; color: var(--muted); margin-top: 4px; }

/* Responsive — tablet (1024) / large phone (768) / small phone (480).
   Grid rules need no !important now that defaults live in classes above. The
   .nav-links / .admin-banner / .hero-pad rules DO keep it — they override
   Tailwind utility classes (.flex / .items-center / .px-9 .py-8). */
@media (max-width: 1024px) {
  .nav-links { display: none !important; }
  .nav-hamburger { display: inline-flex; }
  .mobile-nav { display: block; }
  .grid-filters { grid-template-columns: 1fr 1fr 1fr; }
  .grid-detail, .grid-detail.is-admin { grid-template-columns: 1fr; }
  .grid-detail-portrait { justify-self: center; }
}
@media (max-width: 768px) {
  /* Birthday-match grid: date picker, window pills, submit each get their
     own full-width row — the only layout that reads cleanly this narrow. */
  .grid-bday { grid-template-columns: 1fr; }
  .grid-bday > .grid-bday-window,
  .grid-bday > .grid-bday-submit { grid-column: 1 / -1; }
  .grid-filters { grid-template-columns: 1fr 1fr; }
  .grid-2col { grid-template-columns: 1fr; }
  /* Media block stacks: frame, then photo + Telegram button, each full width. */
  .media-block { flex-direction: column; }
  .media-figure-lead, .media-side { flex: 1 1 auto; width: 100%; max-width: 100%; }
  .stats-strip { grid-template-columns: 1fr 1fr; }
  /* AI stats strip folds 4 cells into 2×2; the 3rd cell starts a new visual
     row so it loses its start-border and gains a top one. */
  .ai-stats-strip { grid-template-columns: 1fr 1fr; }
  .ai-stat:nth-child(3) { border-inline-start: none; }
  .ai-stat:nth-child(n+3) { border-top: 1px solid var(--divider); }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-grid > *:first-child { grid-column: 1 / -1; }
  .admin-banner { flex-direction: column; align-items: flex-start !important; gap: 16px; }
  .dates-strip { grid-template-columns: 1fr 1fr; }
  .grid-pair { grid-template-columns: 1fr; }
  /* Tighten the header so logo + controls (incl. the admin button) fit
     small phones without overflowing. !important beats Tailwind px-8 / gap-6. */
  .header-bar { padding-inline: 16px !important; gap: 12px !important; }
}
@media (max-width: 480px) {
  .grid-filters { grid-template-columns: 1fr; }
  .stats-strip { grid-template-columns: 1fr 1fr; }
  .hero-pad { padding: clamp(20px, 5vw, 44px) !important; }
}

/* Skip-to-content link — visible only on keyboard focus */
.skip-link {
  position: absolute; top: -40px; inset-inline-start: 1rem;
  background: var(--forest); color: var(--paper);
  padding: 0.5rem 1rem; border-radius: 0 0 6px 6px;
  font-weight: bold; z-index: 100;
  transition: top 0.15s ease-out;
  font-family: var(--font-body);
}
.skip-link:focus { top: 0; }

/* Loading skeleton — shimmer animation used during the initial data fetch */
.skeleton {
  display: block;
  background: linear-gradient(90deg, var(--bg-3) 0%, var(--bg-2) 50%, var(--bg-3) 100%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite linear;
  border-radius: 4px;
}
@keyframes shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Screen-reader-only utility — visually hidden but readable by AT */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Mobile nav drawer + hamburger — desktop hides hamburger; mobile hides nav-links */
.nav-hamburger {
  display: none;
  background: transparent; border: 1px solid var(--divider);
  border-radius: 8px; padding: 6px;
  color: var(--ink); cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.nav-hamburger:hover { background: var(--bg-2); border-color: var(--olive); }
.mobile-nav { display: none; }

/* ── Date picker — Litepicker restyled to the AQMAR dark theme ───────────
   Litepicker themes through its own --litepicker-* custom properties; setting
   them on .litepicker overrides the library defaults for every calendar.
   Covers all three pickers (home birthday search + admin birth/martyrdom). */
.litepicker {
  --litepicker-container-months-color-bg: var(--paper);
  --litepicker-container-months-box-shadow-color: rgba(0,0,0,.55);
  --litepicker-footer-color-bg: var(--bg-3);
  --litepicker-footer-box-shadow-color: var(--divider);
  --litepicker-tooltip-color-bg: var(--bg-3);
  --litepicker-month-header-color: var(--ink);
  --litepicker-button-prev-month-color: var(--muted);
  --litepicker-button-next-month-color: var(--muted);
  --litepicker-button-prev-month-color-hover: var(--forest);
  --litepicker-button-next-month-color-hover: var(--forest);
  --litepicker-month-weekday-color: var(--faint);
  --litepicker-month-week-number-color: var(--faint);
  --litepicker-day-width: 40px;
  --litepicker-day-color: var(--ink-2);
  --litepicker-day-color-hover: var(--forest);
  --litepicker-is-today-color: var(--olive-2);
  --litepicker-is-in-range-color: rgba(251,191,36,.14);
  --litepicker-is-locked-color: var(--faint);
  --litepicker-is-start-color: #1a1505;
  --litepicker-is-start-color-bg: var(--forest);
  --litepicker-is-end-color: #1a1505;
  --litepicker-is-end-color-bg: var(--forest);
  --litepicker-highlighted-day-color: var(--ink);
  --litepicker-highlighted-day-color-bg: var(--bg-3);
  --litepicker-button-apply-color-bg: var(--forest);
  --litepicker-button-apply-color: #1a1505;
  --litepicker-button-cancel-color-bg: var(--bg-3);
  --litepicker-button-cancel-color: var(--ink);
  font-family: var(--font-body);
}
.litepicker .container__months {
  border: 1px solid var(--divider);
  border-radius: 8px;
}
.litepicker .container__days > div,
.litepicker .container__days > a { font-size: var(--text-sm); }
/* Month + year <select> dropdowns (the SPA inits Litepicker with dropdowns). */
.litepicker .month-item-header select {
  background: var(--bg-3);
  color: var(--ink);
  border: 1px solid var(--divider);
  border-radius: 6px;
  padding: 4px 6px !important;
  margin: 0 4px;
  font-family: var(--font-body);
}
.litepicker .month-item-header select.month-item-year {
  min-width: 70px;
}

.month-item-year {
    text-align: center;
    text-align-last: center;
}

.month-item-name option,
.month-item-year option {
    text-align: center;
}


.litepicker .month-item-header select option {
  background: var(--bg-2);
  color: var(--ink);
}

/* ===== Boot spinner (offline-cache first load) =====
   Full-screen overlay shown from the first paint until app.js init() has
   loaded the registry and written the cache version. Lives outside the Alpine
   root, so it does not depend on Alpine being ready. */
#boot-spinner {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  background: var(--bg);
  transition: opacity 0.35s ease;
}
#boot-spinner.boot-hide {
  opacity: 0;
  pointer-events: none;
}
.boot-ring {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 3px solid var(--divider);
  border-top-color: var(--forest);
  animation: boot-spin 0.8s linear infinite;
}
.boot-label {
  font-family: var(--font-body);
  color: var(--muted);
  font-size: var(--text-sm);
  letter-spacing: 0.05em;
}
@keyframes boot-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .boot-ring { animation-duration: 2s; }
}

/* ===== Combined search page — locked secondary filters (F1) =====
   Until the user picks a birthday OR clicks "browse the full registry", the
   secondary filters are dimmed + inert and a dashed hint row explains how to
   unlock them. Toggled by the `filtersUnlocked` Alpine flag. */
.filters-locked .lockable {
  opacity: 0.4;
  filter: grayscale(0.4);
  pointer-events: none;
  user-select: none;
}
.lock-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
  border: 1px dashed var(--divider);
  border-radius: 10px;
  padding: 18px 22px;
  background: rgba(251, 191, 36, 0.05);
}

/* "More filters" collapse toggle (2026-06-17): the heading row is a button.
   The caret points right when collapsed, down when expanded. */
.filters-toggle {
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: start;
}
.filters-chevron {
  display: inline-block;
  font-size: 11px;
  line-height: 1;
  transform: rotate(-90deg);
  transition: transform .2s ease;
}
.filters-chevron.is-open { transform: rotate(0deg); }
