:root {
  --ink:      #1b1333;   /* deep plum, mascot outline + text-on-accent */
  --bg:       #17122a;   /* plum-ink background */
  --surface:  #211a3a;
  --surface-2:#2b2247;
  --border:   #392c58;
  --coral:    #ff5d8f;   /* primary pop */
  --mint:     #49d6c0;   /* secondary pop */
  --butter:   #ffc857;   /* tertiary pop */
  --lavender: #b39cff;   /* quaternary pop */
  --cheek:    #ff9dc0;   /* mascot cheeks */
  --text:     #f6f1ff;
  --muted:    #a99ec9;
  --radius:   14px;
  --radius-lg:20px;
  --topbar-bg:rgba(23,18,42,0.82);  /* sticky topbar; per-theme so light skins don't get a dark bar */
}

/* ---------- wrestler themes ----------
   Each block overrides the same custom-property contract defined on :root, so
   the whole UI re-skins from these ~13 variables alone. data-theme is stamped
   on <html> server-side (from the friendlies_theme cookie) so there's no flash
   of the default palette on load. Slugs mirror the THEMES set in app.py and the
   THEMES list in app.js. Names are placeholders the user plans to rename — the
   comment records who each represents so that mapping isn't lost.

   default (:root above) = Rika-adjacent plum/coral (the original look). */

/* Rika — black, white, silver, shocks of red (Rika Tatsumi / 辰巳リカ) */
[data-theme="rika"] {
  --ink:#0d0d0f; --bg:#111114; --surface:#1b1b20; --surface-2:#26262d;
  --border:#38383f; --coral:#e5133a; --mint:#c9ccd4; --butter:#eef0f4;
  --lavender:#9aa0ad; --cheek:#ff6b7f; --text:#f4f5f7; --muted:#9a9aa6;
  --topbar-bg:rgba(13,13,15,0.85);
}

/* Shino — green, greenish-yellow, white (Shino Suzuki / 鈴木志乃).
   Softened from the original: --mint was a near-neon yellow-green (#c7e94d)
   used on hover/focus/badges — very high luminance against the dark green
   background, genuinely harsh to stare at. Pulled it down to a muted sage-lime
   and eased the primary green pop off max saturation so nothing glows. */
[data-theme="shino"] {
  --ink:#0f2417; --bg:#122820; --surface:#1a3329; --surface-2:#234036;
  --border:#33564a; --coral:#4bc48a; --mint:#a9cf5c; --butter:#dcecc0;
  --lavender:#8bcfa8; --cheek:#a6ef7f; --text:#eef6ee; --muted:#8fb39c;
  --topbar-bg:rgba(18,40,32,0.85);
}

/* Miu — shades of pink, white (Miu Watanabe / 渡辺未詩) */
[data-theme="miu"] {
  --ink:#4a1130; --bg:#fff2f8; --surface:#ffe3ef; --surface-2:#ffd3e5;
  --border:#f7b3d0; --coral:#ff4f9a; --mint:#ff8fc0; --butter:#ffc2dd;
  --lavender:#d96fb0; --cheek:#ff9ec9; --text:#5a1136; --muted:#b56690;
  --topbar-bg:rgba(255,242,248,0.85);
}

/* HIMAWARI — bright yellow, orange, brown (sunflower) */
[data-theme="himawari"] {
  --ink:#3a2408; --bg:#2a1c0a; --surface:#3a2810; --surface-2:#4b3416;
  --border:#664824; --coral:#ff9c1a; --mint:#ffd23f; --butter:#ffe07a;
  --lavender:#d98a3c; --cheek:#ffbe5c; --text:#fff5e0; --muted:#c9a56f;
  --topbar-bg:rgba(42,28,10,0.85);
}

/* Raku — softer yellow, pastels (Raku / らく) */
[data-theme="raku"] {
  --ink:#5a4a2e; --bg:#fffdf3; --surface:#fdf6e0; --surface-2:#f7edcf;
  --border:#ecdcae; --coral:#f7b955; --mint:#a8d8c0; --butter:#ffe9a6;
  --lavender:#c4b0e0; --cheek:#ffc9d6; --text:#6b5836; --muted:#a99a76;
  --topbar-bg:rgba(255,253,243,0.85);
}

/* Arisu — bright rainbow skewed purple, b&w zebra accent (Arisu Endo / 遠藤有栖).
   Loud & candy-bright on purpose: a vivid violet base (not near-black) so the
   whole UI reads purple, with max-saturation rainbow pops (magenta→lime→amber→
   cyan) across the accent slots. b&w zebra carried by the near-black ink and
   bright-white text. */
[data-theme="arisu"] {
  --ink:#160a24; --bg:#3a1a6e; --surface:#4a2288; --surface-2:#5c2ca8;
  --border:#7d44d6; --coral:#f4f405; --mint:#7bff3d; --butter:#ffd21a;
  --lavender:#26e0ff; --cheek:#ff8fe6; --text:#ffffff; --muted:#d0b6ff;
  --topbar-bg:rgba(58,26,110,0.82);
}

/* Yuki Arai — blue, white, red, stars; emphasis blue (Yuki Arai / 荒井優希). */
[data-theme="arai"] {
  --ink:#0a1a3a; --bg:#0d1e46; --surface:#152a5c; --surface-2:#1e3873;
  --border:#2f4d94; --coral:#e63054; --mint:#5b9dff; --butter:#e8f0ff;
  --lavender:#8fb8ff; --cheek:#ff8fa8; --text:#f0f6ff; --muted:#94b0dc;
  --topbar-bg:rgba(13,30,70,0.85);
}

/* Hyper Misao — teal, pink/magenta, black (Hyper Misao / ハイパーミサヲ). */
[data-theme="misao"] {
  --ink:#0a0a0d; --bg:#101015; --surface:#181820; --surface-2:#22222d;
  --border:#37374a; --coral:#ff3d97; --mint:#25d0c0; --butter:#7cf0e4;
  --lavender:#ff8fc4; --cheek:#ff9ecf; --text:#f2f4f6; --muted:#9aa4b0;
  --topbar-bg:rgba(16,16,21,0.85);
}

/* Kamiyu — denim (blue & white), sparkly silver/diamonds, black (Yuki Kamifuku
   / 上福ゆき / @zacyuki). Denim-blue base; the pops carry silver-white +
   diamond-ice tones; black ink. The woven denim texture and the twinkling
   diamond sparkles are added by the two overlay layers below (both scoped to
   this theme only) — see .denim-fx / [data-theme="kamiyu"] body::after. */
[data-theme="kamiyu"] {
  --ink:#0b0f1a; --bg:#1c3a63; --surface:#244a7a; --surface-2:#2d588c;
  --border:#3f6ba6; --coral:#eaf1fb; --mint:#bcd4ef; --butter:#dfe8f5;
  --lavender:#9ec3e8; --cheek:#cfe0f2; --text:#f4f8fd; --muted:#a9c2df;
  --topbar-bg:rgba(20,42,72,0.85);
  /* faint diagonal denim weave woven over --bg. Two opposing thread directions +
     a fine warp line, all very low-alpha so photos stay the focus. */
  --denim: repeating-linear-gradient(45deg,  rgba(255,255,255,0.035) 0 1px, transparent 1px 5px),
           repeating-linear-gradient(-45deg, rgba(0,0,0,0.06)       0 1px, transparent 1px 5px),
           repeating-linear-gradient(90deg,  rgba(255,255,255,0.02) 0 1px, transparent 1px 3px);
}
/* Denim texture layer: painted onto the body background only under this theme.
   The base :root body background (the two radial glows + --bg) still applies;
   this just prepends the weave on top of --bg via an extra background image. */
[data-theme="kamiyu"] body {
  background:
    radial-gradient(1200px 500px at 80% -10%, rgba(158,195,232,0.10), transparent 60%),
    radial-gradient(1000px 500px at 0% 0%, rgba(220,232,245,0.06), transparent 55%),
    var(--denim),
    var(--bg);
  background-attachment: fixed;
}
/* Sparkle layer: a fixed, non-interactive full-viewport wash of tiny silver
   "diamond" dots that slowly twinkle. Scoped to Kamiyu; pointer-events:none so
   it never eats clicks; low opacity to stay subtle. */
[data-theme="kamiyu"] body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.9) 0.5px, transparent 1.5px),
    radial-gradient(circle, rgba(220,235,255,0.7) 0.5px, transparent 1.5px);
  background-size: 180px 180px, 240px 240px;
  background-position: 20px 40px, 130px 110px;
  opacity: 0.5;
  animation: kamiyu-twinkle 5.5s ease-in-out infinite;
}
@keyframes kamiyu-twinkle {
  0%, 100% { opacity: 0.28; }
  50%      { opacity: 0.6; }
}
@media (prefers-reduced-motion: reduce) {
  [data-theme="kamiyu"] body::after { animation: none; opacity: 0.4; }
}

/* Pom — CLOWN MODE (Pom / ぽむ). Deliberately obnoxious, loud, chaotic, almost
   unusable — per request, leaning into her clown costume (cobalt polka-dot bib,
   red ruffles, plaid yellow, black&white harlequin skirt, primary chaos). This
   is the one theme that intentionally violates the "keep photos the focus"
   rule. Circus-primary palette; the loud background patterns + the constant
   hue-cycling + the wobble are the chaos. Everything decorative is
   pointer-events:none so it's *annoying*, not actually broken — clicks still
   land — and all motion is killed under prefers-reduced-motion. */
/* --pom-hue is a real animatable angle: typing it via @property lets it
   interpolate, so animating it cycles the *background color itself* (built from
   hsl(var(--pom-hue)...)) instead of filtering the whole page. That's the fix —
   the old `filter: hue-rotate()` on body recolored every descendant (photos,
   text, buttons) too, which is why the base stayed blue while content shifted. */
@property --pom-hue {
  syntax: '<angle>'; inherits: true; initial-value: 220deg;
}
[data-theme="pom"] {
  --pom-hue: 220deg;
  --ink:#10121c;
  --bg:        hsl(var(--pom-hue) 78% 42%);   /* the color that now cycles on its own */
  --surface:   hsl(var(--pom-hue) 72% 48%);
  --surface-2: hsl(var(--pom-hue) 70% 54%);
  --border:#ffd21a; --coral:#ff2020; --mint:#ffe000; --butter:#ff59b0;
  --lavender:#00c2ff; --cheek:#ff8fbf; --text:#ffffff; --muted:#ffe9a0;
  --radius:6px; --radius-lg:8px;   /* squarer, cheaper-looking corners */
  --topbar-bg: hsl(var(--pom-hue) 78% 42% / 0.7);
  /* bold black&white harlequin diamonds (the skirt) */
  --harlequin:
    repeating-linear-gradient(60deg,  rgba(0,0,0,0.5) 0 22px, transparent 22px 44px),
    repeating-linear-gradient(-60deg, rgba(0,0,0,0.5) 0 22px, transparent 22px 44px);
  /* fat white polka dots (the bib) */
  --polka: radial-gradient(circle, rgba(255,255,255,0.85) 9px, transparent 10px);
  /* drive the hue cycle here on :root so --bg / --surface / --topbar-bg all
     shift together. No `filter` anywhere = content stays its true colors. */
  animation: pom-huecycle 8s linear infinite;
}
/* Loud patterned background over the (now self-shifting) --bg. */
[data-theme="pom"] body {
  background:
    var(--polka),
    var(--harlequin),
    var(--bg);
  background-size: 64px 64px, 44px 76px, 44px 76px;
}
/* A second fixed layer of drifting confetti dots over everything. */
[data-theme="pom"] body::after {
  content: ""; position: fixed; inset: -20%; z-index: -1; pointer-events: none;
  background-image:
    radial-gradient(circle, #ff2020 6px, transparent 7px),
    radial-gradient(circle, #ffe000 6px, transparent 7px),
    radial-gradient(circle, #ff59b0 6px, transparent 7px);
  background-size: 150px 150px, 200px 210px, 260px 180px;
  background-position: 0 0, 70px 90px, 160px 40px;
  opacity: 0.55;
  animation: pom-drift 3.2s linear infinite;
}
/* The topbar can't sit still either. */
[data-theme="pom"] .topbar { animation: pom-wobble 0.9s ease-in-out infinite; }
[data-theme="pom"] .brand-star { animation: pom-spin 2.4s linear infinite; }

/* Hover = a fast, desynced hue-rotate on just the hovered element, so images,
   buttons, chips etc. churn through colors while pointed at. Different periods
   (2.1 / 1.3 / 1.7s) keep them out of phase so it reads as chaotic/random.
   Applied only :hover, only to the element itself — never an ancestor — so it
   doesn't bleed onto the rest of the page. */
[data-theme="pom"] .photo-card:hover { animation: pom-hoverchurn 2.1s linear infinite; }
[data-theme="pom"] .btn-accent:hover,
[data-theme="pom"] .btn-ghost:hover,
[data-theme="pom"] .btn-icon:hover,
[data-theme="pom"] .theme-btn:hover { animation: pom-hoverchurn 1.3s linear infinite; }
[data-theme="pom"] .ex-chip:hover,
[data-theme="pom"] .search-chip:hover,
[data-theme="pom"] .tag-chip:hover { animation: pom-hoverchurn 1.7s linear infinite; }

@keyframes pom-huecycle   { to { --pom-hue: 580deg; } }   /* 220 -> 220+360 = full wheel */
@keyframes pom-hoverchurn { to { filter: hue-rotate(360deg); } }
@keyframes pom-drift    { to { background-position: 150px 150px, 220px 300px, 420px 220px; } }
@keyframes pom-wobble   { 0%,100% { transform: rotate(-0.8deg); } 50% { transform: rotate(0.8deg); } }
@keyframes pom-spin     { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  [data-theme="pom"],
  [data-theme="pom"] body::after,
  [data-theme="pom"] .topbar,
  [data-theme="pom"] .brand-star,
  [data-theme="pom"] .photo-card:hover,
  [data-theme="pom"] .btn-accent:hover,
  [data-theme="pom"] .btn-ghost:hover,
  [data-theme="pom"] .btn-icon:hover,
  [data-theme="pom"] .theme-btn:hover,
  [data-theme="pom"] .ex-chip:hover,
  [data-theme="pom"] .search-chip:hover,
  [data-theme="pom"] .tag-chip:hover { animation: none; }
}

/* Pom B — Harajuku plaid (Pom / ぽむ, her *other* costume: the tartan-mix
   Harajuku look — navy/white/red/yellow plaid, cream knit, red trim). The calm
   counterpart to CLOWN MODE: cozy, muted, no animation, photos stay the focus.
   A faint static tartan weave sits on the background as the nod to the plaid.
   Originally a light cream theme; flipped to a dark warm-charcoal base (not
   the light bg re-tinted) for better reading comfort — cream/plaid colors now
   live in the surfaces and accents instead of the page background. */
[data-theme="pomb"] {
  --ink:#efe6d4; --bg:#211a14; --surface:#2c2319; --surface-2:#39301f;
  --border:#544632; --coral:#e0574f; --mint:#6f93d6; --butter:#e8b93a;
  --lavender:#8fa3d4; --cheek:#e6a0a0; --text:#f1e9db; --muted:#b5a488;
  --topbar-bg:rgba(33,26,20,0.88);
  /* faint tartan: two low-alpha red/navy stripe sets crossing over --bg */
  --tartan:
    repeating-linear-gradient(0deg,  rgba(224,87,79,0.07) 0 3px, transparent 3px 26px),
    repeating-linear-gradient(0deg,  rgba(111,147,214,0.07) 13px 16px, transparent 16px 26px),
    repeating-linear-gradient(90deg, rgba(224,87,79,0.07) 0 3px, transparent 3px 26px),
    repeating-linear-gradient(90deg, rgba(111,147,214,0.07) 13px 16px, transparent 16px 26px);
}
[data-theme="pomb"] body {
  background:
    var(--tartan),
    var(--bg);
  background-size: 26px 26px;
}

/* Suzume — cyber bee: neon yellow-green & silver, black trim (Suzume / すずめ).
   Hip/cool/cheerful, not soft — a near-black tech base (faint blue-gray tint,
   not flat black) with a chartreuse-neon primary pop and chrome-silver
   secondary, echoing her hex-panel gear. A faint honeycomb lattice is woven
   into the background as the "bee" nod, in the same restrained spirit as
   Kamiyu's denim weave — visible on close look, not distracting from photos. */
[data-theme="suzume"] {
  --ink:#0a0d0a; --bg:#111614; --surface:#181f1c; --surface-2:#212a25;
  --border:#33413a; --coral:#c8f21e; --mint:#d8d8dc; --butter:#eaffb0;
  --lavender:#9fb0aa; --cheek:#e6ff8f; --text:#f2f7ee; --muted:#8fa39a;
  --topbar-bg:rgba(17,22,20,0.85);
  /* honeycomb: two 60°-mirrored hex-edge line sets, very low alpha */
  --honeycomb:
    repeating-linear-gradient(60deg,  rgba(200,242,30,0.05) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(-60deg, rgba(200,242,30,0.05) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(0deg,   rgba(216,216,220,0.04) 0 1px, transparent 1px 34px);
}
[data-theme="suzume"] body {
  background:
    var(--honeycomb),
    var(--bg);
  background-size: 40px 68px;
}

/* Shoko — bestial kaiju (Shoko Nakajima / 中島翔子). Brown leather & fur, bone/
   cream, deep red, a touch of turquoise (the cord ties/wraps on her gear).
   Warm dark-brown base rather than black, so it reads "hide/leather," not
   generic dark mode. */
[data-theme="shoko"] {
  --ink:#1c130c; --bg:#241811; --surface:#2f2016; --surface-2:#3c291b;
  --border:#5a4128; --coral:#b3372a; --mint:#4fa8a0; --butter:#e8dcc4;
  --lavender:#9c7a52; --cheek:#d98f7a; --text:#f1e6d3; --muted:#b39a7c;
  --topbar-bg:rgba(28,19,12,0.87);
}

/* Haru — tokusatsu (Kamen Rider vibes) (Haru). Bright tech-blue, chrome silver,
   a slight yellow secondary accent, echoing the paneled chest-armor look.
   --butter alone (8 call sites sitewide) barely shows up, so yellow is also
   carried by --lavender (163 combined call sites w/ --coral/--mint: filter
   buttons, chips, code snippets, secondary hovers) so it actually reads as
   present, while --mint stays chrome silver instead of being sacrificed to it.
   --lavender drives *text color* at most of those sites though, so the
   original full-saturation #ffd23f was straight yellow text on blue
   everywhere — too loud. Muted it toward a soft gold so it still reads as
   "yellow accent" without shouting as body text.
   Background is tokusatsu "speed lines" — the radiating diagonal-motion-line
   burst from action-panel effects — instead of a flat grid, which read as a
   dull test pattern. Fixed corner, low alpha, so it's a mood cue, not noise. */
[data-theme="haru"] {
  --ink:#04101c; --bg:#0a2c4a; --surface:#0f3c63; --surface-2:#164c7c;
  --border:#2f74a8; --coral:#1fc8e8; --mint:#c7ccd4; --butter:#ffd23f;
  --lavender:#d9b869; --cheek:#8fe0ff; --text:#f0f8ff; --muted:#8fb6d4;
  --topbar-bg:rgba(10,44,74,0.85);
  /* speed-line burst radiating from the top-right, plus a faint chrome sheen
     band. conic-gradient makes the radiating wedges; repeating it gives evenly
     spaced "lines" instead of a smooth fade. */
  --speedlines:
    repeating-conic-gradient(from 0deg at 100% 0%,
      rgba(127,216,238,0.09) 0deg 1.2deg, transparent 1.2deg 6deg),
    linear-gradient(200deg, rgba(199,204,212,0.07), transparent 45%);
}
[data-theme="haru"] body {
  background:
    var(--speedlines),
    var(--bg);
}

* { box-sizing: border-box; }
body {
  margin: 0;
  background:
    radial-gradient(1200px 500px at 80% -10%, rgba(179,156,255,0.10), transparent 60%),
    radial-gradient(1000px 500px at 0% 0%, rgba(73,214,192,0.07), transparent 55%),
    var(--bg);
  color: var(--text);
  font-family: 'Inter', system-ui, "Hiragino Sans", "Noto Sans JP", sans-serif;
  /* sticky footer: body is a min-height-viewport flex column, and the footer
     takes margin-top:auto (below), so on short pages (e.g. an untrusted user's
     view-only People page) the footer sinks to the bottom of the screen instead
     of riding up under the content. Tall pages scroll normally. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.hidden { display: none !important; }
a { color: inherit; }

/* ---------- top bar ---------- */
.topbar {
  display: flex; align-items: center; gap: 20px;
  padding: 14px 26px 22px;
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
  background: var(--topbar-bg);
  backdrop-filter: blur(10px);
}

/* Understated app-wide loading indicator: a thin accent bar riding the bottom
   edge of the topbar. A small sliver slides across (indeterminate — we don't
   know the load duration). Hidden by default; JS adds .is-loading only for
   loads slow enough to notice, and never more than one at a time. */
.loading-bar {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  overflow: hidden; pointer-events: none;
  opacity: 0; transition: opacity 120ms ease;
}
.loading-bar.is-loading { opacity: 1; }
.loading-bar::before {
  content: ""; position: absolute; top: 0; height: 100%;
  width: 34%; left: -34%;
  background: var(--coral, #ff5d8f);
  border-radius: 2px;
}
.loading-bar.is-loading::before { animation: loading-slide 1.05s ease-in-out infinite; }
@keyframes loading-slide {
  0%   { left: -34%; }
  60%  { left: 100%; }
  100% { left: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  /* No traveling sliver — a static, gently pulsing full-width bar instead. */
  .loading-bar::before { width: 100%; left: 0; animation: none !important; }
  .loading-bar.is-loading::before { animation: loading-pulse 1.2s ease-in-out infinite !important; }
  @keyframes loading-pulse { 0%,100% { opacity: 0.35; } 50% { opacity: 0.9; } }
}

/* While a view is loading, hold the page tall so the not-yet-populated sections
   don't collapse and float the footer up under the topbar. The min-height is
   released the instant the load settles (see _hide/_showLoadingBar). Views sit
   directly in <body> with the footer as the last sibling, so reserving height
   on the body keeps the footer parked at the bottom of the viewport. */
body.app-loading { min-height: 100vh; }
/* Don't show the footer over empty content — it reappears once sections paint. */
body.app-loading .site-footer { visibility: hidden; }
/* Browse (accent) leads the right cluster: it takes the auto margin so it +
   the user nav sit together on the right, Browse next to the greeting. */
.nav-right-lead { margin-left: auto; }
/* logged-in user + logout, sitting just after Browse on the right */
.user-nav { display: inline-flex; align-items: center; gap: 8px; }
.user-greeting { font-size: 14px; color: var(--muted); white-space: nowrap; }
/* LOCAL DEV ONLY: simulated-role switcher (rendered only under the
   RINGSIDE_NO_AUTH bypass). Intentionally understated/muted so it reads as a
   dev affordance, not a normal control. */
.dev-role-switcher {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--muted); white-space: nowrap;
  padding: 3px 8px; border: 1px dashed var(--border, #ccc); border-radius: 8px;
  opacity: 0.72;
}
.dev-role-switcher:hover { opacity: 1; }
.dev-role-switcher label { text-transform: uppercase; letter-spacing: .04em; }
.dev-role-switcher select {
  font-size: 12px; color: inherit; background: transparent;
  border: none; cursor: pointer; padding: 1px 2px;
}
.brand {
  display: flex; align-items: center; gap: 11px;
  background: none; border: none; cursor: pointer; padding: 0; flex: 0 0 auto;
}
.brand-star { width: 64px; height: 64px; color: var(--coral); transition: transform 0.25s cubic-bezier(.5,1.6,.5,1); }
.brand:hover .brand-star { transform: rotate(-12deg) scale(1.08); }
.brand-text { display: flex; flex-direction: column; line-height: 1; align-items: flex-start; }
.brand-mark { font-family: var(--title-font, 'Fredoka'), "Noto Sans JP", sans-serif; font-weight: 600; font-size: 38px; color: var(--text); letter-spacing: 0.3px; }
.brand-sub { font-size: 10px; color: var(--muted); letter-spacing: 1.4px; text-transform: uppercase; margin-top: 3px; }

.search-wrap { flex: 1; display: flex; flex-direction: column; min-width: 0; position: relative; }
.search-row { position: relative; display: flex; gap: 8px; align-items: stretch; }
#search {
  flex: 1; min-width: 0; padding: 11px 18px;
  border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text);
  font-size: 14px; outline: none; font-family: inherit;
}
#search:focus { border-color: var(--coral); box-shadow: 0 0 0 3px color-mix(in srgb, var(--coral) 18%, transparent); }

/* Filters ▾ button + menu */
.filters-wrap { position: relative; flex: 0 0 auto; }
.filters-btn { height: 100%; padding: 0 14px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--muted); font-family: inherit; font-size: 12.5px;
  font-weight: 600; cursor: pointer; white-space: nowrap; }
.filters-btn:hover { border-color: var(--lavender); color: var(--lavender); }
.filters-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; min-width: 220px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); display: flex; flex-direction: column; }
.filter-opt { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: none; border: none; color: var(--text); font-family: inherit; font-size: 13.5px;
  padding: 9px 15px; cursor: pointer; text-align: left; }
.filter-opt:hover { background: var(--surface); color: var(--mint); }
.filter-opt code { color: var(--lavender); font-size: 11.5px; background: var(--bg);
  padding: 1px 6px; border-radius: 6px; }

/* nav autocomplete dropdown */
.nav-suggest { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 25;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); }
.nav-suggest-opt { display: flex; align-items: center; gap: 8px; padding: 9px 16px; font-size: 14px;
  cursor: pointer; }
.nav-suggest-opt:hover, .nav-suggest-opt.active { background: var(--surface); color: var(--coral); }
.nav-suggest-opt .ns-kind { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--lavender); border: 1px solid var(--border); border-radius: 999px; padding: 1px 7px; }
.nav-suggest-opt .ns-sub { margin-left: auto; color: var(--muted); font-size: 12px; }

/* refreshable suggestion chips (replaces the old hint text)
   Absolutely positioned so they hang *below* the search row without adding
   height to .search-wrap — otherwise the taller two-row column throws the
   topbar's vertical centering off and the input sits above the buttons. */
/* one line only: long random chips used to wrap to a 2nd row and overlap the
   content below (the bar is absolutely positioned). nowrap + clip keeps it a
   single tidy row; the shuffle button re-rolls if a name got cut. */
.search-chips { position: absolute; top: 100%; left: 12px; right: 0;
  display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; margin: 6px 0 0;
  overflow: hidden; }
.search-chips .search-chip { flex: 0 0 auto; }
.search-chips .chips-shuffle { flex: 0 0 auto; margin-right: auto; }
.chips-try { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.6px; }
.search-chip { font-family: inherit; font-size: 11.5px; padding: 3px 10px; border-radius: 999px;
  background: var(--surface-2); color: var(--lavender); border: 1px solid var(--border); cursor: pointer; }
.search-chip:hover { border-color: var(--lavender); color: var(--text); }
.chips-shuffle { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 13px;
  padding: 0 4px; }
.chips-shuffle:hover { color: var(--coral); }

/* ---------- buttons ---------- */
.btn-accent, .btn-ghost {
  font-family: inherit; font-size: 13.5px; font-weight: 600;
  padding: 9px 18px; border-radius: 999px; cursor: pointer;
  border: 1px solid transparent; text-decoration: none;
  transition: transform 0.12s ease, box-shadow 0.15s ease, background 0.15s;
  white-space: nowrap;
}
/* color-mix() derives a translucent --coral shadow with no separate rgb-triplet
   variable to keep in sync per theme — was hardcoded to the original pink
   (rgba(255,93,143,...)), so every theme's accent button glowed pink
   regardless of its actual accent color. */
.btn-accent { background: var(--coral); color: var(--ink); box-shadow: 0 4px 0 color-mix(in srgb, var(--coral) 25%, transparent); }
.btn-accent:hover { transform: translateY(-1px); box-shadow: 0 6px 14px color-mix(in srgb, var(--coral) 35%, transparent); }
.btn-accent:active { transform: translateY(1px); box-shadow: 0 2px 0 color-mix(in srgb, var(--coral) 25%, transparent); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border); }
.btn-ghost:hover { border-color: var(--coral); color: var(--coral); }

/* ---------- theme picker ---------- */
.theme-wrap { position: relative; flex: 0 0 auto; }
/* shared circular icon-button look: theme picker + slimmed topbar actions */
.theme-btn, .btn-icon { display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: 0 0 auto; padding: 0;
  border-radius: 999px; border: 1px solid var(--border); background: var(--surface);
  color: var(--lavender); cursor: pointer; transition: color .15s, border-color .15s, transform .12s; }
.theme-btn:hover, .btn-icon:hover { color: var(--coral); border-color: var(--coral); transform: translateY(-1px); }
.theme-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; min-width: 216px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); display: flex; flex-direction: column; padding: 5px; }
.theme-opt { display: flex; align-items: center; gap: 11px; background: none; border: none;
  color: var(--text); font-family: inherit; font-size: 13.5px; font-weight: 500;
  padding: 9px 11px; cursor: pointer; text-align: left; border-radius: 10px; }
.theme-opt:hover { background: var(--surface); }
.theme-opt.active { background: var(--surface); }
.theme-opt.active .theme-opt-name { color: var(--coral); }
.theme-swatch { flex: 0 0 auto; display: flex; border-radius: 999px; overflow: hidden;
  width: 46px; height: 18px; border: 1px solid var(--border); }
.theme-swatch span { flex: 1; }
.theme-opt-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.theme-opt-name { font-weight: 600; }
.theme-opt-sub { display: none; font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.theme-opt .theme-check { margin-left: auto; color: var(--coral); font-size: 13px; opacity: 0; }
.theme-opt.active .theme-check { opacity: 1; }

/* ---------- admin overflow menu ---------- */
.admin-wrap { position: relative; flex: 0 0 auto; }
.admin-menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; min-width: 210px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 12px 28px rgba(0,0,0,0.45); display: flex; flex-direction: column; padding: 5px; }
.admin-item { display: flex; align-items: center; gap: 10px; background: none; border: none;
  color: var(--text); font-family: inherit; font-size: 13.5px; font-weight: 500;
  padding: 9px 11px; cursor: pointer; text-align: left; border-radius: 10px; width: 100%; }
.admin-item:hover { background: var(--surface); color: var(--coral); }
.admin-sep { height: 1px; background: var(--border); margin: 5px 4px; }
.ai-label { flex: 1; }
.ai-badge { flex: 0 0 auto; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  background: var(--coral); color: var(--ink); font-size: 10.5px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; }
.ai-engine { flex: 0 0 auto; font-size: 10.5px; font-weight: 700; letter-spacing: 0.3px;
  padding: 2px 9px; border-radius: 999px; text-transform: lowercase; }
/* provider badge is now a menu row: drop the pill frame, keep the color accent on
   its engine chip only. */
.provider-badge { font-size: inherit; padding: 9px 11px; border-radius: 10px; font-weight: 500;
  border: none; cursor: pointer; letter-spacing: 0; background: none; color: var(--text); }
.provider-badge.local .ai-engine { color: var(--mint); border: 1px solid color-mix(in srgb, var(--mint) 40%, transparent); background: color-mix(in srgb, var(--mint) 10%, transparent); }
.provider-badge.api   .ai-engine { color: var(--butter); border: 1px solid color-mix(in srgb, var(--butter) 40%, transparent); background: color-mix(in srgb, var(--butter) 10%, transparent); }

/* ---------- landing ---------- */
.landing {
  max-width: 640px; margin: 0 auto; padding: 8vh 24px 6vh;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.hero-star {
  width: 128px; height: 128px; color: var(--coral);
  filter: drop-shadow(0 10px 24px color-mix(in srgb, var(--coral) 35%, transparent));
  animation: bob 3.2s ease-in-out infinite;
}
@keyframes bob { 0%,100% { transform: translateY(0) rotate(-3deg); } 50% { transform: translateY(-10px) rotate(3deg); } }
.hero-title { font-family: var(--title-font, 'Fredoka'), "Noto Sans JP", sans-serif; font-weight: 700; font-size: 62px; margin: 14px 0 6px; letter-spacing: 0.5px; }
.hero-tagline { color: var(--muted); font-size: 16px; margin: 0 0 26px; max-width: 440px; line-height: 1.5; }
.hero-search-wrap { width: 100%; max-width: 480px; position: relative; }
#heroSearch {
  width: 100%; padding: 15px 22px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-size: 16px; outline: none; font-family: inherit; text-align: center;
}
#heroSearch:focus { border-color: var(--coral); box-shadow: 0 0 0 4px color-mix(in srgb, var(--coral) 18%, transparent); }
.hero-suggest { position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 15;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 14px 30px rgba(0,0,0,0.45); text-align: left; }
.hero-suggest-opt { padding: 11px 18px; font-size: 15px; cursor: pointer; display: flex;
  align-items: center; justify-content: space-between; gap: 10px; }
.hero-suggest-opt:hover, .hero-suggest-opt.active { background: var(--surface); color: var(--coral); }
.hero-suggest-opt .hs-sub { color: var(--muted); font-size: 12px; }
/* min-height reserves one chip-row (chip = ~33px: 13px text + 7px*2 pad + 1px
   border*2) so the empty container holds its space and the layout doesn't shift
   when loadHeroSuggestions() fills it in. */
.hero-chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  margin: 16px 0 30px; min-height: 33px; }
.ex-chip {
  font-family: inherit; font-size: 13px; padding: 7px 14px; border-radius: 999px;
  background: var(--surface-2); color: var(--lavender); border: 1px solid var(--border); cursor: pointer;
}
.ex-chip:hover { border-color: var(--lavender); transform: translateY(-1px); }
.hero-actions { display: flex; gap: 12px; }
.hero-stat { color: var(--muted); font-size: 13px; margin-top: 28px; }
.hero-stat b { color: var(--mint); }

/* ---------- gallery ---------- */
.browserbar { display: flex; align-items: flex-start; gap: 16px; padding: 18px 26px 4px; }
.tagbar { display: flex; flex-wrap: wrap; gap: 8px; flex: 1; min-width: 0; }
.sort-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; flex: none; }
.sort-row { display: flex; align-items: center; gap: 8px; }
.result-count { font-size: 11.5px; color: var(--muted); font-weight: 600;
  letter-spacing: 0.4px; white-space: nowrap; }
.sort-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.8px;
  color: var(--muted); font-weight: 600; }
.sort-select { font-family: inherit; font-size: 12.5px; color: var(--text);
  background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
  padding: 6px 12px; cursor: pointer; outline: none; }
.sort-select:hover { border-color: var(--coral); }
.sort-select:focus { border-color: var(--coral); box-shadow: 0 0 0 3px color-mix(in srgb, var(--coral) 18%, transparent); }

/* Shared "Set type…" media-type dropdown -- used by BOTH the gallery select-mode
   bulk bar and the review toolbar so the two stay visually identical. Same pill
   look as .sort-select, sized to sit inline with the small pill buttons (btn-sm)
   in those bars. */
.type-select { font-family: inherit; font-size: 12.5px; color: var(--text);
  background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
  padding: 5px 12px; cursor: pointer; outline: none; flex: 0 0 auto; }
.type-select:hover { border-color: var(--coral); }
.type-select:focus { border-color: var(--coral); box-shadow: 0 0 0 3px color-mix(in srgb, var(--coral) 18%, transparent); }
.tag-chip {
  font-family: inherit; font-size: 12.5px; padding: 6px 12px; border-radius: 999px;
  background: var(--surface); color: var(--text); border: 1px solid var(--border); cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
}
.tag-chip:hover { border-color: var(--coral); }
.tag-chip .count { color: var(--muted); font-size: 11px; }

.gallery {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 16px; padding: 16px 26px 60px;
}
.photo-card {
  position: relative; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; cursor: pointer;
  transition: transform 0.14s ease, box-shadow 0.16s ease, border-color 0.16s;
}
/* Hover feedback WITHOUT moving the card's box. A translateY on hover physically
   shifts the card toward the cursor between mousedown and mouseup, so a click (or
   a select-mode toggle) can miss its own target. Instead: a stationary
   border+shadow cue, with the "alive" feel coming from a subtle inner IMAGE zoom
   (which never shifts the outer click target). Started as the review-card hover;
   promoted here so the whole gallery selects/clicks smoothly. */
.photo-card:hover { transform: none; border-color: var(--coral); box-shadow: 0 6px 18px rgba(0,0,0,0.3); }
/* object-position defaults to top so a portrait's head (near the top) is kept
   rather than sliced by an equal top/bottom crop. photoCard() overrides the
   vertical % per image from focus_y (the detected face center) when available.
   overflow:hidden on .photo-card clips the zoom so it never spills the frame. */
.photo-card img { width: 100%; display: block; aspect-ratio: 1/1; object-fit: cover;
  object-position: 50% 20%; background: var(--surface-2); transition: transform 0.16s ease; }
.photo-card:hover img { transform: scale(1.05); }

/* Contain cards (wide/tall utility media: screenshots, match cards, posters).
   The image is shown WHOLE (object-fit:contain) so nothing is sliced, and the
   letterbox gutters are filled by a blurred, over-scaled copy of the same thumb
   (--card-bg, set inline in photoCard). Keeps the grid square + uniform while a
   9:16 poster or 16:9 screenshot reads correctly instead of being center-cropped.
   The ::before backdrop sits behind the image (z-index) and doesn't zoom on hover,
   so only the crisp foreground image gets the subtle hover scale. */
.photo-card.contain-card { position: relative; }
/* Backdrop is confined to the SQUARE IMAGE AREA only (top: 0, aspect-ratio 1/1),
   NOT the whole card -- otherwise it bleeds over the .card-people row and the
   badges below the image. overflow:hidden on it clips the over-scaled blur. */
.photo-card.contain-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; z-index: 0;
  aspect-ratio: 1/1; overflow: hidden;
  background-image: var(--card-bg); background-size: cover; background-position: center;
  filter: blur(18px) brightness(0.85); transform: scale(1.15); /* scale hides blur edge bleed */
}
.photo-card.contain-card img { position: relative; z-index: 1;
  object-fit: contain; object-position: center; background: transparent; }
/* Badges (gif/art/seq/likely) and the people row must sit ABOVE the backdrop. */
.photo-card.contain-card .media-badge { z-index: 2; }
.photo-card.contain-card .card-people { position: relative; z-index: 2; }
/* Interactive overlays (select check + enlarge button) must sit above the
   contain-card's z-index:1 image, or the image intercepts their clicks. */
.photo-card.contain-card .review-check,
.photo-card.contain-card .review-view { z-index: 3; }
.card-people { padding: 8px 10px 10px; display: flex; flex-wrap: wrap; gap: 5px; }
.card-tags { padding: 6px 10px 10px; display: flex; flex-wrap: wrap; gap: 5px; }
.people-chip { background: var(--surface-2); border: 1px solid color-mix(in srgb, var(--coral) 45%, transparent); color: var(--coral);
  font-size: 10.5px; padding: 2px 8px; border-radius: 999px; font-weight: 600; }
/* "+N more people" counter chip: muted so it reads as a count, not a name. */
.people-chip.people-more { border-color: var(--border); color: var(--muted); cursor: default; }
/* Pending (guest) person, tagged on prod but not yet admin-confirmed: greyed +
   dashed so it reads as provisional, and a trailing "· pending" note. */
.people-chip.pending { border-style: dashed; border-color: var(--border);
  color: var(--muted); opacity: 0.75; }
.people-chip.pending::after { content: ' · pending'; font-weight: 500; opacity: 0.8; }
.mini-tag { background: var(--surface-2); color: var(--muted); font-size: 10.5px; padding: 2px 8px; border-radius: 999px; }

.media-badge { position: absolute; top: 9px; left: 9px; font-size: 10px; font-weight: 700;
  letter-spacing: 0.5px; padding: 3px 8px; border-radius: 999px; pointer-events: none; }
.media-badge.gif    { background: var(--butter); color: var(--ink); }
.media-badge.art    { background: var(--lavender); color: var(--ink); }
.media-badge.seq { left: auto; right: 9px; background: rgba(23,18,42,0.82); color: var(--text);
  border: 1px solid var(--border); }

/* ---------- empty ---------- */
.empty-state { text-align: center; padding: 12vh 20px; color: var(--muted); }
.empty-star { width: 70px; height: 70px; color: var(--surface-2); margin-bottom: 12px; }
.empty-title { font-family: 'Fredoka', sans-serif; font-size: 22px; color: var(--text); margin: 0 0 6px; }
.empty-sub { font-size: 14px; margin: 0; }

/* ---------- modals ---------- */
/* While any modal is open the body is pinned (fixed at its current scroll offset)
   so the page behind the backdrop can't scroll and the scroll position is exactly
   preserved on close -- JS sets top to -scrollY and restores it (see setBodyScrollLock). */
body.modal-open { position: fixed; width: 100%; overflow: hidden; }

.modal { position: fixed; inset: 0; background: rgba(12,8,24,0.72); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center; z-index: 50; padding: 24px; }
.modal-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  width: 100%; max-width: 460px; max-height: 88vh; overflow: auto; position: relative; }
.modal-card.modal-wide { max-width: 680px; }
.modal-head { display: flex; align-items: center; justify-content: space-between;
  padding: 18px 20px 8px; font-family: 'Fredoka', sans-serif; font-size: 19px; }
.modal-close { background: none; border: none; color: var(--muted); font-size: 26px; cursor: pointer; line-height: 1; }
.modal-close:hover { color: var(--coral); }
/* profile modal: anchor the × to the top-right corner (it was sitting in-flow,
   misaligned). z-index so it stays clickable above the header content. */
.modal-profile .modal-close { position: absolute; top: 10px; right: 14px; z-index: 3; }
.modal.locked .modal-close { display: none; }
.modal-body { padding: 8px 20px 22px; }
.field-label { display: block; font-size: 12px; text-transform: uppercase; letter-spacing: 0.8px;
  color: var(--muted); margin: 12px 0 8px; font-weight: 600; }
.label-opt { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--muted); opacity: 0.7; }
/* position:relative so an absolutely-positioned autocomplete menu inserted after
   an input (attachRosterAutocomplete) anchors to THIS row, not a distant ancestor.
   Without it the persona/merge name menus mis-positioned after the profile-edit
   scroll container was added. */
.row { display: flex; gap: 8px; position: relative; }
.row input { flex: 1; padding: 10px 14px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-family: inherit; font-size: 14px; outline: none; }
.row input:focus { border-color: var(--coral); }
.field-note { font-size: 12px; color: var(--muted); line-height: 1.6; margin: 10px 0 0; }
.field-note code { background: var(--bg); padding: 1px 6px; border-radius: 6px; color: var(--lavender); font-size: 11.5px; }
.source-link { color: var(--lavender); text-decoration: none; border-bottom: 1px solid transparent; }
.source-link:hover { border-bottom-color: var(--lavender); }
.source-ext { color: var(--muted); font-size: 10px; }
.scrape-limits { display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.scrape-limits label { display: flex; flex-direction: column; gap: 4px; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); font-weight: 600; flex: 1 1 96px; min-width: 96px; }
.scrape-limits input, .scrape-limits select { width: 100%; padding: 8px 12px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text); font-family: inherit;
  font-size: 13.5px; text-transform: none; letter-spacing: 0; font-weight: 400; outline: none; }
.scrape-limits input:focus { border-color: var(--coral);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--coral) 18%, transparent); }

/* Rounded-rect select that matches the text inputs it sits beside (the scrape/
   backfill limit rows + the recurring-source hint). Native-appearance reset with
   a chevron drawn in, so it doesn't render as a chunky OS control. Reused so the
   media-kind dropdowns look identical everywhere. */
.styled-select { appearance: none; -webkit-appearance: none; cursor: pointer;
  padding: 8px 28px 8px 12px; border-radius: 10px; border: 1px solid var(--border);
  background-color: var(--bg); color: var(--text); font-family: inherit; font-size: 13.5px;
  outline: none; background-repeat: no-repeat; background-position: right 10px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); }
.scrape-limits .styled-select { width: 100%; }
.styled-select:hover { border-color: var(--coral); }
.styled-select:focus { border-color: var(--coral);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--coral) 18%, transparent); }
.divider { text-align: center; color: var(--muted); font-size: 12px; margin: 18px 0 6px; position: relative; }
.divider::before, .divider::after { content: ""; position: absolute; top: 50%; width: 42%; height: 1px; background: var(--border); }
.divider::before { left: 0; } .divider::after { right: 0; }
.checkbox-row { display: flex; align-items: center; gap: 8px; font-size: 13.5px; margin: 10px 0; cursor: pointer; }
input[type="file"] { width: 100%; font-size: 13px; color: var(--muted); }
.add-status { font-size: 13px; color: var(--mint); margin-top: 12px; min-height: 18px; }
.meta-line { font-size: 12px; color: var(--muted); margin: 6px 0; }

/* ---------- dedicated upload modal ---------- */
.drop-zone { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 30px 20px; border: 2px dashed var(--border); border-radius: var(--radius);
  background: var(--bg); color: var(--muted); cursor: pointer; text-align: center;
  transition: border-color 0.15s, background 0.15s, color 0.15s; outline: none; }
.drop-zone:hover, .drop-zone:focus-visible { border-color: var(--coral); color: var(--coral); }
.drop-zone.dragover { border-color: var(--coral); background: rgba(255,122,140,0.08); color: var(--coral); }
.drop-zone-title { font-size: 14px; font-weight: 600; margin: 4px 0 0; color: var(--text); }
.drop-zone.dragover .drop-zone-title { color: var(--coral); }
.drop-zone-sub { font-size: 12px; margin: 0; }

.upload-preview { display: grid; grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
  gap: 8px; margin-top: 12px; max-height: 214px; overflow-y: auto; padding: 2px; }
.up-thumb { position: relative; aspect-ratio: 1/1; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border); background: var(--surface-2); }
.up-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.up-thumb-rm { position: absolute; top: 3px; right: 3px; width: 20px; height: 20px; border: none;
  border-radius: 50%; background: rgba(12,8,24,0.72); color: #fff; font-size: 14px; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.12s, background 0.12s; }
.up-thumb:hover .up-thumb-rm { opacity: 1; }
.up-thumb-rm:hover { background: var(--coral); color: var(--ink); }

.upload-actions { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 16px; }
.upload-actions .add-status { margin: 0; flex: 1 1 auto; }
.upload-actions .btn-accent { flex: 0 0 auto; }

/* scrape progress */
.scrape-progress { margin-top: 14px; padding: 13px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); }
.progress-head { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; font-size: 13px; }
.progress-head .btn-cancel { margin-left: auto; padding: 5px 14px; color: var(--coral); border-color: var(--coral); }
.spinner { width: 15px; height: 15px; border: 2.5px solid var(--border); border-top-color: var(--coral);
  border-radius: 50%; animation: spin 0.8s linear infinite; display: inline-block; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }
/* recluster loader: shown in place of the cluster grid while grouping runs */
.cluster-loading { display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 60px 20px; color: var(--muted); font-size: 15px; grid-column: 1 / -1; }
.cluster-loading .spinner { width: 22px; height: 22px; }
/* spinner-only variant (stats load): centered spinner, no label */
.cluster-loading.spinner-only { padding: 48px 20px; }
.cluster-loading.spinner-only .spinner { width: 26px; height: 26px; }

/* Manage people list (stats) */
.managed-people-list { display: flex; flex-direction: column; margin-top: 8px;
  border-radius: 7px; overflow: hidden; }
.managed-person { display: flex; align-items: center; gap: 10px; padding: 4px 10px;
  font-size: 13px; }
.managed-person:nth-child(odd) { background: var(--surface-2); }
.managed-person:hover { background: var(--surface); }
.managed-person .mp-name { font-weight: 600; text-decoration: none; }
.managed-person .mp-name:hover { text-decoration: none; color: var(--coral); }
.managed-person .mp-guest { font-size: 10px; }
.managed-person .mp-count { color: var(--muted); font-size: 12px; margin-left: auto; }
.managed-person .mp-del { color: var(--coral); border-color: transparent;
  padding: 2px 8px; font-size: 12px; }
.managed-person .mp-del:hover { border-color: var(--coral); }
.ss-filter { width: 100%; margin-top: 8px; padding: 7px 10px; border-radius: 7px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-size: 14px; box-sizing: border-box; }
.progress-bar { height: 7px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.progress-fill { height: 100%; width: 0; background: linear-gradient(90deg, var(--coral), var(--lavender));
  border-radius: 999px; transition: width 0.3s ease; }
.progress-fill.indeterminate { width: 40% !important; animation: slide 1.2s ease-in-out infinite; }
@keyframes slide { 0% { margin-left: -40%; } 100% { margin-left: 100%; } }
.progress-counts { font-size: 12px; color: var(--muted); margin-top: 7px; }

/* ---------- photo detail ---------- */
.modal-photo { max-width: 1240px; }
/* The photo modal owns its OWN two-column scrolling: the card itself does not
   scroll (that produced a 3rd, conflicting scrollbar). Instead the body is a
   fixed-height box and EACH column scrolls independently inside it, so the image
   side and the detail side no longer fight over one shared scroll (the dead-space
   / overscroll bug). When the columns wrap on a narrow screen (see below), we
   hand scrolling back to the card so it's one natural scroll again. */
.modal-photo { overflow: hidden; height: 88vh; display: flex; flex-direction: column; }
/* the close button is position:absolute (out of flow), so the body is the sole
   in-flow child and fills the card's height. NO flex-wrap here: wrapping breaks
   align-items:stretch height propagation, so the columns sized to content and the
   card (overflow:hidden) clipped everything with no scroll. The narrow-screen
   stack is handled explicitly by the media query below (flex-direction:column). */
.modal-photo-body { display: flex; gap: 0; position: relative;
  flex: 1; min-height: 0; align-items: stretch; }
.photo-stage { position: relative; flex: 3 1 560px; min-height: 320px; display: flex;
  align-items: center; justify-content: center; background: var(--bg);
  overflow-y: auto; }
/* single image: fit inside the stage (which is now the fixed body height) so it
   centers without nudging the stage into its own scroll. Stacked frames override
   this (they're full-width and scroll as a column). */
.photo-stage img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; cursor: zoom-in; }
@media (max-width: 880px) { .photo-stage img { max-height: 70vh; } }
/* #photoModalImg (the single-image case) needs a FIXED box, not max-width/
   max-height -- those only cap the size, they don't force it. With them alone
   the <img> renders at its own NATURAL resolution (clamped by the max), so a
   low-res thumbnail rendered small even though its aspect-ratio matched the
   eventual higher-res image -- then jumped larger once the bigger image
   loaded. width/height:100% (object-fit:contain still letterboxes correctly)
   makes the box always exactly the container's size regardless of which
   image -- thumb or display -- currently occupies it, so the thumb->display
   swap sharpens in place with truly zero size change. Doesn't touch stacked
   frames: .stack-frame img is more specific and already overrides height. */
#photoModalImg { width: 100%; height: 100%; }
@media (max-width: 880px) { #photoModalImg { height: 70vh; } }
/* stacked sequence frames: scroll through them, no per-frame nav */
.photo-stage:has(.stack-frame) { flex-direction: column; align-items: stretch; justify-content: flex-start; }
.stack-frame { position: relative; display: flex; align-items: center; justify-content: center;
  border-bottom: 2px solid var(--bg); }
.stack-frame:last-child { border-bottom: none; }
.stack-frame img { max-height: none; width: 100%; object-fit: contain; }
/* the frame that matched the search gets a subtle highlight ring */
.stack-frame.is-cover { box-shadow: inset 0 0 0 2px var(--coral); }
.stack-num { position: absolute; top: 8px; left: 8px; font-size: 11px; font-weight: 700;
  padding: 3px 9px; border-radius: 999px; background: rgba(23,18,42,0.75); color: var(--text);
  pointer-events: none; }
.stack-frame.is-cover .stack-num { background: var(--coral); color: var(--ink); }
/* per-frame tools on a stacked sequence frame (hover to reveal) */
.stack-tools { position: absolute; top: 6px; right: 6px; display: flex; gap: 6px;
  opacity: 0; transition: opacity 0.12s ease; }
.stack-frame:hover .stack-tools { opacity: 1; }
.stack-tool { border: none; border-radius: 999px; cursor: pointer; font-family: inherit;
  font-size: 11px; font-weight: 600; padding: 4px 10px; background: rgba(23,18,42,0.85);
  color: var(--text); }
.stack-tool:hover { background: var(--lavender); color: var(--ink); }
.stack-tool.danger:hover { background: var(--coral); color: var(--ink); }
.detail-danger { display: flex; align-items: center; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
.detail-danger .btn-danger { margin: 0; }

/* photo-modal labeling layer */
/* leave room on the right so the Label-people button never sits under the
   absolutely-positioned modal close (×) button */
.people-header { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; margin-bottom: 10px; padding-right: 44px; }
#labelToggle { flex: none; }
#labelToggle.active { background: var(--coral); color: var(--ink); border-color: var(--coral); }
.label-panel { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px;
  margin-bottom: 14px; background: var(--bg); }
/* one section per frame; multi-frame sequences get a divider + frame header */
.label-frame { display: flex; flex-direction: column; }
.label-frame.multi { border-top: 1px solid var(--border); padding-top: 12px; margin-top: 12px; }
.label-frame.multi:first-child { border-top: none; padding-top: 0; margin-top: 0; }
.label-frame-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
  font-size: 12px; font-weight: 600; color: var(--lavender); text-transform: uppercase; letter-spacing: 0.5px; }
.label-frame-thumb { width: 34px; height: 34px; border-radius: 8px; object-fit: cover;
  background: var(--surface-2); flex: none; }
.label-hint { font-size: 12px; color: var(--muted); margin: 0 0 10px; line-height: 1.5; }
.label-linked { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.label-person-chip { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px;
  padding: 3px 6px 3px 11px; border-radius: 999px; background: color-mix(in srgb, var(--coral) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--coral) 40%, transparent); color: var(--coral); }
.label-person-chip button { background: none; border: none; color: var(--muted); cursor: pointer;
  font-size: 15px; line-height: 1; padding: 0; }
.label-person-chip button:hover { color: var(--coral); }
/* source-suggested (unconfirmed) person chip: tentative dashed look, distinct
   from a solid confirmed chip. Confirm = coral ✓, remove = muted ×. */
.label-person-chip.likely { background: transparent; border-style: dashed;
  border-color: var(--border); color: var(--muted); }
.label-person-chip .likely-tag { font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); background: var(--surface-2);
  padding: 1px 5px; border-radius: 999px; }
.label-person-chip .chip-confirm { color: var(--coral); font-size: 13px; font-weight: 700; }
.label-person-chip .chip-confirm:hover { color: var(--text); }
/* "likely X" badge on a review card (bottom-left, tentative dashed) */
.media-badge.likely-badge { top: auto; bottom: 9px; left: 9px; right: auto;
  background: rgba(0,0,0,0.62); color: #ffd6a6; border: 1px dashed rgba(255,214,166,0.6);
  max-width: calc(100% - 18px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.label-faces { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; }
.label-face-row { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; }
.label-face-crop { width: 52px; height: 52px; border-radius: 10px; object-fit: cover;
  background: var(--surface-2); flex: none; }
.label-face-body { flex: 1; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.lf-named { font-size: 14px; font-weight: 600; }
.lf-mini { background: none; border: 1px solid var(--border); border-radius: 999px; color: var(--muted);
  font-family: inherit; font-size: 11px; padding: 3px 10px; cursor: pointer; }
.lf-mini:hover { border-color: var(--coral); color: var(--coral); }
.lf-suggest { background: color-mix(in srgb, var(--mint) 12%, transparent); border: 1px solid color-mix(in srgb, var(--mint) 45%, transparent);
  color: var(--mint); font-family: inherit; font-size: 12.5px; padding: 5px 12px; border-radius: 999px; cursor: pointer; }
.lf-suggest:hover { background: var(--mint); color: var(--ink); }
.lf-suggest b { font-weight: 700; }
.label-face-body .name-wrap { flex: 1 1 100%; min-width: 0; }
/* long suggestion names shouldn't force the row wider than the panel */
.lf-suggest { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.label-add-lbl { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--muted); font-weight: 600; margin: 4px 0 6px; }

/* prev / next: pinned to the viewport edges, OUTSIDE the modal card, so they
   never overlap the image or detail panel. #photoModal (.modal) has a
   backdrop-filter, which makes it the containing block for these fixed
   children — so they anchor to the modal overlay (full viewport). */
.photo-nav { position: fixed; top: 50%; transform: translateY(-50%); z-index: 60;
  width: 52px; height: 52px; border-radius: 50%; border: none; cursor: pointer;
  background: rgba(23,18,42,0.72); color: #fff; font-size: 30px; line-height: 1;
  display: flex; align-items: center; justify-content: center; padding: 0 0 3px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  transition: background 0.14s ease, opacity 0.14s ease; }
.photo-nav:hover { background: var(--coral); color: var(--ink); }
.photo-nav.prev { left: 20px; }
.photo-nav.next { right: 20px; }
.photo-nav:disabled { opacity: 0.25; cursor: default; background: rgba(23,18,42,0.6); color: #fff; }
/* on narrow screens where the card fills the width, tuck them tighter to the
   edge and shrink slightly so they still read as "outside" the content */
@media (max-width: 900px) {
  .photo-nav { width: 44px; height: 44px; }
  .photo-nav.prev { left: 6px; }
  .photo-nav.next { right: 6px; }
}
.photo-loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.photo-detail { flex: 1 1 300px; padding: 22px; min-width: 280px; box-sizing: border-box;
  overflow-x: hidden; overflow-y: auto; }
/* Narrow screens: STACK the two columns and hand scrolling back to the card as
   ONE natural scroll (independent-scroll columns make no sense stacked). Since we
   removed flex-wrap, stack explicitly with flex-direction:column. */
@media (max-width: 880px) {
  .modal-photo { height: auto; max-height: 88vh; overflow: auto; display: block; }
  .modal-photo-body { flex-direction: column; }
  .photo-stage, .photo-detail { overflow-y: visible; flex: 0 0 auto; }
  .photo-stage { max-height: 70vh; }   /* keep a single tall image from filling the viewport */
}
.detail-people { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }

/* sequence detail: overall header + one block per frame */
.seq-header { display: flex; flex-direction: column; gap: 10px; padding-bottom: 14px;
  margin-bottom: 4px; border-bottom: 1px solid var(--border); }
.seq-header #seqUngroupBtn { align-self: flex-start; }
.frame-block { padding: 16px 0; border-bottom: 1px solid var(--border); }
.frame-block:last-child { border-bottom: none; }
.frame-block-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.6px;
  color: var(--lavender); margin-bottom: 10px; }
.frame-block .photo-buttons { margin: 12px 0 8px; }
.frame-block .media-type-row { margin: 4px 0; }
.frame-block .row input { flex: 1; }
/* source caption = the real post text (twitter/IG). Primary, normal size. */
.photo-caption { font-size: 15px; line-height: 1.5; margin: 0 0 14px; white-space: pre-wrap; }
/* clamp long post text to ~3 lines with a show more/less toggle */
.photo-caption.clamped { display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 2px; }
.caption-toggle { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 12px; padding: 0; margin: 0 0 14px; }
.caption-toggle:hover { color: var(--fg); }
/* AI-generated scene description = secondary. Smaller, muted, sits above tags. */
.photo-desc { font-size: 12.5px; line-height: 1.5; color: var(--muted); margin: 0 0 12px; }

/* ---------- duplicate finder ---------- */
.dup-controls { display: flex; align-items: center; gap: 8px; margin: 4px 0 10px; }
.dup-results { display: flex; flex-direction: column; gap: 18px; }
.dup-cluster { border: 1px solid var(--border); border-radius: 10px; padding: 10px; }
.dup-cluster-head { font-size: 12px; color: var(--muted); margin: 0 0 8px; }
.dup-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.dup-item { width: 192px; display: flex; flex-direction: column; gap: 5px; margin-bottom: 6px; }
.dup-item img { width: 192px; height: 192px; object-fit: cover; border-radius: 8px;
  border: 1px solid var(--border); background: rgba(0,0,0,0.15); }
.dup-item.keep img { outline: 2px solid var(--accent, #49d6c0); outline-offset: 1px; }
.dup-item-meta { font-size: 11px; color: var(--muted); line-height: 1.35; }
.dup-item-actions { display: flex; gap: 6px; }
.dup-item-actions .btn-sm { font-size: 11px; padding: 2px 7px; }
.dup-keep-btn { width: 100%; font-size: 11px; padding: 3px 7px; white-space: normal; line-height: 1.25; }
.dup-item.removed { opacity: 0.4; pointer-events: none; }
.tag-editor { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.tag-pill { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; padding: 3px 6px 3px 11px;
  border-radius: 999px; border: 1px solid var(--border); }
.tag-pill.ai { background: color-mix(in srgb, var(--mint) 10%, transparent); border-color: color-mix(in srgb, var(--mint) 40%, transparent); }
.tag-pill.manual { background: color-mix(in srgb, var(--coral) 10%, transparent); border-color: color-mix(in srgb, var(--coral) 40%, transparent); }
.tag-pill button { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 15px; line-height: 1; padding: 0; }
.tag-pill button:hover { color: var(--coral); }
.photo-buttons { display: flex; gap: 10px; margin: 16px 0 6px; }
.media-type-row { display: flex; align-items: center; gap: 10px; margin: 14px 0 4px; }
.media-type-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.8px;
  color: var(--muted); font-weight: 600; }
.media-type-select { font-family: inherit; font-size: 13px; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 999px;
  padding: 7px 14px; cursor: pointer; outline: none; }
.media-type-select:hover, .media-type-select:focus { border-color: var(--coral); }
.btn-danger { font-family: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  margin-top: 16px; padding: 8px 16px; border-radius: 999px; background: transparent;
  color: var(--coral); border: 1px solid color-mix(in srgb, var(--coral) 45%, transparent); }
.btn-danger:hover { background: var(--coral); color: var(--ink); border-color: var(--coral); }
.btn-danger:disabled { opacity: 0.6; cursor: default; }

/* ---------- tagging progress toast ---------- */
/* back-to-top button (bottom-right, high-contrast accent, chunky arrow) */
.scroll-top { position: fixed; right: 22px; bottom: 22px; z-index: 55;
  width: 50px; height: 50px; border-radius: 50%; border: none;
  background: var(--coral); color: var(--ink); font-size: 18px; font-weight: 900;
  line-height: 1; cursor: pointer; box-shadow: 0 6px 20px rgba(0,0,0,0.45);
  display: flex; align-items: center; justify-content: center; padding: 0 0 1px;
  transition: transform 0.15s ease, box-shadow 0.15s ease; }
.scroll-top:hover { transform: translateY(-3px); box-shadow: 0 10px 26px rgba(0,0,0,0.55); }
/* when the tagging toast is visible (bottom-right), lift the button above it */
body:has(#tagToast:not(.hidden)) .scroll-top { bottom: calc(22px + 96px); }

.tag-toast { position: fixed; right: 22px; bottom: 22px; z-index: 60; width: 320px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; box-shadow: 0 12px 30px rgba(0,0,0,0.45);
  animation: toast-in 0.25s cubic-bezier(.5,1.5,.5,1); }
@keyframes toast-in { from { transform: translateY(12px); opacity: 0; } to { transform: none; opacity: 1; } }
.tag-toast .toast-head { display: flex; align-items: center; gap: 10px; font-size: 14px;
  font-weight: 600; margin-bottom: 10px; }
.tag-toast .toast-cancel { margin-left: auto; background: none; border: none; color: var(--muted);
  font-size: 22px; line-height: 1; cursor: pointer; padding: 0 2px; }
.tag-toast .toast-cancel:hover { color: var(--coral); }
.tag-toast .toast-sub { font-size: 12px; color: var(--muted); margin-top: 7px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tag-toast.done .spinner { display: none; }

/* Local-only persistent indicator for background scrape/sync activity.
   Bottom-center so it never competes with the tag toast (right) or the
   theme-song player (left) for the same corner. */
.bg-activity-toast { position: fixed; left: 50%; bottom: 22px; z-index: 59;
  transform: translateX(-50%); display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 999px;
  padding: 9px 18px; box-shadow: 0 12px 30px rgba(0,0,0,0.45);
  font-size: 13px; font-weight: 600; color: var(--text);
  animation: toast-in 0.25s cubic-bezier(.5,1.5,.5,1); }
.bg-activity-toast.hidden { display: none; }

/* ---------- persistent theme-song player (bottom bar) ---------- */
.song-player { position: fixed; left: 22px; bottom: 22px; z-index: 62;
  display: flex; align-items: center; gap: 12px; width: 340px; max-width: calc(100vw - 44px);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 10px 14px; box-shadow: 0 12px 30px rgba(0,0,0,0.45);
  animation: toast-in 0.25s cubic-bezier(.5,1.5,.5,1); }
.song-pp { flex: none; width: 34px; height: 34px; border-radius: 50%; border: none;
  background: var(--coral); color: var(--ink); font-size: 13px; cursor: pointer;
  display: flex; align-items: center; justify-content: center; }
.song-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.song-title { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.song-bar { height: 4px; border-radius: 3px; background: var(--border); overflow: hidden; }
.song-progress { height: 100%; width: 0; background: linear-gradient(90deg, var(--coral), var(--lavender)); }
.song-time { flex: none; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.song-close { flex: none; background: none; border: none; color: var(--muted); font-size: 20px;
  line-height: 1; cursor: pointer; padding: 0 2px; }
.song-close:hover { color: var(--coral); }

/* ---------- theme-song controls (in profile / team editors + views) ---------- */
.song-play-btn { background: var(--surface-2); border: 1px solid color-mix(in srgb, var(--coral) 35%, transparent);
  color: var(--text); font-family: inherit; font-size: 13px; padding: 7px 14px;
  border-radius: 999px; cursor: pointer; margin-top: 4px; }
.song-play-btn:hover { border-color: var(--coral); color: var(--coral); }
.theme-song-edit { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 8px; }
.song-current { font-size: 12.5px; color: var(--muted); display: flex; align-items: center;
  gap: 10px; flex-wrap: wrap; }
.song-current span { overflow: hidden; text-overflow: ellipsis; }
.song-current.song-none { font-style: italic; }
.song-upload-lbl { font-size: 12px; color: var(--muted); display: flex; flex-direction: column; gap: 5px; }

/* ---------- theme-song embed modal ---------- */
.modal-embed { max-width: 720px; width: 92vw; }
.embed-frame { position: relative; width: 100%; padding-top: 56.25%; border-radius: var(--radius);
  overflow: hidden; background: #000; }
.embed-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ---------- belt indicators + manager ---------- */
.belt-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0 2px; }
.belt-row.hidden { display: none; }
/* Each belt sets --belt-accent (its title color); background/border/text are
   derived from it so a new belt only needs one color. Default gold if unset. */
.belt-badge { --belt-accent: #DAAF44; display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px 4px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--belt-accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--belt-accent) 60%, transparent);
  color: var(--belt-accent); font-size: 12.5px; font-weight: 600; }
/* 'iron' color sentinel: a brushed-metal gradient rather than a single tint. */
.belt-badge.belt-iron { color: #d8dde3;
  background: linear-gradient(135deg, rgba(120,130,140,0.28), rgba(70,76,84,0.28));
  border-color: rgba(176,186,196,0.55); }
.belt-icon { width: 18px; height: 18px; object-fit: contain; }
.belt-icon-ph { width: auto; font-size: 14px; display: inline-flex; }
.belt-label { line-height: 1; }
/* the SVG belt glyph: sized to its context, inherits color via currentColor */
.belt-svg { width: 1.15em; height: 1.15em; vertical-align: -0.22em; color: currentColor; }
#beltsBtn .belt-svg { color: var(--butter); }
.belt-icon-ph .belt-svg { width: 18px; height: 18px; }
.pc-belt { display: inline-flex; align-items: center; gap: 2px; padding: 0 7px;
  color: var(--butter); border-left: 1px solid color-mix(in srgb, var(--coral) 25%, transparent); }
.pc-belt .belt-svg { width: 14px; height: 14px; }
.team-chip .belt-svg { color: var(--butter); }

.people-head-actions { display: flex; gap: 10px; align-items: center; }
.belts-list { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.belt-manage-row { display: flex; align-items: center; gap: 12px; justify-content: space-between; }
.belt-manage-name { font-size: 14px; font-weight: 600; }
.belt-manage-row .sort-select { min-width: 200px; }

/* ---------- people / cluster review ---------- */
.people-head { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; padding: 26px 26px 6px; flex-wrap: wrap; }
.people-title { font-family: 'Fredoka', sans-serif; font-size: 30px; margin: 0 0 4px; }
.people-sub { color: var(--muted); font-size: 14px; margin: 0; }
.people-sub b { color: var(--mint); }
.recluster-note { margin: 10px 26px 0; padding: 11px 16px; border-radius: var(--radius);
  background: color-mix(in srgb, var(--mint) 10%, transparent); border: 1px solid color-mix(in srgb, var(--mint) 40%, transparent);
  color: var(--mint); font-size: 13px; display: flex; align-items: center; gap: 10px; }

/* named-so-far quick links */
.named-strip { display: flex; flex-direction: column; gap: 18px; padding: 4px 26px 0; }
.strip-group { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }
.strip-chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; flex: 1; }
/* collapsible guest/former groups: hide the chips until the icon is clicked */
.strip-group.collapsed .strip-chips { display: none; }
.strip-status-icon.status-guest, .strip-status-icon.status-former {
  cursor: pointer; font-family: inherit; }
.strip-toggle { background: none; border: none; color: var(--muted); cursor: pointer;
  font-family: inherit; font-size: 12px; text-transform: uppercase; letter-spacing: 0.6px;
  font-weight: 600; padding: 5px 2px; align-self: center; }
.strip-toggle:hover { color: var(--text); }
/* left controls column: status icon/toggle on top, sort toggle stacked beneath */
.strip-controls { flex: none; display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.strip-head { display: flex; align-items: center; gap: 4px; }
/* subtle, low-prominence sort toggle sitting under the status button */
.strip-sort { background: none; border: none; cursor: pointer; font-family: inherit;
  color: var(--muted); opacity: 0.55; padding: 2px 4px; border-radius: 6px;
  margin-left: 2px; display: inline-flex; align-items: center; }
.strip-sort:hover { opacity: 1; color: var(--text); background: var(--surface); }
.sort-svg { width: 16px; height: 16px; display: block; fill: none; }
/* only meaningful while the group is expanded -- hide it for collapsed guest/former */
.strip-group.collapsed .strip-sort { display: none; }
.named-strip .strip-label { font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.8px; color: var(--muted); font-weight: 600; margin-right: 2px;
  min-width: 96px; }
/* status group header as a compact icon (hover shows the label) */
.strip-status-icon { flex: none; width: 26px; height: 26px; display: inline-flex;
  align-items: center; justify-content: center; font-size: 15px; line-height: 1;
  border-radius: 50%; background: var(--surface); border: 1px solid var(--border);
  cursor: default; margin-right: 4px; }
/* the ★/✦/☆ glyphs don't share a metric baseline; nudge the stars so they center */
.strip-status-icon.status-active,
.strip-status-icon.status-former { padding-bottom: 1px; }
/* status icons: active=mint (on roster), guest=lavender (visiting -- neutral but
   distinct), former=muted hollow star (retired/faded) */
.strip-status-icon.status-active { color: var(--mint); border-color: color-mix(in srgb, var(--mint) 40%, transparent); }
.strip-status-icon.status-guest  { color: var(--lavender); border-color: color-mix(in srgb, var(--lavender) 35%, transparent); }
.strip-status-icon.status-former { color: var(--muted); }
.person-chip { position: relative; display: inline-flex; align-items: stretch; gap: 0;
  font-size: 13px; border-radius: 999px; background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--coral) 35%, transparent); color: var(--text); font-family: inherit;
  overflow: visible; }
.person-chip:hover { border-color: var(--coral); }
.pc-name { background: none; border: none; color: inherit; font-family: inherit;
  font-size: 13px; padding: 5px 8px 5px 12px; cursor: pointer; display: inline-flex;
  align-items: center; gap: 6px; border-radius: 999px 0 0 999px; }
.pc-name:hover { color: var(--coral); }
.person-chip .pc-count { color: var(--coral); font-weight: 700; font-size: 12px; }
/* roster-status pill (sits between name and role toggle) — an icon now */
.pc-tag { background: var(--surface-2); border: none; border-left: 1px solid var(--border);
  color: var(--muted); font-family: inherit; font-size: 13px; font-weight: 700;
  padding: 0 9px; cursor: pointer; }
.pc-tag:hover { background: var(--surface); }
.pc-tag.status-active { color: var(--mint); }
.pc-tag.status-guest  { color: var(--lavender); }   /* guest = visiting (neutral, distinct from former) */
.pc-tag.status-former { color: var(--muted); }
.pc-tag-menu, .pc-role-menu { position: absolute; top: calc(100% + 4px); right: 0; z-index: 8;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
  overflow: hidden; box-shadow: 0 8px 20px rgba(0,0,0,0.45); display: flex; flex-direction: column; }
.pc-tag-opt, .pc-role-opt { background: none; border: none; color: var(--text); font-family: inherit;
  font-size: 12.5px; padding: 7px 16px; cursor: pointer; text-align: left; white-space: nowrap; }
.pc-tag-opt:hover, .pc-role-opt:hover { background: var(--surface); color: var(--mint); }
.pc-tag-opt.active, .pc-role-opt.active { color: var(--coral); font-weight: 600; }

.pc-role { background: var(--surface-2); border: none; border-left: 1px solid var(--border);
  color: var(--muted); font-family: inherit; font-size: 11px; font-weight: 700; width: 24px;
  cursor: pointer; border-radius: 0 999px 999px 0; }
.pc-role:hover { color: var(--lavender); background: var(--surface); }
/* read-only status/role icons (non-admin chips): same look, no interactivity.
   They're <span>s (not <button>s), so give them the same flex-centering the
   buttons get implicitly -- otherwise the glyphs stick to the top/top-left. */
.pc-readonly { cursor: default; display: inline-flex; align-items: center;
  justify-content: center; }
.pc-tag.pc-readonly:hover, .pc-role.pc-readonly:hover { background: none; }
/* a flagged person's whole chip reads as de-emphasized in the listing */
.person-chip.low-priority { opacity: 0.6; }
.person-chip.low-priority:hover { opacity: 1; }
/* wrestler + ref colors swapped (wrestler now pops butter; ref is muted) */
.pc-role.role-wrestler { color: var(--butter); }
.pc-role.role-referee  { color: var(--muted); }
.pc-role.role-announcer { color: var(--mint); }
.pc-role.role-other    { color: var(--lavender); }

/* While the cluster data is still loading, hold back the whole cluster surface so
   the header + people/teams render first (no empty-region flash above them). */
#people.clusters-pending .cluster-controls,
#people.clusters-pending #reclusterNote,
#people.clusters-pending #clusterMetaRow,
#people.clusters-pending #clusterGrid,
#people.clusters-pending #clustersEmpty { display: none !important; }

/* grouping controls */
/* the cluster surface starts here (controls); nudge it down from the teams
   section above so the two areas read as distinct. */
.cluster-controls { display: flex; align-items: flex-end; gap: 28px; flex-wrap: wrap;
  padding: 16px 26px 4px; margin-top: 26px; }
/* size-lever + stat share one row, baseline-aligned. */
.cluster-meta-row { display: flex; align-items: flex-end; gap: 24px;
  flex-wrap: wrap; }
/* prod-only client-side pile-size lever (display toggled by no-recluster rules) */
.cluster-size-filter { flex-direction: column; gap: 6px; padding: 0 26px 4px;
  max-width: 340px; }
/* faces/groups stat -- shares .cluster-meta-row with the size lever. */
.cluster-faces-stat { margin: 0; padding: 0 26px; color: var(--muted);
  font-size: 13px; white-space: nowrap; }
.cluster-faces-stat b { color: var(--text); }
.control-group { display: flex; flex-direction: column; gap: 8px; min-width: 320px; }
.control-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.8px;
  color: var(--muted); font-weight: 600; display: flex; align-items: baseline; gap: 10px; }
.control-hint { text-transform: none; letter-spacing: 0; font-weight: 400; font-size: 11.5px; }
.slider-row { display: flex; align-items: center; gap: 12px; }
.slider-end { font-size: 11.5px; color: var(--muted); flex: none; }
#tightness { flex: 1; accent-color: var(--coral); cursor: pointer; }
.crowd-toggle { margin: 0 0 4px; font-size: 13px; }
/* segmented off/low/med/high control */
.seg-control { display: inline-flex; border: 1px solid var(--border); border-radius: 999px;
  overflow: hidden; background: var(--surface); width: fit-content; }
.seg-control button { background: none; border: none; color: var(--muted); font-family: inherit;
  font-size: 12.5px; font-weight: 600; padding: 7px 15px; cursor: pointer;
  border-right: 1px solid var(--border); }
.seg-control button:last-child { border-right: none; }
.seg-control button:hover { color: var(--text); }
.seg-control button.active { background: var(--coral); color: var(--ink); }

.cluster-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 18px; padding: 18px 26px 60px; }
.cluster-card { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 14px; display: flex; flex-direction: column;
  transition: opacity 0.3s ease, transform 0.3s ease; }
.cluster-card.leaving { opacity: 0; transform: scale(0.96); }
.cluster-card-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 10px; gap: 10px; }
.cluster-count { font-family: 'Fredoka', sans-serif; font-size: 17px; }
.cluster-count b { color: var(--coral); }
.cluster-head-actions { display: flex; align-items: center; gap: 12px; flex: none; }
.cluster-skip { background: none; border: none; color: var(--muted); font-size: 12px;
  cursor: pointer; padding: 2px 4px; }
.cluster-skip:hover { color: var(--coral); }
.cluster-expand { background: none; border: none; color: var(--lavender); font-size: 12px;
  cursor: pointer; padding: 2px 4px; font-family: inherit; white-space: nowrap; }
.cluster-expand:hover { color: var(--mint); }
.cluster-expand:disabled { color: var(--muted); cursor: default; }
.cluster-ignore-all { background: none; border: none; color: var(--coral); font-size: 12px;
  cursor: pointer; padding: 2px 4px; font-family: inherit; white-space: nowrap; }
.cluster-ignore-all:hover { text-decoration: underline; }
.cluster-ignore-all:disabled { color: var(--muted); cursor: default; text-decoration: none; }
.face-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-bottom: 12px; }
/* expanded: smaller tiles + scroll so a 40+ cluster stays inside the card */
.face-grid.expanded { grid-template-columns: repeat(5, 1fr); max-height: 340px;
  overflow-y: auto; padding-right: 4px; }
.face-cell { position: relative; border-radius: 10px; overflow: visible;
  transition: opacity 0.2s ease, transform 0.2s ease; }
.face-cell.removing { opacity: 0; transform: scale(0.85); }
.face-cell img { width: 100%; aspect-ratio: 1/1; object-fit: cover; border-radius: 10px;
  background: var(--surface-2); display: block; }
.face-x { position: absolute; top: 4px; right: 4px; width: 22px; height: 22px;
  border-radius: 50%; border: none; cursor: pointer; font-size: 16px; line-height: 1;
  background: rgba(23,18,42,0.72); color: #fff; opacity: 0; transition: opacity 0.12s ease;
  display: flex; align-items: center; justify-content: center; padding: 0; }
.face-cell:hover .face-x { opacity: 1; }
.face-x:hover { background: var(--coral); color: var(--ink); }
/* magnifier: top-LEFT so it doesn't collide with the ✕ */
.face-zoom { position: absolute; top: 4px; left: 4px; width: 22px; height: 22px;
  border-radius: 50%; border: none; cursor: pointer; font-size: 12px; line-height: 1;
  background: rgba(23,18,42,0.72); color: #fff; opacity: 0; transition: opacity 0.12s ease;
  display: flex; align-items: center; justify-content: center; padding: 0; }
.face-cell:hover .face-zoom { opacity: 1; }
.face-zoom:hover { background: var(--lavender); color: var(--ink); }
/* Fixed positioning (coords set by placeMenu in JS) so the menu escapes the
   expanded face-grid's overflow-y scroll clipping -- it was getting cut off at
   the container edge. z-index sits above the sticky bars. */
.face-menu { position: fixed; z-index: 60;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
  overflow: hidden; box-shadow: 0 8px 20px rgba(0,0,0,0.45); display: flex; flex-direction: column; }
.face-choice { background: none; border: none; color: var(--text); font-family: inherit;
  font-size: 12.5px; padding: 8px 14px; cursor: pointer; white-space: nowrap; text-align: left; }
.face-choice:hover { background: var(--surface); color: var(--mint); }
.face-choice.ignore:hover { color: var(--coral); }
.face-choice.name:hover { color: var(--lavender); }

/* inline single-face name picker, anchored over the cell */
.face-namer { position: absolute; top: 0; left: 0; z-index: 10; width: 240px;
  background: var(--surface); border: 1px solid var(--coral); border-radius: 12px;
  padding: 10px; box-shadow: 0 12px 28px rgba(0,0,0,0.5); }
.face-namer .name-wrap { display: flex; gap: 6px; }
.face-namer .name-input { font-size: 13px; padding: 8px 12px; }
.face-namer .name-save { padding: 8px 12px; font-size: 12.5px; }
.face-namer-cancel { background: none; border: none; color: var(--muted);
  font-size: 11.5px; cursor: pointer; margin-top: 6px; padding: 2px; }
.face-namer-cancel:hover { color: var(--coral); }

.name-wrap { position: relative; display: flex; gap: 8px; }
.name-input { flex: 1; min-width: 0; padding: 10px 14px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-family: inherit; font-size: 14px; outline: none; }
.name-input:focus { border-color: var(--coral); }
.name-save { flex: 0 0 auto; }
.name-menu { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 5;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  max-height: 240px; overflow: auto; box-shadow: 0 10px 24px rgba(0,0,0,0.4); }
/* fixed variant: positioned via JS from the input's rect so it escapes any
   overflow:auto scroll container (was being clipped inside the profile edit). */
.name-menu-fixed { position: fixed; left: 0; right: auto; top: 0; z-index: 80; }
.name-opt { padding: 9px 14px; font-size: 13.5px; cursor: pointer; display: flex;
  align-items: center; justify-content: space-between; gap: 8px; }
.name-opt:hover, .name-opt.active { background: var(--surface); }
.name-opt .opt-sub { color: var(--muted); font-size: 11.5px; margin-left: auto; }
/* alt-persona flag: distinguishes 'Hyper Misao' (alt) from a plain 'Misao' */
.name-opt .opt-persona { font-size: 10.5px; font-weight: 600; color: var(--lavender);
  background: rgba(184,148,255,0.14); border: 1px solid rgba(184,148,255,0.3);
  border-radius: 999px; padding: 1px 7px; white-space: nowrap; }
.name-opt .opt-pending { font-size: 10.5px; font-weight: 600; color: var(--muted);
  background: var(--surface-2); border: 1px dashed var(--border);
  border-radius: 999px; padding: 1px 7px; white-space: nowrap; }
.name-opt.create { color: var(--mint); }
.name-opt.create b { color: var(--text); }

/* tag autocomplete: relative wrapper so the .name-menu anchors to the input.
   inline-flex + the input flexing keeps the input's original width in its row. */
.tag-ac-wrap { position: relative; display: inline-flex; flex: 0 1 auto; min-width: 0; }
.tag-ac-wrap > input { width: 100%; }

/* ignored faces review */
.ignored-section { margin: 8px 26px 60px; border-top: 1px solid var(--border); padding-top: 14px; }
.ignored-toggle { background: none; border: none; color: var(--muted); font-family: inherit;
  font-size: 13px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center;
  gap: 8px; padding: 4px 0; }
.ignored-toggle:hover { color: var(--text); }
.ig-caret { transition: transform 0.15s ease; display: inline-block; }
.ignored-toggle.open .ig-caret { transform: rotate(90deg); }
.ig-count { background: var(--surface-2); color: var(--coral); font-weight: 700;
  border-radius: 999px; padding: 1px 9px; font-size: 12px; }
.ignored-hint { font-size: 12.5px; color: var(--muted); margin: 10px 0 12px; }
.ignored-grid { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); max-width: 900px; }
.ignored-grid .face-cell { cursor: pointer; }
.ignored-grid .face-cell::after { content: 'put back'; position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center; text-align: center;
  background: rgba(23,18,42,0.72); color: var(--mint); font-size: 11px; font-weight: 600;
  opacity: 0; transition: opacity 0.12s ease; border-radius: 10px; }
.ignored-grid .face-cell:hover::after { opacity: 1; }

/* ---------- person profile modal ---------- */
.modal-profile { max-width: 560px; }
/* pad the right so the corner-anchored close button never overlaps the Edit btn */
.modal-profile .modal-body { padding-top: 20px; }
.profile-head { display: flex; align-items: center; gap: 16px; padding-right: 30px; }
.profile-head-text { flex: 1 1 auto; min-width: 0; }
/* circular, face-cropped avatar left of the name */
.profile-avatar { flex: 0 0 auto; width: 84px; height: 84px; border-radius: 50%;
  overflow: hidden; background: var(--surface-2); border: 2px solid var(--border);
  display: flex; align-items: center; justify-content: center; }
.profile-avatar img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 40%; display: block; }
.avatar-initials { font-family: 'Fredoka', sans-serif; font-size: 28px; color: var(--muted); }
.profile-name { font-family: 'Fredoka', sans-serif; font-size: 26px; margin: 0 0 3px;
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
/* promotion badge(s) to the right of the name -- small text pills (placeholder
   for future per-promotion icons). */
.promo-badges { display: inline-flex; align-items: center; gap: 6px; }
.promo-badge { font-family: system-ui, sans-serif; font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.4px; text-transform: uppercase; line-height: 1;
  padding: 4px 8px; border-radius: 999px; color: var(--lavender);
  background: color-mix(in srgb, var(--lavender) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--lavender) 40%, transparent);
  white-space: nowrap; }
.profile-sub { color: var(--muted); font-size: 14px; margin: 0; }
.promo-former { color: var(--muted); }

/* promotions chip editor (profile edit) */
.pf-promos-block { margin-top: 16px; }
.pf-promos-head { display: flex; align-items: center; justify-content: space-between; }
.pf-manage-link { background: none; border: none; color: var(--lavender); cursor: pointer;
  font-size: 12px; text-decoration: underline; padding: 0; }
.pf-manage-link:hover { color: var(--coral); }
.pf-promo-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; min-height: 20px; }
.pf-promo-empty { color: var(--muted); font-size: 13px; }
.pf-promo-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 6px 4px 12px;
  border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border);
  font-size: 13px; }
.pf-promo-chip.former { opacity: 0.72; }
.pf-promo-name { font-weight: 600; }
.pf-promo-status { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.5px;
  padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--mint); cursor: pointer; }
.pf-promo-chip.former .pf-promo-status { color: var(--muted); }
.pf-promo-remove { background: none; border: none; color: var(--muted); cursor: pointer;
  font-size: 16px; line-height: 1; padding: 0 2px; }
.pf-promo-remove:hover { color: var(--coral); }
.pf-promo-add { margin-top: 4px; }

/* promotions manager modal */
.promo-manager-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.promo-manager-row { display: flex; align-items: center; gap: 8px; }
.promo-mgr-name { flex: 1 1 auto; min-width: 0; font-family: inherit; font-size: 14px;
  color: var(--text); background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 7px 10px; }
.promo-mgr-count { flex: 0 0 auto; color: var(--muted); font-size: 11.5px; white-space: nowrap; }
/* birthday: three compact inputs on one row. flex + min-width:0 so they SHARE the
   row and shrink to fit (width:100% each made them overflow and pushed the year
   input out of the modal -- the "year is inaccessible" bug). */
.pf-bday-row { display: flex; gap: 6px; width: 100%; }
.pf-bday-row input { flex: 1 1 0; min-width: 0; box-sizing: border-box; }   /* even thirds */
/* People modal: short single-value facts as compact tiles (label over value) in
   a responsive grid -- turns the old long uniform label:value list into a
   scannable stat block. Rich/multi-value facts still use .fact-row below. */
.profile-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 8px; margin: 18px 0 4px; }
.profile-stats:empty { display: none; }
.stat-tile { display: flex; flex-direction: column; gap: 2px; padding: 9px 12px;
  border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--border); }
.stat-k { color: var(--muted); font-size: 10.5px; text-transform: uppercase;
  letter-spacing: 0.6px; font-weight: 600; }
.stat-v { font-size: 15px; font-weight: 600; color: var(--text);
  overflow-wrap: anywhere; }

/* with the stat tiles above, the remaining fact-rows are the rich ones -- give
   them a touch more room and drop the redundant top border (tiles precede them). */
.profile-stats + .profile-facts { border-top: none; margin-top: 10px; }
.profile-facts { margin: 16px 0 6px; border-top: 1px solid var(--border); }
.fact-row { display: flex; gap: 14px; padding: 8px 0; border-bottom: 1px solid var(--border);
  font-size: 14px; }
.fact-k { flex: 0 0 120px; color: var(--muted); font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.6px; font-weight: 600; padding-top: 1px; }
.fact-v { flex: 1; }
.fact-v a { color: var(--lavender); text-decoration: none; }
.fact-v a:hover { color: var(--coral); }
/* social links (Links row): each is an inline row of its platform icon + handle,
   separated by a comfortable gap so X and Instagram read as distinct chips. */
.fact-v .social-link { display: inline-flex; align-items: center; gap: 5px; margin-right: 16px; }
.fact-v .social-link:last-child { margin-right: 0; }
.social-ic { width: 15px; height: 15px; vertical-align: -0.15em; }
/* tag-team names link to the team card */
.team-link { cursor: pointer; }
.fact-v a.team-inactive { color: var(--muted); }
/* team card: member avatar + name rows. Spaced out more than the people modal --
   the members ARE the team, so they get real visual weight (grid of face crops). */
.team-members { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px; margin: 20px 0 6px; }
.team-members.hidden { display: none; }
.team-member { display: flex; align-items: center; gap: 12px; text-align: left;
  padding: 8px 10px; border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--surface-2); cursor: pointer; color: var(--text);
  font: inherit; transition: border-color 0.12s, transform 0.12s; }
.team-member:hover { border-color: var(--coral); transform: translateY(-1px); }
.team-member-avatar { flex: 0 0 auto; width: 52px; height: 52px; border-radius: 50%;
  overflow: hidden; background: var(--surface); border: 2px solid var(--border);
  display: flex; align-items: center; justify-content: center; }
.team-member-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.team-member-avatar .avatar-initials { font-size: 20px; }
.team-member-name { flex: 1 1 auto; min-width: 0; font-weight: 600; font-size: 14px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.profile-bio { font-size: 14px; line-height: 1.55; color: var(--text); margin: 14px 0 4px; }
.profile-actions { display: flex; align-items: center; gap: 12px; margin-top: 18px; }

/* Edit mode: a scrollable field area with a PINNED Save/Cancel bar at the bottom,
   so Save is always visible and clearly separated from the fields (it used to be
   buried mid-form between the theme song and the persona/merge tools). */
#profileEdit { display: flex; flex-direction: column; min-height: 0; max-height: 74vh; }
.pf-scroll { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding-right: 6px; }
.pf-actions-sticky { position: sticky; bottom: 0; margin-top: 0;
  padding: 14px 0 4px; background: var(--surface);
  border-top: 1px solid var(--border); justify-content: flex-end; }
.pf-actions-sticky .btn-accent { padding: 10px 22px; }   /* make Save prominent */
/* section headers grouping the fields */
.pf-group-label { font-family: 'Fredoka', sans-serif; font-size: 14px; color: var(--lavender);
  margin: 16px 0 2px; }
.pf-scroll .pf-group-label:first-child { margin-top: 4px; }
/* advanced (persona / merge) collapsed by default */
.pf-advanced { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 6px; }
.pf-advanced > summary { cursor: pointer; font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.8px; color: var(--muted); font-weight: 600; padding: 8px 0; list-style: none; }
.pf-advanced > summary::-webkit-details-marker { display: none; }
.pf-advanced > summary::before { content: '▸ '; }
.pf-advanced[open] > summary::before { content: '▾ '; }

/* low-priority toggle (Advanced section): an unobtrusive pill that lights up
   lavender when active. Rare-use, so it stays quiet until pressed. */
.profile-priority { margin: 4px 0 14px; }
.pf-priority-toggle { display: inline-flex; align-items: center; gap: 7px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--muted);
  font-family: inherit; font-size: 13px; padding: 6px 12px; border-radius: 999px;
  cursor: pointer; }
.pf-priority-toggle:hover { color: var(--text); border-color: var(--lavender); }
.pf-priority-toggle.active { color: var(--lavender); border-color: var(--lavender);
  background: rgba(180,150,255,0.12); }
.pf-priority-icon { font-size: 15px; line-height: 1; }

.fetch-wrap { position: relative; }
/* opens UPWARD from the button: the fetch button sits low in the profile modal,
   and the modal card scrolls/clips, so a downward menu was hidden below the fold */
.fetch-menu { position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 8; min-width: 200px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden; box-shadow: 0 -10px 24px rgba(0,0,0,0.45); display: flex; flex-direction: column; }
.fetch-opt { background: none; border: none; color: var(--text); font-family: inherit;
  font-size: 13.5px; padding: 10px 16px; cursor: pointer; text-align: left; white-space: nowrap; }
.fetch-opt:hover { background: var(--surface); color: var(--mint); }
.profile-fetch-status { display: flex; align-items: center; gap: 10px; margin-top: 14px;
  padding: 11px 14px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg); font-size: 13px; }
.profile-fetch-status .btn-cancel { margin-left: auto; padding: 5px 12px; color: var(--coral);
  border-color: var(--coral); }

.profile-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; margin-top: 14px; }
.profile-form label, .profile-bio-label { display: flex; flex-direction: column; gap: 5px;
  font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted);
  font-weight: 600; }
.profile-form input, .profile-bio-label textarea { font-family: inherit; font-size: 14px;
  text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 9px 12px;
  outline: none; }
.profile-form input:focus, .profile-bio-label textarea:focus { border-color: var(--coral); }
.profile-bio-label { margin-top: 12px; }
.profile-bio-label textarea { resize: vertical; }

.profile-merge { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }

/* ---------- teams manager ---------- */
.teams-section { padding: 10px 26px 0; }
.teams-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.btn-sm { padding: 5px 12px; font-size: 12px; }
.teams-list { display: flex; flex-wrap: wrap; gap: 8px; }
.team-chip { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 7px 13px; cursor: pointer; font-family: inherit; color: var(--text); font-size: 13.5px; text-align: left; }
.team-chip:hover { border-color: var(--lavender); }
/* name + belt (right after the name) + nickname on one line; wraps and lets the
   card grow to fit a champion team rather than clipping the icon. */
.tc-title { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 4px;
  font-weight: 600; }
.tc-title .belt-svg { flex: 0 0 auto; }
.tc-members { color: var(--muted); font-size: 11px; }
/* inactive (disbanded) teams: still shown, greyed out */
.team-chip.inactive { opacity: 0.55; }
.tc-inactive-tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 1px 6px; }
.team-inactive { color: var(--muted); opacity: 0.75; }
.team-status-lbl { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--muted); margin-top: 10px; }
.team-status-lbl select { display: block; margin-top: 5px; font-family: inherit; font-size: 13.5px;
  color: var(--text); background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 7px 10px; }
.team-editor { margin-top: 12px; padding: 14px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--surface); max-width: 620px;
  display: flex; flex-direction: column; gap: 8px; }
.team-editor-row { display: flex; gap: 8px; }
.team-editor input { flex: 1; padding: 9px 13px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-family: inherit; font-size: 13.5px; outline: none; }
.team-editor input:focus { border-color: var(--coral); }
.team-editor-actions { display: flex; gap: 10px; margin-top: 4px; }
.team-editor-actions .btn-danger { margin: 0; margin-left: auto; }

/* ---------- post-import review screen ---------- */
.review-head { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; padding: 26px 26px 6px; flex-wrap: wrap; }
.review-title { font-family: 'Fredoka', sans-serif; font-size: 28px; margin: 0 0 3px; }
.review-sub { color: var(--muted); font-size: 14px; margin: 0; }
.review-sub b { color: var(--mint); }
/* Review toolbar: one wrapping bar of small, consistent controls, grouped by
   purpose (selection · bulk-edit · destructive). Groups are pill-shaped panels
   so the row reads as three tidy clusters rather than a scatter of controls. */
/* Sticky under the topbar so bulk actions stay reachable while scrolling a long
   run. top offset = measured topbar height (--topbar-h, set at runtime since the
   topbar height varies with wrapping/zoom). Opaque bg + blur so thumbs scroll
   cleanly underneath; z-index below the topbar (20) but above the grid cards. */
.review-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 12px 26px;
  position: sticky; top: calc(var(--topbar-h, 64px) - 1px); z-index: 15;
  background: var(--topbar-bg); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border); }
.rtb-group { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 5px 10px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); }
.rtb-actions { margin-left: auto; }        /* destructive cluster to the right */
.rtb-edit { flex: 1 1 auto; min-width: 0; }
/* compact styled checkbox rows (Select all / Scenery only) */
.rtb-check { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px;
  color: var(--text); cursor: pointer; white-space: nowrap; margin: 0; }
.rtb-check input { width: 15px; height: 15px; accent-color: var(--coral); cursor: pointer; }
.rtb-count { font-size: 12px; color: var(--muted); white-space: nowrap; min-width: 64px; }
.rtb-scenery { padding-left: 8px; border-left: 1px solid var(--border); }
.rtb-input { flex: 0 0 auto; width: 150px; padding: 5px 12px; border-radius: 999px; border-bottom-right-radius: 0; border-top-right-radius: 0;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  font-family: inherit; font-size: 12.5px; outline: none; }
.rtb-input:focus { border-color: var(--coral); }
/* The bulk-edit group is what flexes as the toolbar tightens; within it, only the
   "label people" field is allowed to shrink (it's over-long) -- the tag field and
   the type select keep their size. */
.rtb-input.rtb-grow { flex: 1 1 120px; width: auto; min-width: 90px; }
.rtb-type { flex: 0 0 auto; }
.rtb-group .btn-danger, .rtb-group .btn-accent, .rtb-group .btn-ghost { margin: 0; }

/* Input + its action button, joined into ONE connected pill so it reads as a
   field-with-a-submit rather than two loose controls. Used by both the review
   toolbar and the browse/search select bar (Tag / Label). The input's right side
   and the button's left side flatten and share a seam. */
.field-btn { display: inline-flex; align-items: stretch; }
.field-btn-grow { flex: 1 1 auto; min-width: 0; }
.field-btn > input,
.gallery-bulkbar .field-btn > input { border-top-right-radius: 0;
  border-bottom-right-radius: 0; border-right: none; }
.rtb-group .field-btn > button,
.gallery-bulkbar .field-btn > button { border-top-left-radius: 0; border-bottom-left-radius: 0;
  border: 1px solid var(--border); border-left: none;
  background: var(--surface-2); align-self: stretch; }
.rtb-group .field-btn > button:hover,
.gallery-bulkbar .field-btn > button:hover { background: var(--surface); border-color: var(--coral); color: var(--coral); }
/* keep the input's focus ring reading across the seam */
.field-btn > input:focus { border-color: var(--coral); position: relative; z-index: 1; }

/* ---------- gallery select-mode bulk bar (main grid) ----------
   The action bar only exists while Select is toggled on, so default browse stays
   clean. Mirrors the review toolbar's pill look for consistency. */
/* Sticky under the topbar (like the review toolbar). Deliberately does NOT
   include the browserbar (tags + sort) above it -- only the bulk action bar
   sticks, so the tag/sort area scrolls away normally. */
.gallery-bulkbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 26px;
  position: sticky; top: calc(var(--topbar-h, 64px) - 1px); z-index: 15;
  background: var(--topbar-bg); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border); }
.gallery-bulkbar .bulk-count { font-size: 12px; color: var(--muted);
  white-space: nowrap; min-width: 68px; font-weight: 600; }
.gallery-bulkbar .bulk-sep { width: 1px; align-self: stretch; margin: 2px 4px;
  background: var(--border); }
.gallery-bulkbar .bulk-tag-input { flex: 0 1 220px; min-width: 140px; padding: 5px 12px;
  border-radius: 999px; border-bottom-right-radius: 0; border-top-right-radius: 0; border: 1px solid var(--border); background: var(--bg);
  color: var(--text); font-family: inherit; font-size: 12.5px; outline: none; }
.gallery-bulkbar .bulk-tag-input:focus { border-color: var(--coral); }
/* in the joined Tag/Label pill the input carries the group's flex */
.gallery-bulkbar .field-btn { flex: 0 1 260px; min-width: 150px; }
.gallery-bulkbar .field-btn > .bulk-tag-input { flex: 1 1 auto; min-width: 0; }
.gallery-bulkbar .btn-danger { margin: 0; margin-left: auto; }   /* zero the base 16px top so it aligns with siblings; destructive stays right */

/* Select-mode cards: the base .photo-card hover (stationary box + image zoom) is
   already click-friendly; here we just surface the check + suppress the pointer
   cursor confusion. The .review-check overlay CSS (below) is shared. */
.gallery.select-mode .photo-card { cursor: pointer; user-select: none; -webkit-user-select: none; }
.gallery.select-mode .photo-card .review-check { display: flex; }
/* Select-mode card layout (matches the review screen's corners):
     top-left    = select check      (shared .review-check rule, z-index 3)
     top-right   = media/type badge  (gif/art/type -- shifted here off the check)
     bottom-left = "likely X" source guess
     bottom-right= enlarge (⛶) button, on hover
   The .card-people names row is hidden in select mode so nothing collides with
   the bottom-left likely badge -- names aren't needed while bulk-selecting. */
.gallery.select-mode .card-people { display: none; }
.gallery.select-mode .photo-card .media-badge:not(.seq):not(.likely-badge) {
  top: 9px; bottom: auto; left: auto; right: 9px; }
.gallery.select-mode .photo-card .media-badge.likely-badge {
  top: auto; bottom: 9px; left: 9px; right: auto; }
.gallery.select-mode .photo-card .select-view { display: block; }
.gallery.select-mode .photo-card:hover .select-view { opacity: 1; }
/* the enlarge button only exists in select mode; keep it hidden everywhere else
   (a plain gallery card has no use for it -- a click already opens the photo). */
.photo-card .select-view { display: none; }
/* "likely X" is only meaningful while confirming/rejecting source guesses, i.e.
   in select mode (and on review cards, which have their own always-on badge). On
   a plain browse gallery card it's noise, so hide it unless we're in select mode.
   .review-card is excluded so the review screen keeps showing it. */
.gallery:not(.select-mode) .photo-card:not(.review-card) .likely-badge { display: none; }
/* Hide the check only on PLAIN gallery cards outside select-mode. Review cards
   (.review-card) are always selectable, so they must be excluded here -- without
   the :not(.review-card) they matched this rule and the check vanished on the
   entire review / "just imported" screen. */
.gallery:not(.select-mode) .photo-card:not(.review-card) .review-check { display: none; }
.photo-card.selected { border-color: var(--coral); box-shadow: 0 0 0 2px var(--coral); }
.photo-card.selected .review-check { opacity: 1; background: var(--coral); color: var(--ink); border-color: var(--coral); }

.review-card { cursor: pointer; user-select: none; -webkit-user-select: none; }
/* the thumbnail must not be draggable/selectable -- a tiny pointer drag would
   start an image drag and swallow the click, another cause of "didn't register". */
.review-card img { -webkit-user-drag: none; user-select: none; pointer-events: none; }
/* The stationary-box hover + inner image zoom that makes cards smooth click
   targets now lives on the base .photo-card rule (promoted from here so the whole
   gallery benefits). Review cards inherit it; only the select-check + selected
   state below are review-specific. */
/* Shared by review cards and gallery select-mode cards. */
/* z-index keeps the select check ABOVE any media/type badge that shares the
   top-left corner -- otherwise a badge appended later in the DOM (match card,
   art, gif) paints over the check and it looks like the checkmark "disappeared"
   in select/review mode. Left-anchored badges are dropped to the bottom in
   select/review contexts (below) so they don't crowd the check either. */
.review-card .review-check, .photo-card .review-check { position: absolute; top: 8px; left: 8px;
  z-index: 3;
  width: 24px; height: 24px;
  border-radius: 50%; background: rgba(23,18,42,0.7); border: 2px solid #fff; color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 14px; opacity: 0.35;
  transition: opacity 0.12s, background 0.12s; }
/* On REVIEW cards the top-left corner belongs to the select check. Review never
   shows sequence/group cards, so the top-RIGHT corner is free -- park the type
   badge there. Bottom-left stays clear for the "likely X" badge (they used to
   both sit bottom-left and overlap); bottom-right is the ⛶ view button. */
.review-card .media-badge:not(.seq):not(.nobody):not(.likely-badge):not(.restored-badge) {
  top: 9px; bottom: auto; left: auto; right: 9px; }

.review-card.selected { border-color: var(--coral); box-shadow: 0 0 0 2px var(--coral); }
.review-card.selected .review-check { opacity: 1; background: var(--coral); color: var(--ink); border-color: var(--coral); }
.review-view { position: absolute; bottom: 8px; right: 8px; width: 26px; height: 26px; border: none;
  border-radius: 50%; background: rgba(23,18,42,0.7); color: #fff; cursor: pointer; font-size: 13px;
  opacity: 0; transition: opacity 0.12s; }
.review-card:hover .review-view { opacity: 1; }
.review-view:hover { background: var(--lavender); color: var(--ink); }
.media-badge.nobody { left: auto; right: 9px; background: var(--surface-2); color: var(--muted);
  border: 1px solid var(--border); }
/* resurrected-from-tombstone: bottom-right so it never collides with the select
   check (top-left) or the type/gif/art badges (bottom-left in review context). */
.media-badge.restored-badge { top: auto; bottom: 9px; left: auto; right: 9px;
  background: var(--butter); color: var(--ink); }

/* load more / infinite scroll */
.load-sentinel { height: 1px; }
.load-more { display: block; margin: 8px auto 50px; }
.load-more.hidden { display: none; }

/* ---------- accessibility ---------- */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
@media (max-width: 720px) {
  .search-chips { display: none; }
  .hero-title { font-size: 48px; }
  .brand-sub { display: none; }
}

/* photo modal close sits above the image */
.modal-photo .modal-close { position: absolute; top: 8px; right: 14px; z-index: 3;
  background: rgba(23,18,42,0.7); width: 34px; height: 34px; border-radius: 50%; }

/* ---------- scraping automation ---------- */
.auto-badge {
  position: absolute; top: -4px; right: -4px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 8px; background: var(--coral); color: var(--ink);
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
}
.btn-icon { position: relative; }
.modal-auto { max-width: 680px; }
.modal-auto .modal-body { max-height: 74vh; overflow-y: auto; }
.auto-status { font-size: 13px; color: var(--text); margin-bottom: 10px; min-height: 18px; }
.auto-section { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border); }
.auto-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.seed-wrap { display: flex; gap: 6px; }
.auto-add-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 10px 0; }
.auto-add-row input[type=text] { flex: 1; min-width: 160px; }
.src-label-in { flex: 0 1 140px !important; }
.src-interval { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.src-interval input { width: 52px; }

/* X account URL builder (fills the source URL field) */
.x-builder { display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  margin: 10px 0 2px; padding: 8px 10px; background: var(--surface-2);
  border: 1px dashed var(--border); border-radius: var(--radius); }
.x-builder-label { font-size: 12px; color: var(--muted); font-weight: 600; white-space: nowrap; }
.x-builder input[type=text] { flex: 1; min-width: 120px; }
.x-handle-in { flex: 0 1 150px !important; }
.x-terms-in { flex: 1 1 160px !important; }
.x-builder .sort-select { flex: 0 0 auto; }

.sources-list, .runs-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
/* cap the runs list so the modal can't grow unbounded as runs accumulate; it
   scrolls within this height. Display is also capped in JS (10 groups, 10 runs
   each) -- the full log stays in the DB. */
.runs-list { max-height: 46vh; overflow-y: auto; padding-right: 4px; }
.run-more-note { color: var(--muted); font-style: italic; }
.runs-show-more { align-self: flex-start; margin-top: 2px; }
.source-row, .run-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px 12px;
}
/* A source row now carries up to 4 action buttons; in a single flex row those
   squeezed the label/url/meta to nothing. Stack instead: info on its own full-
   width line, actions wrapping on the line below. (Run rows keep the side-by-side
   layout -- they only have one review control.) */
.source-row { flex-direction: column; align-items: stretch; }
.source-row.disabled { opacity: 0.55; }
.src-main, .run-main { min-width: 0; flex: 1; }
.src-label, .run-label { font-weight: 600; font-size: 13px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.src-url { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.src-meta, .run-meta { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.src-actions { display: flex; gap: 5px; flex-wrap: wrap; }
.src-status, .run-phase { padding: 1px 7px; border-radius: 8px; font-size: 10.5px; font-weight: 600;
  background: var(--bg); color: var(--muted); }
.src-status.ok, .run-phase.ok { color: #6fe0a0; }
.src-status.err, .run-phase.err { color: #ff7a90; }
.run-phase.run { color: var(--butter); }
.src-err { color: #ffb020; cursor: help; }
.src-nolimit { color: #ffb020; cursor: help; }
.src-next { color: var(--muted); cursor: help; }
.src-next.due { color: #6fe0a0; }   /* due now / imminent -> green */
.btn-del:hover { color: #ff7a90; }

.review-head-actions { display: flex; gap: 8px; align-items: center; }
.review-head-actions .btn-danger { margin-top: 0; }
.review-pager { display: flex; gap: 12px; align-items: center; justify-content: center;
  margin: 8px 0 4px; }
.pager-label { font-size: 12px; color: var(--muted); }

/* ---------- review: match-card badge, selected-actions, re-check ---------- */
.type-badge { background: rgba(0,0,0,0.72); color: #ffd23f; }
.recheck-status { font-size: 12px; color: var(--muted); white-space: nowrap; }

/* ---------- tag-pending menu ---------- */
/* tag-wrap lives inside the admin menu (which has overflow:hidden), so the
   sub-panel expands INLINE below the button rather than as an absolute flyout
   -- a flyout would be clipped by the parent's overflow. */
.tag-wrap { display: block; }
.tag-wrap > #tagBtn { width: 100%; }
.tag-menu {
  margin: 4px 2px 2px; padding: 10px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
  display: flex; flex-direction: column; gap: 8px;
}
.tag-menu-count { font-size: 13px; font-weight: 600; color: var(--text); }
.tag-menu-label { font-size: 12px; color: var(--muted); }
.tag-menu-row { display: flex; gap: 6px; }
.tag-menu-row input { flex: 1; }

/* ---------- global review-queue row ---------- */
.review-all-row { display: flex; align-items: center; gap: 12px; }
.pending-total { font-size: 13px; font-weight: 600; color: var(--text); }
.retag-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.retag-resume-row { display: flex; align-items: center; gap: 12px; margin-top: 8px; }
.retag-resume-row .field-note { margin: 0; }

/* one-off backfill URL box: was inheriting cramped browser defaults */
#backfillUrls { display: block; width: 100%; box-sizing: border-box; min-height: 84px;
  resize: vertical; padding: 10px 14px; border-radius: var(--radius);
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  font-family: inherit; font-size: 14px; line-height: 1.5; outline: none;
  margin-bottom: 10px; }
#backfillUrls:focus { border-color: var(--coral); }
#backfillUrls::placeholder { color: var(--muted); }

/* top-of-modal quick jump to the review queue */
.review-all-top { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px; padding: 10px 14px; border-radius: var(--radius);
  background: color-mix(in srgb, var(--coral) 10%, transparent); border: 1px solid color-mix(in srgb, var(--coral) 35%, transparent); }
.review-all-top-count { font-size: 13px; font-weight: 600; color: var(--text); }
.review-all-top .btn-sm { flex-shrink: 0; }

/* ---------- runs grouped by source + review dropdown ---------- */
.source-group {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 4px 4px 6px; margin-bottom: 8px;
}
.source-group .source-head {
  background: transparent; border: none; border-radius: 0;
  border-bottom: 1px solid var(--border); margin-bottom: 4px;
}
.run-subline {
  font-size: 11.5px; color: var(--muted); padding: 3px 12px; line-height: 1.5;
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
}
.run-subline:hover { color: var(--text); }

/* ---------- collapsible section headers (sources / recent runs) ---------- */
.collapse-head {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; color: inherit; text-transform: uppercase;
  letter-spacing: 0.8px; font-size: 12px; font-weight: 600;
}
.collapse-head .chev { width: 0.9em; height: 0.9em; opacity: 0.7; }
.collapse-head:hover .chev { opacity: 1; }
.collapse-head[aria-expanded="false"] .chev-down { transform: rotate(0deg); }  /* point right when collapsed */
.collapse-count { text-transform: none; letter-spacing: 0; font-weight: 500;
  font-size: 11px; color: var(--muted); }
.collapse-body.collapsed { display: none; }
.review-dd-wrap { position: relative; flex-shrink: 0; }
.review-dd-menu {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 70;
  min-width: 180px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 4px; box-shadow: 0 10px 28px rgba(0,0,0,0.35);
  display: flex; flex-direction: column; gap: 2px;
}
.review-dd-item {
  text-align: left; background: transparent; border: none; color: var(--text);
  font-size: 12px; padding: 7px 10px; border-radius: 8px; cursor: pointer;
}
.review-dd-item:hover { background: var(--surface-2); }

/* ---------- activity log ---------- */
.modal-head-tools { display: flex; align-items: center; gap: 12px; }
.log-link {
  background: none; border: none; color: var(--muted); font-size: 12px;
  cursor: pointer; padding: 2px 4px; text-decoration: underline;
}

/* ---------- app dialogs (confirm/alert/prompt replacement) ---------- */
.dialog-card { max-width: 420px; }
.dialog-msg { margin: 4px 0 0; color: var(--text); font-size: 14px; line-height: 1.5;
  white-space: pre-wrap; }   /* honor the \n line breaks the old prompts used */
.dialog-input { width: 100%; margin-top: 14px; padding: 9px 12px; font-family: inherit;
  font-size: 14px; color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--radius); }
.dialog-input:focus { outline: none; border-color: var(--coral); }
.dialog-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
/* a destructive confirm tints the OK button as a warning */
.dialog-card.danger .btn-accent { background: #ff4d6a; box-shadow: 0 4px 0 rgba(255,77,106,0.25); }
/* alert()-style single-button dialogs hide Cancel */
.dialog-card.alert-only .dialog-actions .btn-ghost { display: none; }

/* ---------- toasts (appToast) ---------- */
.toast-host { position: fixed; top: 16px; right: 16px; z-index: 90;
  display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
.app-toast { pointer-events: auto; min-width: 220px; max-width: 340px;
  background: var(--surface); border: 1px solid var(--border); border-left: 4px solid var(--mint);
  border-radius: var(--radius); padding: 12px 16px; color: var(--text); font-size: 13.5px;
  line-height: 1.45; box-shadow: 0 12px 28px rgba(0,0,0,0.4);
  animation: toast-in 0.22s ease; white-space: pre-wrap; }
.app-toast.error { border-left-color: #ff4d6a; }
.app-toast.leaving { animation: toast-out 0.2s ease forwards; }
@keyframes toast-in  { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateX(16px); } }
.log-link:hover { color: var(--text); }
#activityBackBtn { margin-bottom: 10px; }
.stat-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 6px;
}
.stat-grid > div {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 10px; text-align: center;
}
.stat-grid b { display: block; font-size: 18px; color: var(--text); }
.stat-grid span { font-size: 11px; color: var(--muted); }
.activity-err-line {
  display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); margin-bottom: 6px;
}
.err-badge {
  flex-shrink: 0; font-size: 10px; font-weight: 700; text-transform: uppercase;
  color: #ff7a90; background: var(--bg); padding: 2px 7px; border-radius: 8px; margin-top: 2px;
}
.err-body { min-width: 0; flex: 1; }
/* Rerun button on a source error sits at the right, aligned to the top */
.err-rerun { flex-shrink: 0; margin-top: 1px; }
.err-label { font-size: 12.5px; font-weight: 600; color: var(--text); }
.err-msg { font-size: 11.5px; color: var(--muted); word-break: break-word; }
.err-when { font-size: 10.5px; color: var(--muted); opacity: 0.7; margin-top: 2px; }
.failed-tags-grid { display: flex; flex-direction: column; gap: 8px; }
.failed-tag-card {
  display: flex; gap: 10px; align-items: center;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 8px;
}
.failed-tag-card img {
  width: 52px; height: 52px; object-fit: cover; border-radius: 8px;
  cursor: pointer; flex-shrink: 0;
}
.ft-info { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
.ft-name {
  font-size: 12px; color: var(--text); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.ft-err { font-size: 11px; color: #ff9aa8; word-break: break-word; }
.ft-info .btn-sm { align-self: flex-start; }

/* ---------- reusable SVG chevron ---------- */
.chev {
  width: 1em; height: 1em; flex-shrink: 0;
  vertical-align: -0.12em;               /* optical baseline align next to text */
  transition: transform 0.18s ease;
}
.chev-down  { transform: rotate(90deg); }
.chev-up    { transform: rotate(-90deg); }
.chev-left  { transform: rotate(180deg); }
/* right = base orientation, no rotation */

/* dropdown carets flip to point up when their menu is open */
.chev-toggle.open .chev-down { transform: rotate(-90deg); }

/* nav chevrons (prev/next, scroll-top) sit a touch larger for tap targets */
.photo-nav .chev, .scroll-top .chev { width: 1.4em; height: 1.4em; vertical-align: middle; }

/* ---------- chevron button layout ---------- */
/* text+chevron buttons: keep the glyph vertically centered next to the label */
.filters-btn, #profileFetchBtn, .review-dd-wrap .chev-toggle,
.review-pager .btn-sm, #reviewApproveBtn, #activityBackBtn {
  display: inline-flex; align-items: center; gap: 5px; justify-content: center;
}
/* icon-only nav buttons now hold an SVG, not a big glyph -- reset glyph padding */
.photo-nav { padding: 0 !important; }
.photo-nav .chev { width: 22px; height: 22px; }
.scroll-top { display: inline-flex; align-items: center; justify-content: center; }
.scroll-top .chev { width: 20px; height: 20px; }
.ig-caret .chev { width: 0.85em; height: 0.85em; }

/* ---------- automation status row (status + cancel button) ---------- */
.auto-status-row { display: flex; align-items: center; gap: 12px; justify-content: space-between; }

/* ---------- login gate overlay ----------
   Full-screen cover shown when there is no session. Uses the same palette
   variables as the rest of the UI so it re-skins with the active theme. */
.login-overlay {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
}
.login-overlay.hidden { display: none; }
.login-card {
  display: flex; flex-direction: column; gap: 14px;
  width: min(360px, 90vw);
  padding: 32px 28px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 40px rgba(0,0,0,0.35);
}
.login-brand {
  font-family: 'Fredoka', sans-serif; font-weight: 700; font-size: 30px;
  color: var(--coral); text-align: center; line-height: 1;
}
.login-sub {
  color: var(--muted); text-align: center; font-size: 13px; margin-top: -6px;
  margin-bottom: 4px;
}
.login-card input {
  padding: 12px 14px; font-size: 15px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
}
.login-card input:focus { outline: 2px solid var(--coral); outline-offset: 1px; }
.login-card button {
  padding: 12px 14px; font-size: 15px; font-weight: 600; cursor: pointer;
  background: var(--coral); color: var(--ink);
  border: none; border-radius: var(--radius);
}
.login-card button:disabled { opacity: 0.6; cursor: default; }
.login-error {
  color: var(--coral); font-size: 13px; text-align: center;
}
.login-error.hidden { display: none; }
.auto-status-row .auto-status { flex: 1; min-width: 0; }
#cancelCurrentBtn { flex-shrink: 0; }

/* ---------- source trust indicator ---------- */
.src-trusted { color: #6fe0a0; font-weight: 600; }
.src-trust-on { color: #6fe0a0; border-color: #6fe0a0; }

/* ---------- sources grouped by connected person ---------- */
.source-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.source-group-head { display: flex; align-items: center; gap: 8px; padding: 2px 2px 4px;
  border-bottom: 1px solid var(--border); }
.sg-icon { flex: none; width: 22px; height: 22px; display: inline-flex; align-items: center;
  justify-content: center; font-size: 13px; line-height: 1; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--border); }
.sg-icon.status-active { color: var(--mint); border-color: color-mix(in srgb, var(--mint) 40%, transparent); }
.sg-icon.status-guest  { color: var(--lavender); border-color: color-mix(in srgb, var(--lavender) 35%, transparent); }
.sg-icon.status-former { color: var(--muted); }
.sg-icon-other { color: var(--muted); font-weight: 700; }
.sg-name { font-weight: 600; font-size: 13px; color: var(--text); }
.sg-count { margin-left: auto; font-size: 11px; color: var(--muted);
  background: var(--surface); border-radius: 999px; padding: 1px 8px; }
/* group-header "Rename group" sits after the count pill, flush right */
.sg-rename { flex: none; }
/* per-row connection chip: which person this source feeds. '~' + dimmer = a
   URL-derived guess; solid = a manual link. */
.src-conn { color: var(--mint); font-weight: 600; }
.src-conn.derived { color: var(--muted); font-style: italic; }

/* ---------- automation not-owner warning ---------- */
.auto-not-owner { color: #ffb020; font-size: 12px; }

/* ---------- re-tag test-batch review ---------- */
.retag-review-list { max-height: 60vh; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.retag-review-row { display: flex; gap: 12px; padding: 8px; background: var(--surface-2); border-radius: 10px; }
.retag-review-row img { width: 84px; height: 84px; object-fit: cover; border-radius: 8px; flex-shrink: 0; }
.retag-review-body { min-width: 0; flex: 1; }
.retag-cap { font-size: 13px; margin-bottom: 6px; word-break: break-word; }
.retag-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.retag-chip { font-size: 11px; padding: 2px 8px; border-radius: 999px; }
.retag-chip.added { background: color-mix(in srgb, var(--mint) 18%, transparent); color: var(--mint); }
.retag-chip.removed { background: color-mix(in srgb, var(--coral) 16%, transparent); color: var(--coral); text-decoration: line-through; }
.retag-err { color: var(--coral); font-size: 13px; }

/* ---------- admin Stats / insights page ---------- */
.stats-head { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; padding: 26px 26px 6px; flex-wrap: wrap; }
.stats-title { font-family: 'Fredoka', sans-serif; font-size: 30px; margin: 0 0 4px; }
.stats-sub { color: var(--muted); font-size: 14px; margin: 0; }
.stats-body { padding: 12px 26px 40px; display: flex; flex-direction: column; gap: 22px; }
.stats-section { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px 18px; }
.stats-section.tucked { background: none; border-style: dashed; }
.ss-title { font-size: 16px; margin: 0 0 12px; color: var(--text); }
.ss-sub { font-size: 12px; text-transform: uppercase; letter-spacing: 0.6px;
  color: var(--muted); font-weight: 600; margin: 16px 0 8px; }
.ss-hint { text-transform: none; letter-spacing: 0; font-weight: 400; opacity: 0.75; }
.ss-body { min-width: 0; }
/* headline stat cards */
.stat-grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px; }
.stat-card { background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 14px; }
.sc-val { font-size: 22px; font-weight: 700; color: var(--text); line-height: 1.1; }
.sc-label { font-size: 12px; color: var(--muted); margin-top: 3px; }
.sc-sub { font-size: 11px; color: var(--muted); opacity: 0.8; margin-top: 4px; }
/* two-column layout for side-by-side breakdowns */
.stat-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
@media (max-width: 720px) { .stat-two-col { grid-template-columns: 1fr; } }

/* ---- admin: users table + review queue ---- */
.admin-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.admin-table th { text-align: left; font-size: 12px; color: var(--muted);
  font-weight: 600; padding: 6px 12px; border-bottom: 1px solid var(--border); }
.admin-table td { padding: 8px 12px; border-bottom: 1px solid var(--border);
  vertical-align: middle; }
.pill { display: inline-block; font-size: 11px; padding: 1px 7px; border-radius: 999px;
  background: var(--surface-2); color: var(--muted); }
.pill-warn { background: #b4540033; color: #e08a3c; }

.rq-section { display: flex; flex-direction: column; gap: 8px; }
.rq-section h3 { font-size: 14px; color: var(--text); margin: 4px 0; }
.rq-item { display: flex; align-items: center; gap: 12px; padding: 8px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); }
.rq-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 6px;
  flex: 0 0 auto; background: var(--surface); }
.rq-thumb-empty { display: block; }
/* Sample thumbnails for a pending-person review card (up to 3 spot-check crops). */
.rq-thumbs { display: flex; gap: 4px; flex: 0 0 auto; }
.rq-thumb-link { display: block; }
.rq-body { flex: 1 1 auto; min-width: 0; }
.rq-reason { font-size: 13px; color: var(--text); word-break: break-word; }
.rq-actions { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
/* upload review: bulk bar + circular approve (check) / reject (X) buttons */
.rq-bulkbar { display: flex; align-items: center; gap: 12px; padding: 4px 8px;
  flex-wrap: wrap; }
.rq-bulk-actions { display: inline-flex; gap: 6px; margin-left: auto; }
.rq-check { display: inline-flex; align-items: center; gap: 5px; font-size: 13px;
  color: var(--muted); cursor: pointer; }
.rq-icon-btn { width: 34px; height: 34px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; border: 1.5px solid var(--border);
  background: var(--surface); color: var(--text); cursor: pointer;
  transition: background .14s ease, border-color .14s ease, color .14s ease; }
.rq-icon-btn:hover { transform: translateY(-1px); }
.rq-icon-btn:disabled { opacity: 0.4; cursor: default; transform: none; }
.rq-approve:hover:not(:disabled) { background: var(--harlequin); border-color: var(--harlequin); color: #fff; }
.rq-reject:hover:not(:disabled)  { background: var(--coral); border-color: var(--coral); color: #fff; }

/* activity log */
.activity-filters { display: flex; gap: 8px; align-items: center; }
.act-when { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.act-tgt { color: var(--muted); font-size: 12px; white-space: nowrap; }
.act-do { text-align: right; white-space: nowrap; }
tr.act-reverted td { opacity: 0.5; }
/* Resolved name/photo links in the activity log (db.enrich_actions) -- a real
   name or thumbnail instead of a bare id, without turning the table into a
   gallery: the thumb is small and inline with the text. */
.act-link { color: var(--coral); text-decoration: none; display: inline-flex;
  align-items: center; gap: 5px; }
.act-link:hover { text-decoration: underline; }
.act-thumb { width: 20px; height: 20px; object-fit: cover; border-radius: 4px;
  vertical-align: middle; }

/* contributor leaderboard (stats page) */
.stat-table-wrap { overflow-x: auto; }
.contrib-table { width: 100%; font-variant-numeric: tabular-nums; }
.contrib-table th, .contrib-table td { text-align: right; white-space: nowrap; padding: 5px 10px; }
.contrib-table th:first-child, .contrib-table td.cc-user { text-align: left; }
.contrib-table td.cc-user { font-weight: 600; color: var(--text); }
.contrib-table td.cc-zero { color: var(--border); }
.contrib-table td.cc-total { font-weight: 700; }
.cc-recent { color: var(--harlequin); font-size: 11px; margin-left: 3px; vertical-align: super; }

/* routine (login/logout) activity rows: present but visually de-prioritized */
tr.act-routine td { opacity: 0.55; }
tr.act-routine .act-when, tr.act-routine td { color: var(--muted); }
.activity-routine-toggle { color: var(--muted); font-size: 12px; cursor: pointer;
  background: none; border: none; padding: 4px 0; text-decoration: underline; }

.users-section { margin-bottom: 26px; }
.users-section-title { font-size: 15px; color: var(--text); margin: 0 0 10px;
  padding-bottom: 6px; border-bottom: 1px solid var(--border); }
.u-id { color: var(--muted); font-variant-numeric: tabular-nums; width: 40px; }
.u-manage { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
/* new-user form */
.new-user-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 14px; }
.new-user-form input[type=text], .new-user-form input[type=password] {
  padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg); color: var(--text); font-size: 14px; }
.new-user-form select { padding: 6px 8px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--bg); color: var(--text); }
/* trusted toggle switch */
.new-user-trusted { display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--muted); cursor: pointer; user-select: none; }
.new-user-trusted input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-track { position: relative; width: 38px; height: 22px; flex: none;
  background: var(--border); border-radius: 999px; transition: background .16s ease; }
.toggle-thumb { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  background: #fff; border-radius: 50%; transition: transform .16s ease;
  box-shadow: 0 1px 2px rgba(0,0,0,.35); }
.new-user-trusted input:checked + .toggle-track { background: var(--harlequin); }
.new-user-trusted input:checked + .toggle-track .toggle-thumb { transform: translateX(16px); }
.new-user-trusted input:focus-visible + .toggle-track {
  outline: 2px solid var(--denim); outline-offset: 2px; }
.new-user-trusted .toggle-label { color: var(--text); }
.new-user-msg { font-size: 13px; }
.new-user-msg.ok { color: var(--harlequin); }
.new-user-msg.err { color: var(--coral); }

/* ---- role-based visibility ----
   The body carries is-admin / is-trusted / is-user (set from /api/me in setMe).
   .admin-only shows only for admins; .trusted-only for trusted users + admins.
   Non-trusted users see neither, leaving them view + flag only. Server enforces
   the same tiers -- this just declutters the UI. Default hidden so there's no
   flash for lower tiers before /api/me resolves (setMe adds the body class). */
.admin-only, .trusted-only { display: none !important; }
body.is-admin .admin-only { display: revert !important; }
body.is-trusted .trusted-only { display: revert !important; }
/* the inverse: things ONLY non-admins should see (e.g. a Flag button where an
   admin has Delete). Hidden for admins. */
body.is-admin .nonadmin-only { display: none !important; }
/* trust-specific splits (non-admin only). is-trusted covers admins too, so gate
   these on is-user (authenticated non-admin) to exclude admins:
     .trusted-user-only  -> a TRUSTED non-admin (is-user + is-trusted)
     .untrusted-only     -> an UNTRUSTED non-admin (is-user, NOT is-trusted) */
.trusted-user-only, .untrusted-only { display: none !important; }
body.is-user.is-trusted .trusted-user-only { display: revert !important; }
body.is-user:not(.is-trusted) .untrusted-only { display: revert !important; }
/* pending-backlog workflow (tag-pending + review-pending) is local-only. On prod
   the shell renders body.no-pending, hiding those two top-bar controls. Hidden
   via CSS (not template-removed) because app.js binds tagBtn/tagStartBtn UNGUARDED
   -- removing them from the DOM throws at boot and freezes the SPA. */
body.no-pending #tagBtn,
body.no-pending .tag-wrap,
body.no-pending #reviewBtn { display: none !important; }
/* re-tag buttons run the VLM, which doesn't exist on prod (no-curate). Hidden
   via CSS (not removed) -- app.js binds both unguarded. */
body.no-curate #retagBtn,
body.no-curate #galleryRetagBtn { display: none !important; }
/* Ignored faces is an admin face-clustering tool with no ML stack on prod.
   The JS guard in refreshIgnoredCount keeps it hidden for non-admins + prod;
   this is the belt-and-suspenders for prod (won't fight the count-based
   .hidden toggle for a local admin -- only applies under no-curate). */
body.no-curate #ignoredSection { display: none !important; }
/* The face-clustering COMPUTE controls need the ML stack (absent on prod), so
   under no-recluster hide "Find faces to name" + the grouping/crowd knobs + the
   recluster note. The cluster GRID + empty state stay visible -- prod users NAME
   the frozen, pre-built piles (cluster_review); only the re-tuning is gone. The
   client-side size slider (#clusterSizeFilter) replaces the ML knobs there.
   (Hidden elements stay in the DOM -- their JS grabs them at module load.) */
body.no-recluster #reclusterBtn,
body.no-recluster .cluster-controls,
body.no-recluster #reclusterNote { display: none !important; }
/* prod hides .cluster-controls (which carries the down-shift), so move the nudge
   onto the meta row there so the cluster area still sits apart from teams. */
body.no-recluster #clusterMetaRow { margin-top: 26px; }
/* The prod-only client-side size slider: hidden where the ML knobs exist (local
   admins re-cluster instead), shown under no-recluster -- but ONLY to trusted
   users + admins, since only they see/name clusters (untrusted have no cluster
   surface to size-filter). */
#clusterSizeFilter { display: none; }
body.no-recluster.is-trusted #clusterSizeFilter { display: flex; }
/* Untrusted users: the People page is view-only (browse the roster). The whole
   cluster-review + team-management surface is trusted+admin only. Hidden with
   display:none (NOT .trusted-only, whose display:revert re-show would flatten
   the grid/flex layouts). This is a UX declutter -- the server gate
   (_TRUSTED_WRITE_ENDPOINTS) is the real boundary; these endpoints 403 for an
   untrusted user regardless of what's shown. */
body.is-user:not(.is-trusted) .teams-section,
body.is-user:not(.is-trusted) .cluster-controls,
body.is-user:not(.is-trusted) #clusterMetaRow,
body.is-user:not(.is-trusted) #clusterGrid,
body.is-user:not(.is-trusted) #clustersEmpty,
body.is-user:not(.is-trusted) #ignoredSection { display: none !important; }
/* Local Users page: user activity is reviewed on prod, so the local review
   section shows this notice instead of a queue. */
.review-prod-notice { padding: 22px 24px; border: 1px dashed var(--border);
  border-radius: 12px; background: var(--surface-2, rgba(127,127,127,0.06)); }
.review-prod-title { font-size: 15px; font-weight: 600; margin: 0 0 6px; }
.review-prod-notice code { font-size: 0.9em; padding: 1px 5px; border-radius: 4px;
  background: rgba(127,127,127,0.15); }
/* horizontal bar breakdowns */
.stat-bars { display: flex; flex-direction: column; gap: 5px; }
.stat-bar-row { display: grid; grid-template-columns: 120px 1fr 52px; align-items: center; gap: 8px; }
.sb-label { font-size: 12.5px; color: var(--text); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.stat-bar-link { color: var(--text); text-decoration: none; }
.stat-bar-link:hover { color: var(--mint); text-decoration: underline; }
.sb-track { height: 8px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.sb-fill { display: block; height: 100%; background: var(--mint); border-radius: 999px; }
.sb-n { font-size: 12px; color: var(--muted); text-align: right; }
/* no-photo person chips */
.stat-chip-list { display: flex; flex-wrap: wrap; gap: 6px; }
.stat-person-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px;
  padding: 3px 10px; border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--border); color: var(--text); text-decoration: none; }
.stat-person-chip:hover { border-color: var(--mint); }
.spc-status { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.4px; }
/* tucked-away collapsible (blocked list) */
.stats-details > summary { cursor: pointer; font-weight: 600; font-size: 14px; color: var(--text);
  list-style: none; padding: 4px 0; }
.stats-details > summary::-webkit-details-marker { display: none; }
.stats-details > summary::before { content: '▸ '; color: var(--muted); }
.stats-details[open] > summary::before { content: '▾ '; }
.blocked-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px; margin-top: 10px; }
.blocked-cell { background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 10px; padding: 8px; display: flex; flex-direction: column; gap: 6px; align-items: center; }
.blocked-cell img { width: 72px; height: 72px; object-fit: cover; border-radius: 8px; }
.bc-noface { width: 72px; height: 72px; border-radius: 8px; background: var(--surface);
  display: flex; align-items: center; justify-content: center; color: var(--muted); }
.bc-meta { font-size: 10.5px; color: var(--muted); text-align: center; line-height: 1.3; }

/* ---------- Sync to prod (admin, local-only) ---------- */
.sync-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin-bottom: 18px;
}
.sync-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sync-card h3 { margin: 0; font-size: 0.98rem; }
.sync-card .field-note { flex: 1; margin: 0; }
.sync-card .sync-run { margin-top: 4px; align-self: flex-start; }
.sync-run:disabled { opacity: 0.5; cursor: default; transform: none; box-shadow: none; }

/* progress panel (steps + bar + collapsible log) */
.sync-progress {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card-bg);
}
.sync-log-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  font-size: 0.92rem;
  font-weight: 600;
}
.sync-phase { color: var(--muted); font-weight: 500; font-variant-numeric: tabular-nums; }
.sync-phase.ok { color: var(--harlequin); }
.sync-phase.err { color: var(--coral); }

/* step checklist */
.sync-steps {
  list-style: none;
  margin: 0;
  padding: 14px 16px 6px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
}
.sync-step {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.86rem;
  color: var(--muted);
}
/* The status mark. NOTE: don't render the "in progress" dot as a text glyph (●) --
   a glyph's ink sits off its baseline, so flex-centering centers the LINE BOX and
   the dot always looks off-center no matter the line-height. The dot is drawn as a
   real CSS circle (::before) instead, which is centered by construction. The ✓/✗
   glyphs are fine as text (line-height:1 + flex centering). */
.sync-step-mark {
  width: 18px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  font-size: 0.72rem; line-height: 1;
  border: 1.5px solid var(--border);
  color: var(--muted);
  /* hide any text content by default; states below opt back in */
  font-size: 0;
}
/* waiting + active: a drawn dot, perfectly centered */
.sync-step-mark::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.45;
}
.sync-step.active .sync-step-mark::before { opacity: 1; }
/* done/error show a real glyph instead of the drawn dot */
.sync-step.done .sync-step-mark,
.sync-step.error .sync-step-mark { font-size: 0.72rem; }
.sync-step.done .sync-step-mark::before,
.sync-step.error .sync-step-mark::before { content: none; }
.sync-step.active { color: var(--text); font-weight: 600; }
.sync-step.active .sync-step-mark {
  border-color: var(--coral); color: var(--coral);
  animation: sync-pulse 1s ease-in-out infinite;
}
.sync-step.done { color: var(--text); }
.sync-step.done .sync-step-mark {
  background: var(--harlequin); border-color: var(--harlequin); color: var(--ink);
}
.sync-step.error .sync-step-mark {
  background: var(--coral); border-color: var(--coral); color: var(--ink);
}
@keyframes sync-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }

/* progress bar: determinate (fill width) or indeterminate (moving shimmer) */
.sync-bar {
  position: relative;
  height: 8px;
  margin: 6px 16px 14px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--border) 60%, transparent);
  overflow: hidden;
}
.sync-bar-fill {
  position: absolute; inset: 0 auto 0 0;
  width: 0;
  border-radius: 999px;
  background: var(--coral);
  transition: width 0.3s ease;
}
.sync-bar.err .sync-bar-fill { background: var(--coral); }
.sync-bar.indeterminate .sync-bar-fill {
  width: 35%;
  animation: sync-slide 1.15s ease-in-out infinite;
}
@keyframes sync-slide {
  0% { left: -35%; } 100% { left: 100%; }
}

/* collapsible raw output log */
.sync-log-details { border-top: 1px solid var(--border); }
.sync-log-details > summary {
  cursor: pointer; padding: 8px 14px; font-size: 0.82rem; color: var(--muted);
  user-select: none;
}
.sync-log-details > summary:hover { color: var(--text); }
.sync-log {
  margin: 0;
  padding: 12px 14px;
  max-height: 300px;
  overflow: auto;
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--bg);
  color: var(--text);
  border-top: 1px solid var(--border);
}

/* ---------- site footer ---------- */
.site-footer {
  /* margin-top:auto is the sticky-footer lever (see body flex column) -- on short
     pages it eats the free vertical space and pushes the footer to the bottom of
     the screen; on tall pages it collapses to 0 and the content's own bottom
     padding (grid/section padding) keeps the gap above the divider. */
  margin: auto auto 24px;
  padding: 16px 20px 0;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 11px;
  line-height: 1.6;
  text-align: center;
}
.site-footer p { margin: 0 auto 6px;   max-width: 1100px;}
.site-footer p:last-child { margin-bottom: 0; }

/* ---------- schedule / calendar ----------
   The calendar view reuses .stats-view (single centered column). These styles
   cover the month grid, the agenda list beneath it, and the birthday/debut/show
   badges. Colors come from the same theme tokens as everything else so the view
   re-skins with the active wrestler theme. */
.cal-head-actions { display: flex; align-items: center; gap: 8px; }
.cal-month-label { font-weight: 700; min-width: 9.5em; text-align: center; }
/* quiet aside on the Schedule header -- said once here, never per-event */
.cal-tz-note { opacity: 0.75; font-style: italic; }

/* inline icon: sizes to the surrounding text, inherits color via currentColor */
.ic { width: 1em; height: 1em; display: inline-block; vertical-align: -0.13em; fill: none; }

/* ----- two-column layout: calendar (left) | listing (right) ----- */
.cal-layout {
  display: grid;
  grid-template-columns: minmax(340px, 1.15fr) minmax(280px, 1fr);
  gap: 24px;
  align-items: start;
  padding: 12px 26px 40px;      /* align with .stats-head's 26px gutters */
  max-width: 1800px;            /* don't sprawl on ultra-wide screens */
  margin:0 auto;
}
.cal-col { min-width: 0; }
/* the agenda column scrolls independently on tall months, pinned near the grid */
.cal-col-agenda { position: sticky; top: 8px; max-height: calc(100vh - 90px); overflow-y: auto; }
/* month-nav now lives in the calendar column, above the grid */
.cal-monthbar {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-bottom: 8px;
}
/* flow to a single column on tablet & smaller */
@media (max-width: 860px) {
  .cal-layout { grid-template-columns: 1fr; gap: 18px; }
  .cal-col-agenda { position: static; max-height: none; overflow: visible; }
}

/* ----- month grid (roomy, informative cells) ----- */
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
  margin: 0;
}
.cal-dow {
  text-align: center; font-size: 0.72rem; letter-spacing: 0.05em;
  color: var(--muted); padding: 2px 0; text-transform: uppercase; font-weight: 700;
}
.cal-cell {
  position: relative; min-height: 96px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); padding: 6px 7px;
  display: flex; flex-direction: column; gap: 4px; overflow: visible;
}
.cal-cell.cal-empty { border: none; background: transparent; }
.cal-cell.cal-has { background: var(--surface-2); cursor: pointer; }
.cal-cell.cal-has:hover { border-color: var(--mint); }
.cal-cell.cal-today { outline: 2px solid var(--coral); outline-offset: -1px; }
.cal-cell:focus-visible { outline: 2px solid var(--mint); outline-offset: -1px; }
.cal-num { font-size: 0.82rem; color: var(--muted); line-height: 1; font-weight: 600; }
.cal-cell.cal-has .cal-num { color: var(--text); }
.cal-cell.cal-today .cal-num { color: var(--coral); }
.cal-marks { display: flex; flex-direction: column; gap: 3px; align-items: stretch; min-width: 0; overflow: hidden; }
/* a hovered / open cell floats above its neighbors so its popup isn't clipped by them */
.cal-cell:hover, .cal-cell:focus-visible, .cal-cell.open { z-index: 41; }
.cal-pill {
  font-size: 0.66rem; line-height: 1.25; max-width: 100%;
  padding: 2px 6px; border-radius: 5px; font-weight: 700; overflow: hidden;
  display: flex; align-items: flex-start; gap: 3px;
}
.cal-pill .ic { width: 0.85em; height: 0.85em; flex: none; margin-top: 0.1em; }
.cal-pill-show  { background: var(--mint); color: var(--ink); }
.cal-pill-bday  { background: var(--coral); color: var(--ink); }
.cal-pill-debut { background: var(--butter); color: var(--ink); }
/* title/name: up to 2 lines, break at spaces first, then ellipsis */
.cal-pill-name {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; line-clamp: 2;
  overflow: hidden; overflow-wrap: anywhere;
}
.cal-pill-more { background: transparent; color: var(--muted); padding: 0 2px; font-weight: 600; white-space: nowrap; }

/* ----- cell hover/tap popup (full day details) ----- */
.cal-popup {
  position: absolute; z-index: 40; left: 50%; top: 100%;
  transform: translateX(-50%) translateY(6px);
  min-width: 210px; max-width: 280px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 10px; padding: 9px 11px; text-align: left;
  box-shadow: 0 10px 30px rgba(0,0,0,0.35);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
}
/* a little pointer arrow */
.cal-popup::before {
  content: ""; position: absolute; left: 50%; top: -5px; width: 9px; height: 9px;
  background: var(--surface); border-left: 1px solid var(--border); border-top: 1px solid var(--border);
  transform: translateX(-50%) rotate(45deg);
}
.cal-cell:hover .cal-popup,
.cal-cell:focus-visible .cal-popup,
.cal-cell.open .cal-popup {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateX(-50%) translateY(4px);
}
/* keep the popup on-screen at the row edges */
.cal-grid > .cal-cell:nth-child(7n+1) .cal-popup,
.cal-grid > .cal-cell:nth-child(7n+2) .cal-popup { left: 0; transform: translateX(0) translateY(6px); }
.cal-grid > .cal-cell:nth-child(7n+1):hover .cal-popup,
.cal-grid > .cal-cell:nth-child(7n+2):hover .cal-popup,
.cal-grid > .cal-cell:nth-child(7n+1).open .cal-popup,
.cal-grid > .cal-cell:nth-child(7n+2).open .cal-popup { transform: translateX(0) translateY(4px); }
.cal-grid > .cal-cell:nth-child(7n).cal-popup,
.cal-grid > .cal-cell:nth-child(7n) .cal-popup { left: auto; right: 0; transform: translateX(0) translateY(6px); }
.cal-grid > .cal-cell:nth-child(7n):hover .cal-popup,
.cal-grid > .cal-cell:nth-child(7n).open .cal-popup { transform: translateX(0) translateY(4px); }
.calpop-head {
  font-weight: 800; font-size: 0.72rem; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted); margin-bottom: 7px;
}
.calpop-item { margin-bottom: 8px; }
.calpop-item:last-child { margin-bottom: 0; }
.calpop-title { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-weight: 700; font-size: 0.82rem; color: var(--text); }
.calpop-meta { color: var(--muted); font-size: 0.74rem; margin-top: 2px; }
.calpop-links { display: flex; gap: 6px; margin-top: 5px; }
.calpop-anniv { display: flex; align-items: center; gap: 6px; font-size: 0.8rem; color: var(--text); }
.calpop-anniv .ic { width: 1em; height: 1em; }
.calpop-anniv:first-child, .calpop-item + .calpop-anniv { margin-top: 2px; }

/* ----- agenda (fills its column) ----- */
.cal-agenda-wrap { width: 100%; }
.cal-agenda { display: flex; flex-direction: column; gap: 8px; text-align: left; }
.cal-agenda-day {
  display: grid; grid-template-columns: 3.2em 1fr; gap: 12px; align-items: start;
  padding: 10px 12px; border-radius: 12px; background: var(--surface);
  border: 1px solid var(--border);
}
.cal-agenda-today { border-color: var(--coral); box-shadow: 0 0 0 1px var(--coral) inset; }
.cal-agenda-date {
  display: flex; flex-direction: column; align-items: center; line-height: 1;
  padding-top: 2px; color: var(--muted);
}
.cal-agenda-dow { font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em; }
.cal-agenda-num { font-size: 1.35rem; font-weight: 800; color: var(--text); }
.cal-agenda-today .cal-agenda-num,
.cal-agenda-today .cal-agenda-dow { color: var(--coral); }
.cal-agenda-rows { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

/* one show row: time | (badges + name / venue) | links */
.cal-item-show { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: start; }
.cal-time {
  font-size: 0.9rem; font-weight: 800; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap; padding-top: 1px;
}
.cal-time-tba { color: var(--muted); font-weight: 700; font-size: 0.78rem; }
.cal-item-body { min-width: 0; }
.cal-item-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.cal-item-title { color: var(--text); font-weight: 600; font-size: 0.95rem; }
.cal-venue { color: var(--muted); font-size: 0.78rem; margin-top: 2px; }
.cal-item-links { display: flex; gap: 6px; align-items: center; }

/* live / VOD chip */
.cal-stream {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 0.6rem; font-weight: 800; letter-spacing: 0.04em;
  padding: 1px 6px 1px 5px; border-radius: 999px; color: var(--ink);
}
.cal-stream .ic { width: 0.85em; height: 0.85em; }
.cal-stream-live { background: var(--coral); color: var(--ink); }
.cal-stream-vod  { background: var(--lavender); color: var(--ink); }

/* external link chip (WU / TJPW) */
.cal-ext {
  display: inline-flex; align-items: center; gap: 2px;
  color: var(--mint); text-decoration: none; font-weight: 800; font-size: 0.66rem;
  border: 1px solid var(--border); border-radius: 6px; padding: 1px 5px 1px 3px;
}
.cal-ext:hover { border-color: var(--mint); }
.cal-ext .ic { width: 0.95em; height: 0.95em; }
.cal-hide {
  border: 1px solid var(--border); background: transparent; color: var(--muted);
  border-radius: 6px; font-size: 0.72rem; line-height: 1; padding: 2px 6px; cursor: pointer;
}
.cal-hide:hover { color: var(--coral); border-color: var(--coral); }

/* anniversary row */
.cal-item-anniv { display: flex; align-items: center; gap: 8px; }
.cal-badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.05em;
  padding: 2px 8px 2px 6px; border-radius: 999px; white-space: nowrap;
  text-transform: uppercase; color: var(--ink);
}
.cal-badge .ic { width: 0.9em; height: 0.9em; }
.cal-badge-bday  { background: var(--coral); color: var(--ink); }
.cal-badge-debut { background: var(--butter); color: var(--ink); }
.cal-person { color: var(--lavender); cursor: pointer; font-weight: 700; }
.cal-person:hover { text-decoration: underline; }

/* ---------- landing "today / upcoming" strip ---------- */
.hero-schedule {
  display: flex; flex-direction: column; gap: 8px; align-items: stretch;
  margin: 18px auto 0; max-width: 460px; width: 100%;
}
.hero-sched-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 2px;
}
.hero-sched-title {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.72rem; font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
}
.hero-sched-title .ic { width: 1.05em; height: 1.05em; color: var(--mint); }
.hero-sched-list { display: flex; flex-direction: column; gap: 7px; }
.hero-sched-item {
  font-size: 0.86rem; color: var(--text); padding: 7px 12px;
  border-radius: 12px; background: var(--surface-2);
  border: 1px solid var(--border); width: 100%;
  display: flex; align-items: center; gap: 7px; justify-content: center;
  text-align: center; flex-wrap: wrap;
}
.hero-sched-show .hs-title { font-weight: 600; }
.hero-sched-anniv {
  cursor: pointer; font-weight: 700;
  background: linear-gradient(90deg, var(--coral), var(--butter));
  color: var(--ink); border: none;   /* theme's 'text on accent' -> readable on every skin */
  animation: hs-pop 2.4s ease-in-out infinite;
}
.hero-sched-anniv b { color: var(--ink); }
.hero-sched-anniv .hs-icon { font-size: 1.05rem; }
@keyframes hs-pop { 0%,100% { transform: scale(1); } 50% { transform: scale(1.03); } }
.hero-sched-show .hs-when { font-weight: 800; color: var(--mint); }
.hs-time { font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }
.hs-venue { color: var(--muted); }

/* ---------- birthday badge + celebration effect ----------
   A person whose birthday is today gets a sparkle badge on their photo cards
   and profile; the profile view adds a soft celebratory glow. Both are toggled
   by a body-level flag list built from /api/birthdays/today (see app.js). */
.bday-badge {
  position: absolute; top: 6px; left: 6px; z-index: 3;
  font-size: 1rem; line-height: 1; pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5));
  animation: bday-bounce 1.8s ease-in-out infinite;
}
@keyframes bday-bounce {
  0%,100% { transform: translateY(0) rotate(-6deg); }
  50%     { transform: translateY(-3px) rotate(6deg); }
}
.profile-bday-glow {
  position: relative;
}
.profile-bday-glow::before {
  content: ""; position: absolute; inset: -2px; border-radius: inherit;
  background: linear-gradient(120deg, var(--coral), var(--butter), var(--mint));
  opacity: 0.5; z-index: -1; filter: blur(10px);
  animation: bday-glow 3s ease-in-out infinite;
}
@keyframes bday-glow { 0%,100% { opacity: 0.35; } 50% { opacity: 0.6; } }

/* confetti burst: fixed pieces animated in, cleaned up by JS after the run */
.confetti-piece {
  position: fixed; top: -12px; width: 9px; height: 14px; z-index: 9999;
  pointer-events: none; opacity: 0.9; border-radius: 2px;
  animation: confetti-fall linear forwards;
}
@keyframes confetti-fall {
  to { transform: translateY(110vh) rotate(540deg); opacity: 0.2; }
}
