/* ═══════════════════════════════════════════════════════════════════════════
   PICSelectHub — PUBLIC & GUEST PAGES
   ───────────────────────────────────────────────────────────────────────────
   Standalone; nothing is shared with admin.css or client.css by import. It
   serves every page that is not inside a panel: the guest gallery and its
   gate, face search, the flipbook viewer, sign-in helpers (forgot, register,
   reset), the error and maintenance pages, and the installer. It replaces
   gallery.css.

   Two things set this surface apart:

   · It is BRANDED. --p is the photographer's own colour, written per event by
     the gallery page. The fallbacks below apply only where no event is in play.

   · The gallery viewer is dark on purpose — a photograph is judged against its
     surround — and keeps its own palette. Everything else follows the
     light-first direction of the panels.

   The fault this file exists to fix: gallery.css defined no custom properties,
   and the standalone pages linked no stylesheet at all, yet their own CSS was
   written against var(--border), var(--surface) and the rest. An undefined
   variable voids the whole declaration, so across the guest surface borders,
   card surfaces and button fills silently rendered as nothing.

   Structure
     1  tokens
     2  the public layout's reset
     3  form, button, alert and gate components (pages that used gallery.css)
     Page rules — one section per page, scoped with :where([data-page="…"])
   ═══════════════════════════════════════════════════════════════════════════ */


/* ═══ 1. Tokens ════════════════════════════════════════════════════════════ */

:root {
  --bg: #f6f7f9;
  --surface: #ffffff;
  --surface2: #f3f4f6;
  --surface3: #e9ebef;

  --border: #e5e7eb;
  --border-strong: #d0d5dd;

  --text: #101828;
  --muted: #5f6a7d;
  --faint: #98a2b3;

  /* The photographer's colour where a page sets it; the product blue where
     none does. */
  --p: #2563eb;
  --accent: #2563eb;
  --accent-fill: #2563eb;
  --accent-fill-hover: #1d4ed8;
  --accent-hover: #1d4ed8;
  --accent-ink: #ffffff;
  --accent-soft: rgba(37, 99, 235, .08);
  --accent-line: rgba(37, 99, 235, .28);
  --primary: var(--accent);

  --success: #067647;
  --success-solid: #067647;
  --success-soft: rgba(6, 118, 71, .09);
  --warning: #b54708;
  --warning-solid: #b54708;
  --warning-soft: rgba(181, 71, 8, .09);
  --danger: #c4261b;
  --danger-solid: #b42318;
  --danger-soft: rgba(217, 45, 32, .08);
  --info: #175cd3;
  --info-soft: rgba(23, 92, 211, .08);

  --ring: rgba(16, 24, 40, .24);
  --ring-shadow: 0 0 0 3px rgba(16, 24, 40, .07);

  --radius-sm: 6px;
  --radius: 9px;
  --radius-lg: 13px;
  --radius-pill: 999px;

  --shadow-1: 0 1px 2px rgba(16, 24, 40, .05);
  --shadow-2: 0 8px 24px rgba(16, 24, 40, .08);
  --shadow-3: 0 24px 56px rgba(16, 24, 40, .14);
  --shadow-ink: rgba(16, 24, 40, .12);

  --transition: .15s cubic-bezier(.4, 0, .2, 1);
}

[data-theme="dark"] {
  --bg: #0b0d10;
  --surface: #14161a;
  --surface2: #1b1e24;
  --surface3: #23272e;

  --border: #22262c;
  --border-strong: #333942;

  --text: #e9ecf1;
  --muted: #8b93a1;
  --faint: #5f6673;

  --accent: #60a5fa;
  --accent-fill: #2563eb;
  --accent-fill-hover: #1d4ed8;
  --accent-hover: #93c5fd;
  --accent-ink: #ffffff;
  --accent-soft: rgba(96, 165, 250, .14);
  --accent-line: rgba(96, 165, 250, .32);

  --success: #4ade80;
  --success-solid: #15803d;
  --success-soft: rgba(74, 222, 128, .13);
  --warning: #fbbf24;
  --warning-solid: #b45309;
  --warning-soft: rgba(251, 191, 36, .13);
  --danger: #f87171;
  --danger-solid: #b91c1c;
  --danger-soft: rgba(248, 113, 113, .13);
  --info: #60a5fa;
  --info-soft: rgba(96, 165, 250, .13);

  --ring: rgba(255, 255, 255, .26);
  --ring-shadow: 0 0 0 3px rgba(255, 255, 255, .10);

  --shadow-1: 0 1px 2px rgba(0, 0, 0, .28);
  --shadow-2: 0 12px 34px rgba(0, 0, 0, .44);
  --shadow-3: 0 28px 70px rgba(0, 0, 0, .58);
  --shadow-ink: rgba(0, 0, 0, .55);
}


/* The face-search journey — the selfie page, the matched photos and the
   "temporarily unavailable" page — sits over photographs like the gallery it
   comes from, and was designed dark. Earlier colour sweeps half-converted it
   to the theme, which in light mode left white text on white. It keeps a dark
   ladder of its own, in the product blue rather than the old purple. */
:root:where([data-page="gallery-face_search"], [data-page="gallery-face_results"], [data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) {
  --bg: #0b0d10;
  --surface: #14161a;
  --surface2: #1b1e24;
  --surface3: #23272e;
  --border: #262a31;
  --border-strong: #363c46;
  --text: #e9ecf1;
  --muted: #9aa2af;
  --faint: #6b7280;
  --accent: #60a5fa;
  --accent-fill: #2563eb;
  --accent-fill-hover: #1d4ed8;
  --accent-ink: #ffffff;
  --accent-soft: rgba(96, 165, 250, .14);
  --accent-line: rgba(96, 165, 250, .32);
  --success: #4ade80;
  --success-solid: #15803d;
  --danger: #f87171;
  --danger-solid: #b91c1c;
  --danger-soft: rgba(248, 113, 113, .13);
  --ring: rgba(255, 255, 255, .26);
  --shadow-ink: rgba(0, 0, 0, .55);
}


/* ═══ 2. The public layout's reset ═════════════════════════════════════════
   layouts/public.ejs used to carry this inline, with a white page and a
   near-black ink hard-coded into it that ignored the theme. :where() keeps it
   at zero specificity, so every page and component rule still wins. */

:where([data-layout="public"]) *,
:where([data-layout="public"]) *::before,
:where([data-layout="public"]) *::after { box-sizing: border-box; margin: 0; padding: 0; }

:where([data-layout="public"]) body {
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
}


/* ═══ 3. Components the gallery pages share ════════════════════════════════
   Only the pages that used to load gallery.css get these: the public layout
   (sign-in helpers, gallery register and expired, flipbook viewer) and the
   guest viewer. Sizes and spacing are gallery.css's own; colour now comes from
   the tokens, the focus ring is neutral, and the purple gradient is gone. */

:where([data-layout="public"], [data-page="gallery-viewer"]) .form-group { margin-bottom: 18px; }

:where([data-layout="public"], [data-page="gallery-viewer"]) .form-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 6px;
  letter-spacing: .03em;
}

:where([data-layout="public"], [data-page="gallery-viewer"]) .form-control {
  width: 100%;
  background-color: var(--surface);
  border: 1px solid var(--border-strong);
  color: var(--text);
  border-radius: 10px;
  padding: 11px 14px;
  font-size: 14px;
  font-family: inherit;
  outline: none;
  transition: border-color .2s, box-shadow .2s, background-color .2s;
}
:where([data-layout="public"], [data-page="gallery-viewer"]) .form-control::placeholder { color: var(--faint); }
:where([data-layout="public"], [data-page="gallery-viewer"]) .form-control:focus {
  border-color: var(--border-strong);
  box-shadow: var(--ring-shadow);
  background-color: var(--surface);
}

:where([data-layout="public"], [data-page="gallery-viewer"]) .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 24px;
  border: none;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: all .2s ease;
  text-decoration: none;
}
:where([data-layout="public"], [data-page="gallery-viewer"]) .btn-primary {
  background: var(--accent-fill);
  color: var(--accent-ink);
  box-shadow: var(--shadow-1);
}
:where([data-layout="public"], [data-page="gallery-viewer"]) .btn-primary:hover {
  background: var(--accent-fill-hover);
  transform: translateY(-1px);
  box-shadow: var(--shadow-2);
}
:where([data-layout="public"], [data-page="gallery-viewer"]) .btn-primary:active { transform: translateY(0); }

:where([data-layout="public"], [data-page="gallery-viewer"]) .alert {
  border-radius: 10px;
  padding: 14px 16px;
  margin-bottom: 18px;
  font-size: 14px;
  line-height: 1.5;
}
:where([data-layout="public"], [data-page="gallery-viewer"]) .alert-error {
  background: var(--danger-soft);
  border: 1px solid var(--border);
  color: var(--danger);
}

/* The PIN / link-expired gate. */
:where([data-layout="public"]) .gallery-gate {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  padding: 24px;
}
:where([data-layout="public"]) .gate-card {
  width: 100%;
  max-width: 440px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 36px;
  box-shadow: var(--shadow-2);
}

/* ═══ Page rules ══════════════════════════════════════════════════════════
   One section per page, moved out of the views. Each rule is scoped with
   :where([data-page="…"]), which adds no specificity, and the sections sit
   after everything above, so every rule keeps the cascade position it had
   when it lived in the page's own <style> block. */

/* ══ The signed-out pages ═════════════════════════════════════════════════
   Sign in, create account, forgot password, set a new password.

   One system rather than four. These four pages had four unrelated prefixes
   — lf-, rg-, fp-, rp- — with four copies of the same field, the same card
   and the same button, which had already drifted apart. They are the first
   thing anyone sees, they are reached from one another, and a person moving
   between them should not feel the ground change.

   These class names are deliberately NOT scoped by data-page: they are a
   shared family, like .btn or .theme-toggle, and scoping would mean four
   copies again.

   ── Colour ──────────────────────────────────────────────────────────────
   Ink, edges and surfaces stay neutral; colour is spent on the few things
   that are meant to be looked at — the mark, the primary action, a verified
   tick, the emphasis in a heading. That is the difference between a page
   that reads as considered and one that reads as unfinished, and it is also
   why the product's rule about edges still holds: no coloured borders, no
   outlines, no focus rings. A field in error says so in words.            */

:root {
  /* Indigo into violet: distinct from the product blue used inside the
     panel, so signed-out pages read as their own place. */
  --au-1: #4f46e5;
  --au-2: #7c3aed;
  --au-grad: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
  --au-ink: #1e1b4b;
  --au-text: #4338ca;
  --au-soft: rgba(79, 70, 229, .075);
  --au-soft-2: rgba(124, 58, 237, .07);
  --au-glow: rgba(79, 70, 229, .22);
  --au-on: #ffffff;
}
[data-theme="dark"] {
  /* Lifted so it carries on a dark surface, and the fill deepened so white
     still reads on it. */
  --au-1: #a5b4fc;
  --au-2: #c4b5fd;
  --au-grad: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
  --au-ink: #e9ecf1;
  --au-text: #a5b4fc;
  --au-soft: rgba(129, 140, 248, .13);
  --au-soft-2: rgba(167, 139, 250, .11);
  --au-glow: rgba(99, 102, 241, .32);
  --au-on: #ffffff;
}

/* ── the shell ────────────────────────────────────────────────────────────
   Below 1024px this is an ordinary page that scrolls as one. At 1024px and
   up it becomes two panes that fill the window: the left one never moves,
   and the right one is the only thing that scrolls. */
.auth-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1fr;
  background: var(--bg);
}
@media (min-width: 1024px) {
  .auth-shell {
    grid-template-columns: 1fr 1.2fr;
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
  }
  .auth-shell.is-narrow { grid-template-columns: 1fr 1fr; }
}

/* ── the inert side ───────────────────────────────────────────────────── */
.auth-aside { display: none; }
.auth-aside-in { display: flex; flex-direction: column; justify-content: center; }
@media (min-width: 1024px) {
  .auth-aside {
    display: block;
    position: relative;
    /* It fills its pane and clips; it is not a scroller and never moves. */
    height: 100%;
    overflow: hidden;
    background:
      radial-gradient(120% 90% at 8% 0%, var(--au-soft) 0%, transparent 58%),
      radial-gradient(100% 80% at 100% 100%, var(--au-soft-2) 0%, transparent 60%),
      var(--surface2);
    border-right: 1px solid var(--border);
  }
  .auth-aside-in {
    height: 100%;
    max-width: 560px;
    padding: 56px 52px;
    overflow: hidden;
  }
}
@media (min-width: 1440px) {
  .auth-aside-in { padding: 64px 64px; }
}

/* A photographer's own image, when they have set one: it replaces the copy
   rather than sitting behind it, so nothing has to stay readable over it. */
.auth-aside.has-image .auth-aside-in { display: none; }
.auth-aside-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}

.auth-aside-h {
  font-size: clamp(26px, 2.4vw, 34px);
  font-weight: 800; line-height: 1.16; letter-spacing: -.03em;
  color: var(--au-ink); margin: 30px 0 14px;
}
/* The one coloured phrase in the heading. */
.auth-aside-h em { font-style: normal; color: var(--au-text); }
.auth-aside-p {
  font-size: 14.5px; line-height: 1.65; color: var(--muted); margin: 0 0 32px;
}
.auth-points {
  list-style: none; margin: 0 0 34px; padding: 0;
  display: flex; flex-direction: column; gap: 18px;
}
.auth-points li {
  display: flex; align-items: flex-start; gap: 13px;
  font-size: 13.5px; line-height: 1.55; color: var(--muted);
}
.auth-points strong { color: var(--text); font-weight: 650; }
.auth-point-i {
  flex: 0 0 32px; width: 32px; height: 32px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--au-soft); border: 1px solid var(--border);
  color: var(--au-text);
}
.auth-point-i svg { width: 15px; height: 15px; }
.auth-aside-foot { font-size: 13px; color: var(--muted); margin: 0; }
.auth-aside-foot a {
  color: var(--au-text); font-weight: 650; text-decoration: none;
  border-bottom: 1px solid var(--border-strong);
}
.auth-aside-foot a:hover { border-bottom-color: var(--au-1); }

/* ── the mark ─────────────────────────────────────────────────────────── */
.auth-logo { display: flex; align-items: center; gap: 11px; }
.auth-logo img { object-fit: contain; display: block; }
.auth-mark {
  width: 42px; height: 42px; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--au-grad); color: var(--au-on);
  box-shadow: 0 6px 18px var(--au-glow);
}
.auth-wordmark {
  font-size: 16.5px; font-weight: 750; letter-spacing: -.015em; color: var(--text);
}
.auth-logo-sm { justify-content: center; margin-bottom: 18px; }
.auth-logo-sm .auth-mark { width: 38px; height: 38px; border-radius: 11px; }

/* ── the form column ──────────────────────────────────────────────────── */
.auth-main {
  display: flex; align-items: center; justify-content: center;
  padding: 44px 20px 56px;
}
@media (min-width: 1024px) {
  /* The only scrolling thing on the page. */
  .auth-main {
    height: 100%;
    overflow-y: auto;
    overscroll-behavior: contain;
    align-items: flex-start;
    padding: 0 28px;
  }
  /* Centred when it fits, top-aligned when it does not — done with margin so
     it works inside a scroll container, which `justify-content: center` does
     not: that clips the top of anything taller than the pane. */
  .auth-col { margin: auto 0; padding: 44px 0 52px; }
}
.auth-col { width: 100%; max-width: 560px; }
.auth-col.is-tight { max-width: 424px; }

.auth-head { text-align: center; margin-bottom: 24px; }
.auth-head h1 {
  font-size: 27px; font-weight: 800; letter-spacing: -.028em;
  color: var(--au-ink); margin: 0 0 7px; line-height: 1.18;
}
.auth-head h1 em { font-style: normal; color: var(--au-text); }
.auth-sub {
  font-size: 13.5px; line-height: 1.6; color: var(--muted); margin: 0 auto; max-width: 44ch;
}

/* ── the card ─────────────────────────────────────────────────────────── */
.auth-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 24px 22px 22px;
  box-shadow: var(--shadow-1);
}

/* ── alerts ───────────────────────────────────────────────────────────── */
.auth-alert {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; margin-bottom: 18px;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface); font-size: 13px; line-height: 1.5;
}
.auth-alert svg { width: 16px; height: 16px; flex: 0 0 16px; margin-top: 1px; }
.auth-alert.is-ok { color: var(--success); }
.auth-alert.is-bad { color: var(--danger); }
.auth-alert span { color: var(--text); }

/* ── a numbered step ──────────────────────────────────────────────────── */
.auth-sec {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 20px 20px 6px;
  margin-bottom: 14px;
  box-shadow: var(--shadow-1);
}
.auth-sec-h {
  display: flex; align-items: flex-start; gap: 12px;
  padding-bottom: 14px; margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.auth-sec-h h2 {
  font-size: 15px; font-weight: 700; color: var(--text); margin: 0 0 3px; letter-spacing: -.012em;
}
.auth-sec-h p { font-size: 12.5px; line-height: 1.55; color: var(--muted); margin: 0; }
.auth-step {
  flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--au-soft); border: 1px solid var(--border);
  font-size: 12px; font-weight: 700; color: var(--au-text);
  transition: background var(--transition), color var(--transition);
}
.auth-step.is-done {
  background: var(--au-grad); border-color: transparent; color: var(--au-on);
  box-shadow: 0 3px 10px var(--au-glow);
}

/* ── fields ───────────────────────────────────────────────────────────── */
.auth-f { margin-bottom: 15px; min-width: 0; }
.auth-f > label {
  display: block; font-size: 12.5px; font-weight: 600; color: var(--text); margin-bottom: 6px;
}
.auth-f-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin-bottom: 6px;
}
.auth-f-row label { font-size: 12.5px; font-weight: 600; color: var(--text); }
.auth-req { color: var(--danger); }
.auth-opt { font-weight: 400; font-size: 11px; color: var(--muted); letter-spacing: .02em; }
.auth-in { position: relative; }
.auth-in-i {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--muted); pointer-events: none;
  transition: color var(--transition);
}
.auth-in input,
.auth-in select {
  width: 100%; height: 44px; padding: 0 13px 0 38px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font-size: 14px; font-family: inherit;
  transition: border-color var(--transition), background var(--transition);
}
.auth-in select { cursor: pointer; padding-right: 30px; }
.auth-in input::placeholder { color: var(--muted); opacity: .75; }
.auth-in input:hover,
.auth-in select:hover { border-color: var(--border-strong); }
.auth-in input:focus,
.auth-in select:focus { outline: none; border-color: var(--border-strong); background-color: var(--surface); }
/* The icon picks up the accent while the field is being used — colour where
   the eye already is, and no ring on the edge. */
.auth-in:focus-within .auth-in-i { color: var(--au-text); }
/* Room for the reveal button on a password field. */
.auth-in.has-eye input { padding-right: 44px; }
.auth-eye {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--radius-sm); background: none;
  color: var(--muted); cursor: pointer; padding: 0;
  transition: color var(--transition), background var(--transition);
}
.auth-eye:hover { color: var(--au-text); background: var(--au-soft); }
.auth-eye svg { width: 16px; height: 16px; }

/* A field in error keeps its neutral edge; the sentence under it carries the
   message, and the fill shifts just enough to find the row again. */
.auth-f.is-bad .auth-in input,
.auth-f.is-bad .auth-in select { background: var(--surface2); }
.auth-msg {
  display: none;
  font-size: 12px; line-height: 1.45; color: var(--danger);
  margin: 6px 0 0; font-weight: 550;
}
.auth-f.is-bad .auth-msg { display: block; }

/* ── two up ───────────────────────────────────────────────────────────────
   The form alternates left and right rather than running down one column,
   which halves how far anyone has to scroll. */
.auth-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
.auth-pair > .auth-f { min-width: 0; }
@media (max-width: 620px) { .auth-pair { grid-template-columns: 1fr; } }
/* Kept for older markup. */
.auth-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
@media (max-width: 620px) { .auth-grid { grid-template-columns: 1fr; } }

/* ── the phone row ────────────────────────────────────────────────────── */
.auth-phone { display: grid; grid-template-columns: 116px 1fr; gap: 8px; }
.auth-phone select,
.auth-phone input {
  width: 100%; height: 44px; padding: 0 13px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background-color: var(--surface); color: var(--text);
  font-size: 14px; font-family: inherit;
  transition: border-color var(--transition);
}
.auth-phone select { cursor: pointer; font-size: 13px; }
.auth-phone input::placeholder { color: var(--muted); opacity: .75; }
.auth-phone select:hover,
.auth-phone input:hover { border-color: var(--border-strong); }
.auth-phone select:focus,
.auth-phone input:focus { outline: none; border-color: var(--border-strong); }
.auth-f.is-bad .auth-phone input { background: var(--surface2); }
@media (max-width: 380px) {
  .auth-phone { grid-template-columns: 104px 1fr; gap: 6px; }
  .auth-phone select { font-size: 12.5px; padding: 0 8px; }
}

/* ── verification panel ───────────────────────────────────────────────── */
.auth-otp {
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--au-soft) 0%, transparent 100%), var(--surface2);
  padding: 15px 15px 16px;
  margin-bottom: 16px;
}
/* One state at a time. A panel showing "send" and "verify" together asks the
   person to work out which one they are on. */
.auth-otp > div { display: none; }
.auth-otp[data-state="idle"] .auth-otp-idle,
.auth-otp[data-state="done"] .auth-otp-done { display: flex; }
.auth-otp[data-state="sent"] .auth-otp-sent { display: block; }

.auth-otp-idle,
.auth-otp-done { align-items: center; gap: 12px; flex-wrap: wrap; }
.auth-otp-copy {
  flex: 1 1 210px; min-width: 0; padding-right: 4px;
  display: flex; flex-direction: column; gap: 3px;
}
.auth-otp-copy strong { font-size: 13px; font-weight: 650; color: var(--text); }
.auth-otp-copy span { font-size: 12px; line-height: 1.5; color: var(--muted); }
.auth-otp-l {
  display: block; font-size: 12.5px; font-weight: 600; color: var(--text); margin-bottom: 8px;
}
.auth-otp-row { display: flex; gap: 8px; }
.auth-otp-row input {
  flex: 1 1 auto; min-width: 0; height: 42px; padding: 0 14px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font-size: 17px; font-weight: 600; letter-spacing: .4em; font-family: inherit;
  transition: border-color var(--transition);
}
.auth-otp-row input::placeholder { letter-spacing: .3em; color: var(--muted); opacity: .6; }
.auth-otp-row input:focus { outline: none; border-color: var(--border-strong); }
.auth-otp-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-top: 10px;
}
.auth-tick {
  flex: 0 0 28px; width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--au-grad); color: var(--au-on);
  box-shadow: 0 3px 10px var(--au-glow);
}
.auth-tick svg { width: 15px; height: 15px; }
.auth-otp-msg { font-size: 12px; line-height: 1.5; color: var(--muted); margin: 10px 0 0; }
.auth-otp-msg:empty { display: none; }
.auth-otp-msg.is-ok { color: var(--success); }
.auth-otp-msg.is-bad { color: var(--danger); }

/* ── buttons ──────────────────────────────────────────────────────────── */
.auth-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 42px; padding: 0 18px;
  border: 1px solid transparent; border-radius: var(--radius);
  font-size: 13.5px; font-weight: 650; font-family: inherit;
  cursor: pointer; white-space: nowrap;
  transition: background var(--transition), color var(--transition),
              opacity var(--transition), box-shadow var(--transition);
}
.auth-btn svg { width: 15px; height: 15px; }
.auth-btn-dark {
  background: var(--au-grad); color: var(--au-on);
  box-shadow: 0 4px 14px var(--au-glow);
}
.auth-btn-dark:hover { opacity: .92; }
.auth-btn-quiet { background: var(--surface); color: var(--text); border-color: var(--border); }
.auth-btn-quiet:hover { background: var(--surface2); }
.auth-btn:disabled { opacity: .5; cursor: default; box-shadow: none; }
/* Pale but still pressable: pressing it is how the person finds out what is
   missing, and a button nobody can press cannot explain itself. */
.auth-btn-dark.is-locked {
  background: var(--surface); color: var(--muted); border-color: var(--border);
  box-shadow: none;
}
.auth-btn-dark.is-locked:hover { background: var(--surface2); opacity: 1; }

.auth-link {
  background: none; border: 0; padding: 0;
  font-size: 12.5px; font-weight: 600; font-family: inherit;
  color: var(--au-text); cursor: pointer; text-decoration: none;
  border-bottom: 1px solid var(--border-strong);
}
.auth-link:hover { border-bottom-color: var(--au-1); }
.auth-link:disabled { color: var(--muted); border-bottom-color: transparent; cursor: default; }

/* ── the main action ──────────────────────────────────────────────────── */
.auth-go {
  width: 100%; height: 48px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid transparent; border-radius: var(--radius);
  background: var(--au-grad); color: var(--au-on);
  font-size: 15px; font-weight: 700; font-family: inherit;
  cursor: pointer; margin-top: 4px;
  box-shadow: 0 8px 22px var(--au-glow);
  transition: background var(--transition), color var(--transition),
              border-color var(--transition), box-shadow var(--transition), opacity var(--transition);
}
.auth-go svg { width: 16px; height: 16px; }
.auth-go:hover { opacity: .93; box-shadow: 0 10px 28px var(--au-glow); }
.auth-go.is-locked {
  background: var(--surface2); color: var(--muted); border-color: var(--border);
  box-shadow: none;
}
.auth-go.is-locked:hover { background: var(--surface3); opacity: 1; box-shadow: none; }
.auth-go-note { font-size: 12px; color: var(--muted); text-align: center; margin: 10px 0 0; }

/* ── footers ──────────────────────────────────────────────────────────── */
.auth-foot { text-align: center; font-size: 13px; color: var(--muted); margin: 20px 0 0; }
.auth-foot a {
  color: var(--au-text); font-weight: 650; text-decoration: none;
  border-bottom: 1px solid var(--border-strong);
}
.auth-foot a:hover { border-bottom-color: var(--au-1); }
.auth-back {
  position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--au-text); font-weight: 650; text-decoration: none; font-size: 13px;
  border-bottom: 1px solid var(--border-strong);
}
.auth-back:hover { border-bottom-color: var(--au-1); }
.auth-back svg { width: 14px; height: 14px; }

/* A line of text is about 17px tall, which is not something anyone hits
   reliably with a thumb. The hit area is grown past the ink instead of
   padding the element, which would push the underline away from the words. */
.auth-back::after,
.auth-link::after,
.auth-foot a::after,
.auth-aside-foot a::after {
  content: ""; position: absolute;
  left: -8px; right: -8px; top: -12px; bottom: -12px;
}
.auth-link, .auth-foot a, .auth-aside-foot a { position: relative; }
.auth-secure {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: 12px; color: var(--muted); margin: 13px 0 0;
  text-align: center; line-height: 1.5;
}
.auth-secure svg { width: 12px; height: 12px; flex: 0 0 12px; }

/* ── a note the page has to answer ────────────────────────────────────── */
.auth-modal {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(16, 24, 40, .5);
}
.auth-modal[hidden] { display: none; }
.auth-modal-card {
  width: 100%; max-width: 400px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 18px; padding: 26px 24px 22px;
  box-shadow: var(--shadow-3);
  text-align: center;
}
.auth-modal-i {
  width: 46px; height: 46px; border-radius: 13px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--au-soft); border: 1px solid var(--border);
  color: var(--au-text); margin-bottom: 14px;
}
.auth-modal-i svg { width: 20px; height: 20px; }
.auth-modal-card h2 {
  font-size: 17px; font-weight: 700; color: var(--text); margin: 0 0 8px; letter-spacing: -.012em;
}
.auth-modal-card p { font-size: 13px; line-height: 1.6; color: var(--muted); margin: 0 0 18px; }
/* What is still missing, named rather than counted. */
.auth-modal-list {
  list-style: none; margin: 0 0 18px; padding: 12px 14px;
  background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius);
  text-align: left;
  max-height: 34vh; overflow-y: auto;
}
.auth-modal-list li {
  font-size: 12.5px; line-height: 1.6; color: var(--text);
  display: flex; align-items: center; gap: 8px;
}
.auth-modal-list li::before {
  content: ""; flex: 0 0 5px; width: 5px; height: 5px; border-radius: 50%;
  background: var(--au-1);
}
.auth-modal-acts { display: flex; gap: 9px; }
.auth-modal-acts .auth-btn { flex: 1 1 0; }

/* ── on a phone ───────────────────────────────────────────────────────────
   Below 1024px there is no split: the form is the page. These sizes keep it
   comfortable down to 320px without anything reaching an edge. */
@media (max-width: 1023px) {
  .auth-main { padding: 32px 18px 44px; }
  .auth-col, .auth-col.is-tight { max-width: 460px; }
}
@media (max-width: 620px) {
  .auth-main { padding: 26px 16px 40px; }
  .auth-head { margin-bottom: 20px; }
  .auth-head h1 { font-size: 23px; }
  .auth-sub { font-size: 13px; }
  .auth-card { padding: 20px 16px 18px; border-radius: 16px; }
  .auth-sec { padding: 18px 16px 4px; border-radius: 16px; }
  .auth-sec-h { gap: 10px; }
  .auth-otp { padding: 14px 13px; }
  /* A full-width action is easier to hit than a snug one. */
  .auth-otp-idle .auth-btn,
  .auth-otp-done .auth-link { width: 100%; }
  .auth-otp-copy { flex: 1 1 100%; }
  .auth-modal-card { padding: 22px 18px 18px; }
  .auth-modal-acts { flex-direction: column-reverse; }
}
@media (max-width: 380px) {
  .auth-main { padding: 22px 14px 36px; }
  .auth-head h1 { font-size: 21px; }
  .auth-card, .auth-sec { padding-left: 14px; padding-right: 14px; }
  .auth-go { height: 46px; font-size: 14.5px; }
  .auth-otp-row { flex-wrap: wrap; }
  .auth-otp-row input { flex: 1 1 100%; }
  .auth-otp-row .auth-btn { width: 100%; }
}
/* A short screen in landscape: let the pane scroll rather than clipping. */
@media (min-width: 1024px) and (max-height: 620px) {
  .auth-aside-in { padding: 32px 44px; justify-content: flex-start; overflow-y: auto; }
  .auth-points { gap: 13px; margin-bottom: 22px; }
  .auth-aside-h { margin-top: 20px; }
}


/* ── pages: errors-404, errors-generic, errors-maintenance ───────────────
   One shell for every error page, so a 403 and a 500 differ in what they say
   rather than in how much care went into them. Unscoped `.err-*` on purpose:
   these three pages ARE the family, exactly like the .auth-* pages, and
   forking them per page is how they drifted apart the first time.

   Nothing here carries a coloured edge. The status is in the words and the
   icon; a red outline round a 404 would say "you broke something", which is
   not what a mistyped link means. */
:where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]),
:where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]) *,
:where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]) *::before,
:where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]) *::after { box-sizing: border-box; margin: 0; padding: 0; }

:where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]) body {
  min-height: 100vh; min-height: 100svh;
  background: var(--bg); color: var(--text);
  font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.err-stage {
  position: relative;
  min-height: 100vh; min-height: 100svh;
  display: flex; align-items: center; justify-content: center;
  padding: 48px 24px;
  isolation: isolate;
  /* The glow is deliberately wider than the screen and the photo frames lean
     past their box; both are decoration, so they are clipped rather than
     allowed to put a sideways scrollbar on a page nobody wanted to be on. */
  overflow: hidden;
}

/* A single soft light behind the card. It is the only decoration on the page
   and it breathes slowly enough not to be noticed until you look. */
.err-glow {
  position: absolute; z-index: -1;
  top: 50%; left: 50%;
  width: min(760px, 100vw); aspect-ratio: 1;
  transform: translate(-50%, -58%);
  background: radial-gradient(circle at 50% 50%, var(--accent-soft) 0%, transparent 62%);
  animation: err-breathe 9s ease-in-out infinite;
  pointer-events: none;
}
@keyframes err-breathe {
  0%, 100% { opacity: .75; transform: translate(-50%, -58%) scale(1); }
  50%      { opacity: 1;   transform: translate(-50%, -58%) scale(1.09); }
}

.err-shell {
  position: relative;
  width: 100%; max-width: 520px;
  text-align: center;
  animation: err-rise .55s cubic-bezier(.22, 1, .36, 1) both;
}
@keyframes err-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* ── the words ───────────────────────────────────────────────────────── */

.err-code {
  font-size: 13px; font-weight: 800; letter-spacing: .16em;
  /* --muted, not --faint: this is the number someone reads out to support. */
  color: var(--muted); margin-bottom: 14px;
  font-variant-numeric: tabular-nums;
}
.err-title {
  font-size: clamp(1.5rem, 1.15rem + 1.6vw, 2rem);
  font-weight: 800; letter-spacing: -.028em; line-height: 1.2;
  color: var(--text); margin-bottom: 14px;
}
.err-text {
  font-size: 15px; line-height: 1.68; color: var(--muted);
  max-width: 42ch; margin: 0 auto;
}
/* The handler's own message, kept apart from the sentence we wrote, and only
   printed when it says something the headline did not. */
.err-detail {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 16px; padding: 9px 15px;
  background: var(--surface2); border-radius: 999px;
  font-size: 12.5px; line-height: 1.5; color: var(--muted);
  max-width: 100%; word-break: break-word;
}

.err-actions {
  display: flex; flex-wrap: wrap; gap: 10px;
  align-items: center; justify-content: center;
  margin-top: 30px;
}
.err-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 22px; border: none; border-radius: 12px;
  font-family: inherit; font-size: 14px; font-weight: 700;
  text-decoration: none; cursor: pointer;
  transition: transform .16s ease, opacity .16s ease, background .16s ease;
}
.err-btn:active { transform: translateY(1px); }
.err-btn-primary {
  background: var(--accent-fill); color: var(--accent-ink);
  box-shadow: 0 6px 20px var(--shadow-ink);
}
.err-btn-primary:hover { background: var(--accent-fill-hover); }
.err-btn-quiet {
  background: var(--surface2); color: var(--muted);
}
.err-btn-quiet:hover { background: var(--surface3); color: var(--text); }

.err-trace {
  margin-top: 28px; text-align: left;
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  overflow: hidden;
}
.err-trace summary {
  padding: 12px 16px; cursor: pointer;
  font-size: 12px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
.err-trace pre {
  padding: 0 16px 16px; margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px; line-height: 1.6; color: var(--muted);
  white-space: pre-wrap; word-break: break-word;
  max-height: 340px; overflow: auto;
}

/* ── the badge on everything except the 404 ──────────────────────────── */

.err-mark {
  position: relative;
  width: 84px; height: 84px; margin: 0 auto 26px;
  display: grid; place-items: center;
}
.err-mark-ring {
  position: absolute; inset: 0;
  border-radius: 26px; background: var(--surface);
  box-shadow: 0 10px 34px var(--shadow-ink);
  animation: err-tilt 7s ease-in-out infinite;
}
@keyframes err-tilt {
  0%, 100% { transform: rotate(-4deg); }
  50%      { transform: rotate(4deg); }
}
.err-mark-icon {
  position: relative; display: grid; place-items: center;
  width: 38px; height: 38px; color: var(--accent);
}
.err-mark-icon svg { width: 100%; height: 100%; }

.err-spin { animation: err-rotate 2.4s linear infinite; }
@keyframes err-rotate { to { transform: rotate(360deg); } }

.err-pulse {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent-fill); flex-shrink: 0;
  animation: err-blink 1.7s ease-in-out infinite;
}
@keyframes err-blink { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* ── the 404's missing photograph ────────────────────────────────────────
   Three frames, fanned and drifting out of step with one another, with the
   middle one empty. It reads as a lost picture without a caption, which is
   the one joke a photo-selection product has earned. */

.err-art {
  position: relative;
  height: 210px; margin-bottom: 26px;
  display: grid; place-items: center;
}

.err-404 {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: clamp(6.5rem, 4rem + 16vw, 11rem);
  font-weight: 900; letter-spacing: -.055em; line-height: 1;
  color: var(--text); opacity: .07;
  user-select: none;
}

.err-frames {
  position: relative;
  display: flex; align-items: flex-end; justify-content: center;
  gap: clamp(-14px, -2vw, 0px);
}

.err-frame {
  width: 108px; padding: 9px 9px 26px;
  background: var(--surface); border-radius: 10px;
  box-shadow: 0 14px 38px var(--shadow-ink);
  animation: err-float 6.5s ease-in-out infinite;
}
.err-frame-l { transform: rotate(-9deg) translateY(10px); animation-delay: -.9s; z-index: 1; margin-right: -16px; }
.err-frame-c { transform: rotate(1deg);                   animation-delay: -2.4s; z-index: 3; }
.err-frame-r { transform: rotate(9deg) translateY(10px);  animation-delay: -4.1s; z-index: 2; margin-left: -16px; }

/* Each frame keeps its own rotation while it drifts, so the fan holds its
   shape instead of collapsing to upright the moment the animation starts. */
@keyframes err-float {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -11px; }
}

.err-shot {
  position: relative; overflow: hidden;
  aspect-ratio: 4 / 3; border-radius: 5px;
  background: var(--surface3);
  display: grid; place-items: center;
}
.err-shot svg { width: 100%; height: 100%; fill: var(--surface); stroke: none; opacity: .85; }

.err-shot-empty {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--border-strong);
  background-image: repeating-linear-gradient(
    45deg, transparent 0 7px, var(--surface2) 7px 14px);
}
.err-q {
  font-size: 30px; font-weight: 900; color: var(--faint);
  animation: err-wink 3.2s ease-in-out infinite;
}
@keyframes err-wink { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }

/* ── stillness on request ────────────────────────────────────────────────
   Every animation here is decorative, so honouring the preference costs the
   page nothing at all. */
@media (prefers-reduced-motion: reduce) {
  :where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]) *,
  :where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]) *::before,
  :where([data-page="errors-404"], [data-page="errors-generic"], [data-page="errors-maintenance"]) *::after {
    animation: none !important;
    transition: none !important;
  }
}

@media (max-width: 480px) {
  .err-stage { padding: 32px 18px; }
  .err-art { height: 178px; }
  .err-frame { width: 88px; padding: 7px 7px 21px; }
  .err-actions .err-btn { flex: 1 1 100%; justify-content: center; }
}

/* ── pages: errors-face_disabled, errors-album_unavailable ───────────────
   from src/views/errors/face_disabled.ejs and errors/album_unavailable.ejs —
   the same card, so one set of rules covers both. */
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]),
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) *,
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) *::before,
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) *::after { box-sizing: border-box; margin: 0; padding: 0; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) body { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--bg); font-family: Inter, -apple-system, sans-serif; padding: 24px; -webkit-font-smoothing: antialiased; }
/* Subtle background texture */
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .card { position: relative; background: var(--surface); border: 1px solid var(--border); border-radius: 24px; padding: 52px 48px 44px; max-width: 480px; width: 100%; text-align: center; box-shadow: 0 32px 80px var(--shadow-ink), 0 0 0 1px rgba(255,255,255,.04) inset; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .icon-wrap { width: 72px; height: 72px; margin: 0 auto 28px; background: var(--accent-soft); border: 1px solid var(--border); border-radius: 20px; display: flex; align-items: center; justify-content: center; position: relative; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .icon-wrap::after { content: ''; position: absolute; inset: -1px; border-radius: 20px; background: var(--surface2); }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .icon-wrap svg { position: relative; z-index: 1; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; background: var(--accent-soft); border: 1px solid var(--border); border-radius: 100px; font-size: 11px; font-weight: 600; color: var(--accent); letter-spacing: .06em; text-transform: uppercase; margin-bottom: 18px; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .badge-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-fill); opacity: .7; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) h1 { font-size: 1.55rem; font-weight: 800; color:var(--text); letter-spacing: -.025em; line-height: 1.25; margin-bottom: 14px; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) p { font-size: 14.5px; color: var(--muted); line-height: 1.65; margin-bottom: 32px; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) p strong { color:var(--text); font-weight: 500; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .divider { height: 1px; background:var(--surface2); margin-bottom: 28px; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .info-row { display: flex; align-items: flex-start; gap: 10px; text-align: left; padding: 14px 16px; background:var(--surface2); border: 1px solid var(--border); border-radius: 12px; margin-bottom: 24px; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .info-icon { flex-shrink: 0; width: 20px; height: 20px; margin-top: 1px; color: var(--accent); }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .info-text { font-size: 13px; color: var(--muted); line-height: 1.55; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .info-text strong { display: block; color:var(--text); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; margin-bottom: 3px; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .btn-back { display: inline-flex; align-items: center; gap: 7px; padding: 11px 24px; background:var(--surface2); border: 1px solid var(--border); border-radius: 10px; color:var(--muted); font-size: 13.5px; font-weight: 600; font-family: inherit; cursor: pointer; text-decoration: none; transition: background .18s, border-color .18s, color .18s; }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .btn-back:hover { background:var(--surface2); border-color: var(--border); color:var(--text); }
:where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .footer-note { margin-top: 28px; font-size: 12px; color: var(--muted); }
@media (max-width: 520px) {
  :where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) .card { padding: 36px 24px 32px; }
  :where([data-page="errors-face_disabled"], [data-page="errors-album_unavailable"]) h1 { font-size: 1.3rem; }
}

/* ── page: flipbook-viewer ───────────────────────────────────────────────
   from src/views/flipbook/viewer.ejs */
/* ══ 3D Flipbook viewer ══════════════════════════════════════════════════
   A book, not a slideshow.

   Closed, only the cover is on screen and it sits in the middle of the stage —
   nothing beside it. Opening slides the book sideways so the spine lands on
   centre and both pages come into view; closing at the end brings the back
   cover back to the middle the same way.

   A page turn is a real rotateY about the spine, with the sheet lit on the way
   round and lifted above its neighbours while it travels. Pages can be dragged
   as well as clicked: pull right-to-left to go on, left-to-right to come back,
   and the sheet follows the finger the whole way. */
:root:where([data-page="flipbook-viewer"]) {
  --fb-ink: #ece8e0;
  --fb-dim: #8f8a80;
  --fb-line: rgba(255, 255, 255, .10);
  /* The stage. A white page needs something to be white against, so the
     ground is a warm pearl that falls away at the corners rather than a flat
     tint — the album keeps its edge without a black surround. */
  --fb-paper: #ddd9d2;
  --fb-stage-ink: #423b32;
  --fb-stage-dim: #7c7368;
  --fb-stage-line: rgba(66, 59, 50, .16);
  --fb-stage-veil: rgba(255, 255, 255, .72);
  /* The bands top and bottom: warm graphite, so the gold still carries and
     nothing on the page is black. */
  --fb-band: #1a1714;
}
:where([data-page="flipbook-viewer"]),
:where([data-page="flipbook-viewer"]) * { box-sizing: border-box; }
:where([data-page="flipbook-viewer"]) body.fb-body { margin: 0; min-height: 100vh; min-height: 100dvh; background: var(--fb-paper); color: var(--fb-ink); font-family: 'Inter', system-ui, -apple-system, sans-serif; display: flex; flex-direction: column; overflow: hidden; }
/* ── the bar across the top ───────────────────────────────────────────── */
:where([data-page="flipbook-viewer"]) .fb-bar { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 18px; padding: 13px 24px; border-bottom: 1px solid var(--fb-line); background: var(--fb-band); flex-shrink: 0; z-index: 20; }
:where([data-page="flipbook-viewer"]) .fb-brand { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
:where([data-page="flipbook-viewer"]) .fb-brand-top { display: flex; align-items: center; gap: 11px; min-width: 0; }
:where([data-page="flipbook-viewer"]) .fb-brand img { max-height: 38px; max-width: 160px; width: auto; display: block; }
:where([data-page="flipbook-viewer"]) .fb-brand-name { font-size: 19px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--fb-gold); white-space: nowrap; line-height: 1; }
:where([data-page="flipbook-viewer"]) .fb-contact { display: flex; align-items: center; gap: 13px; flex-wrap: wrap; }
:where([data-page="flipbook-viewer"]) .fb-social { display: flex; align-items: center; gap: 9px; }
:where([data-page="flipbook-viewer"]) .fb-social a { color: var(--fb-gold); opacity: .92; display: inline-flex; transition: opacity .15s; }
:where([data-page="flipbook-viewer"]) .fb-social a:hover { opacity: 1; }
:where([data-page="flipbook-viewer"]) .fb-line { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--fb-ink); opacity: .82; white-space: nowrap; }
:where([data-page="flipbook-viewer"]) .fb-line svg { color: var(--fb-gold); flex-shrink: 0; }
:where([data-page="flipbook-viewer"]) .fb-tools { display: flex; align-items: center; gap: 9px; }
:where([data-page="flipbook-viewer"]) .fb-btn { width: 40px; height: 40px; flex-shrink: 0; border: 1px solid var(--fb-line); border-radius: 10px; background: rgba(255, 255, 255, .05); color: var(--fb-ink); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background .15s, border-color .15s, color .15s; }
:where([data-page="flipbook-viewer"]) .fb-btn:hover { background: rgba(255, 255, 255, .11); border-color: rgba(255, 255, 255, .24); color: var(--fb-gold); }
:where([data-page="flipbook-viewer"]) .fb-btn[aria-pressed="true"] { color: var(--fb-gold); border-color: var(--fb-gold); }
:where([data-page="flipbook-viewer"]) .fb-btn .off { display: none; }
:where([data-page="flipbook-viewer"]) .fb-btn.is-off .on { display: none; }
:where([data-page="flipbook-viewer"]) .fb-btn.is-off .off { display: block; }
:where([data-page="flipbook-viewer"]) .fb-meta { text-align: right; min-width: 0; display: flex; flex-direction: column; gap: 5px; align-items: flex-end; }
:where([data-page="flipbook-viewer"]) .fb-meta .who { font-size: 17px; font-weight: 800; letter-spacing: .04em; color: var(--fb-gold); line-height: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
:where([data-page="flipbook-viewer"]) .fb-meta .when { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; justify-content: flex-end; }
/* ── the stage ────────────────────────────────────────────────────────── */
:where([data-page="flipbook-viewer"]) .fb-stage { flex: 1; position: relative; display: flex; align-items: center; justify-content: center; padding: 30px 74px; min-height: 0; perspective: 2400px; perspective-origin: 50% 46%; overflow: hidden; touch-action: pan-y; contain: layout paint; /* Static, so it costs one rasterisation for the life of the page; the movement all happens in the layers above it. */ background: radial-gradient(118% 88% at 50% 38%, #ebe8e3 0%, #ddd9d2 46%, #c8c4bb 100%); }
/* Soft lighting: two blurred colour fields drifting across each other at
   rates that do not divide evenly, so the pattern never visibly repeats.
   Each is rendered at a quarter size and scaled up — a 14px blur over a
   quarter-size element looks like a 56px blur over a full-size one and costs
   a sixteenth of the pixels to rasterise. */
:where([data-page="flipbook-viewer"]) .fb-smoke { position: absolute; inset: -30%; pointer-events: none; z-index: 0; opacity: .78; contain: strict; }
:where([data-page="flipbook-viewer"]) .fb-smoke i { position: absolute; top: 0; left: 0; width: 25%; height: 25%; transform-origin: 0 0; display: block; filter: blur(14px); will-change: transform; }
/* Blush, champagne and a cool blue held well below full strength: at this
   opacity they read as light falling on the wall, not as colour. */
:where([data-page="flipbook-viewer"]) .fb-smoke i.a { background: radial-gradient(38% 46% at 24% 32%, rgba(214, 178, 170, .30), transparent 62%), radial-gradient(34% 40% at 76% 60%, rgba(206, 192, 158, .24), transparent 66%), radial-gradient(44% 34% at 50% 88%, rgba(178, 196, 214, .38), transparent 60%); animation: fb-drift-a 52s ease-in-out infinite alternate; }
:where([data-page="flipbook-viewer"]) .fb-smoke i.b { background: radial-gradient(40% 44% at 70% 22%, rgba(196, 184, 206, .28), transparent 64%), radial-gradient(36% 42% at 26% 74%, rgba(212, 198, 176, .22), transparent 62%); animation: fb-drift-b 71s ease-in-out infinite alternate; }
@keyframes fb-drift-a {
  from { transform: scale(4) translate3d(-1%, .6%, 0) rotate(0deg); }
  to   { transform: scale(4.5) translate3d(1.4%, -1.1%, 0) rotate(6deg); }
}
@keyframes fb-drift-b {
  from { transform: scale(4.6) translate3d(1.2%, -.8%, 0) rotate(-4deg); }
  to   { transform: scale(4.1) translate3d(-1.5%, 1%, 0) rotate(3deg); }
}
/* Motes drifting down. There is no element per particle: each layer is one
   tile of dots repeated, and the layer slides down by exactly one tile before
   the animation restarts, so the loop has no seam. Two layers at different
   scales and speeds give the parallax that sells the depth — the whole effect
   is two composited layers moving on transform. */
:where([data-page="flipbook-viewer"]) .fb-snow { position: absolute; inset: 0; pointer-events: none; z-index: 1; contain: strict; }
:where([data-page="flipbook-viewer"]) .fb-snow i { position: absolute; left: 0; right: 0; /* top and height come from the layer: it must start exactly one tile above the stage and be one tile taller, so that after travelling one tile it still covers top and bottom. */ display: block; background-repeat: repeat; background-size: 240px 240px; animation-name: fb-fall; animation-timing-function: linear; animation-iteration-count: infinite; will-change: transform; }
:where([data-page="flipbook-viewer"]) .fb-snow i.a { background-image: radial-gradient(circle at 18% 12%, rgba(255,255,255,.95) 0 1.4px, transparent 2px), radial-gradient(circle at 63% 34%, rgba(255,255,255,.85) 0 1.1px, transparent 1.7px), radial-gradient(circle at 38% 71%, rgba(255,255,255,.92) 0 1.3px, transparent 1.9px), radial-gradient(circle at 87% 86%, rgba(255,255,255,.8) 0 1px, transparent 1.6px); top: -240px; height: calc(100% + 240px); animation-duration: 29s; opacity: .9; }
:where([data-page="flipbook-viewer"]) .fb-snow i.b { background-image: radial-gradient(circle at 44% 20%, rgba(255,255,255,.7) 0 2px, transparent 2.8px), radial-gradient(circle at 8% 58%, rgba(255,255,255,.62) 0 1.7px, transparent 2.4px), radial-gradient(circle at 78% 79%, rgba(255,255,255,.68) 0 2.2px, transparent 3px); background-size: 360px 360px; top: -360px; height: calc(100% + 360px); animation-name: fb-fall-b; animation-duration: 47s; opacity: .62; }
@keyframes fb-fall {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, 240px, 0); }
}
/* The far layer tiles at 360px, so it has to travel 360px to land back
   on itself — same trick, a different distance. */
@keyframes fb-fall-b {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, 360px, 0); }
}
/* ── the book ─────────────────────────────────────────────────────────── */
/* The book is always a full spread wide. Which half is on screen is decided
   by sliding it: closed on the cover it sits half a page right of centre,
   open it is centred, closed on the back it sits half a page left. */
:where([data-page="flipbook-viewer"]) .fb-book { position: relative; transform-style: preserve-3d; transition: transform .62s cubic-bezier(.3, .9, .25, 1); z-index: 5; }
/* A permanently promoted layer is sampled by the compositor rather than drawn
   by the normal path, and a composited 3D layer gets no edge antialiasing —
   which is why a settled album had torn edges. The hint goes on only while
   something is moving. */
:where([data-page="flipbook-viewer"]) .fb-book.is-turning,
:where([data-page="flipbook-viewer"]) .fb-stage.orbit .fb-book { will-change: transform; }
:where([data-page="flipbook-viewer"]) .fb-book.no-anim { transition: none; }
/* Orbit: the album held up and turned by hand. Page turning is suspended
   while it is on, because the same drag cannot mean two things. */
:where([data-page="flipbook-viewer"]) .fb-stage.orbit { cursor: grab; }
:where([data-page="flipbook-viewer"]) .fb-stage.orbit.grabbing { cursor: grabbing; }
:where([data-page="flipbook-viewer"]) .fb-stage.orbit .fb-nav { opacity: .25; pointer-events: none; }
:where([data-page="flipbook-viewer"]) .fb-orbit-hint { position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%); z-index: 16; display: none; align-items: center; gap: 12px; padding: 7px 14px; border-radius: 99px; border: 1px solid var(--fb-stage-line); background: var(--fb-stage-veil); backdrop-filter: blur(6px); font-size: 11.5px; color: var(--fb-stage-dim); white-space: nowrap; }
:where([data-page="flipbook-viewer"]) .fb-stage.orbit .fb-orbit-hint { display: flex; }
:where([data-page="flipbook-viewer"]) .fb-orbit-hint button { border: none; background: none; padding: 0; cursor: pointer; color: var(--fb-stage-ink); font: inherit; font-weight: 600; text-decoration: underline; }
/* The body of the book under the pages: one block per side, each shown only
   when that side has something on it. No empty panel next to a closed cover. */
:where([data-page="flipbook-viewer"]) .fb-slab { position: absolute; top: 0; height: 100%; width: 50%; /* No fill at all. This block exists to cast the album's shadow; the pages themselves cover every part of it that is ever meant to be seen. Giving it a colour meant that the instant a turn began — the moment has-left is set, before any page has landed there — a bare rectangle in that colour faded in beside the album and sat there for the whole turn. Dark, it read as a black slab; pale, as the white box in the report. Transparent, it cannot be seen at all, and box-shadow still draws. */ background: transparent; /* One soft warm shadow, the weight of a book on a table. The heavy black pair that used to be here faded in and out with each side as the album opened, which is exactly the effect that had to go. */ box-shadow: 0 20px 44px var(--shadow-ink); opacity: 0; transition: opacity .32s ease; pointer-events: none; }
:where([data-page="flipbook-viewer"]) .fb-slab.l { left: 0;  border-radius: 3px 1px 1px 3px; }
:where([data-page="flipbook-viewer"]) .fb-slab.r { right: 0; border-radius: 1px 3px 3px 1px; }
/* And nothing at all while a sheet is in the air. has-left is set the moment
   a turn begins, long before a page has landed on that side, so even a
   shadow cast from an empty half would read as a box appearing beside the
   album. The slabs come back once it settles. */
:where([data-page="flipbook-viewer"]) .fb-book.is-turning .fb-slab { opacity: 0; }
:where([data-page="flipbook-viewer"]) .fb-book.has-left  .fb-slab.l { opacity: 1; }
:where([data-page="flipbook-viewer"]) .fb-book.has-right .fb-slab.r { opacity: 1; }
/* A leaf hangs on the spine at the centre of the book. */
:where([data-page="flipbook-viewer"]) .fb-leaf { position: absolute; top: 0; left: 50%; width: 50%; height: 100%; transform-origin: left center; transform-style: preserve-3d; transition: transform .82s cubic-bezier(.42, .04, .28, 1); /* A compositor layer per leaf would cost tens of megabytes of GPU memory for an album that only ever animates one sheet at a time, so the hint goes on the sheet in motion and comes off again when it lands. */ contain: paint; }
:where([data-page="flipbook-viewer"]) .fb-leaf.moving,
:where([data-page="flipbook-viewer"]) .fb-leaf.dragging { will-change: transform; }
:where([data-page="flipbook-viewer"]) .fb-leaf.turned { transform: rotateY(-180deg); }
:where([data-page="flipbook-viewer"]) .fb-leaf.dragging { transition: none; }
/* A sheet in motion rides above everything on either side of it. */
:where([data-page="flipbook-viewer"]) .fb-leaf.moving { z-index: 900 !important; }
:where([data-page="flipbook-viewer"]) .fb-face { position: absolute; inset: 0; backface-visibility: hidden; -webkit-backface-visibility: hidden; background: #efeae3 center center / cover no-repeat; overflow: hidden; }
:where([data-page="flipbook-viewer"]) .fb-face.back { transform: rotateY(180deg); }
/* A cover the studio did not supply is blank board, not the first photograph
   pressed into service. */
:where([data-page="flipbook-viewer"]) .fb-face.blank { background: #f6f5f2; }
:where([data-page="flipbook-viewer"]) .fb-face.blank::after { opacity: .35; }
/* A whisper of shading at the spine, enough that a sheet reads as paper with
   thickness rather than a flat sticker. It was three times this dark, which
   on a lit stage looked like a smudge travelling with the page. */
:where([data-page="flipbook-viewer"]) .fb-face::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(90deg, rgba(48,40,32,.13) 0%, rgba(48,40,32,.04) 12%, rgba(0,0,0,0) 30%); }
:where([data-page="flipbook-viewer"]) .fb-face.back::after { background: linear-gradient(270deg, rgba(48,40,32,.13) 0%, rgba(48,40,32,.04) 12%, rgba(0,0,0,0) 30%); }
/* A sheet in motion used to be washed with black, deepest when it was
   side-on. Removed: the album opens and closes clean. */
/* ── paging ───────────────────────────────────────────────────────────── */
:where([data-page="flipbook-viewer"]) .fb-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 46px; height: 46px; border: 1px solid var(--fb-stage-line); border-radius: 50%; background: var(--fb-stage-veil); color: var(--fb-stage-ink); backdrop-filter: blur(6px); box-shadow: 0 2px 10px var(--shadow-ink); display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 15; transition: background .15s, opacity .15s, color .15s; }
:where([data-page="flipbook-viewer"]) .fb-nav:hover { background: #fff; color: var(--fb-stage-ink); }
:where([data-page="flipbook-viewer"]) .fb-nav[disabled] { opacity: .28; cursor: default; }
:where([data-page="flipbook-viewer"]) .fb-prev { left: 16px; }
:where([data-page="flipbook-viewer"]) .fb-next { right: 16px; }
/* ── progress ─────────────────────────────────────────────────────────── */
/* The album sits between two bands rather than on one: the same graphite top
   and bottom frames the lit stage and keeps the gold legible in both. */
:where([data-page="flipbook-viewer"]) .fb-foot { flex-shrink: 0; padding: 10px 20px 14px; display: flex; align-items: center; justify-content: center; gap: 14px; font-size: 11.5px; color: var(--fb-dim); background: var(--fb-band); border-top: 1px solid var(--fb-line); }
:where([data-page="flipbook-viewer"]) .fb-count { font-variant-numeric: tabular-nums lining-nums; }
:where([data-page="flipbook-viewer"]) .fb-track { flex: 1; max-width: 420px; height: 3px; border-radius: 99px; background: rgba(255,255,255,.14); overflow: hidden; }
:where([data-page="flipbook-viewer"]) .fb-track span { display: block; height: 100%; background: var(--fb-gold); width: 0; transition: width .4s ease; }
:where([data-page="flipbook-viewer"]) .fb-count { font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (max-width: 760px) {
  :where([data-page="flipbook-viewer"]) .fb-bar { grid-template-columns: 1fr auto; gap: 10px; padding: 10px 13px; }
  :where([data-page="flipbook-viewer"]) .fb-meta { grid-column: 1 / -1; text-align: left; order: 3; align-items: flex-start; padding-top: 2px; }
  :where([data-page="flipbook-viewer"]) .fb-meta .when { justify-content: flex-start; }
  :where([data-page="flipbook-viewer"]) .fb-brand img { max-height: 28px; max-width: 108px; }
  :where([data-page="flipbook-viewer"]) .fb-brand-name { font-size: 14px; letter-spacing: .1em; }
  :where([data-page="flipbook-viewer"]) .fb-meta .who { font-size: 14px; }
  :where([data-page="flipbook-viewer"]) .fb-stage { padding: 16px 14px; perspective: 1500px; }
  :where([data-page="flipbook-viewer"]) .fb-nav { width: 38px; height: 38px; }
  :where([data-page="flipbook-viewer"]) .fb-prev { left: 4px; }
  :where([data-page="flipbook-viewer"]) .fb-next { right: 4px; }
  /* One page fills the book, so the leaf covers all of it. */
  :where([data-page="flipbook-viewer"]) .fb-leaf { left: 0; width: 100%; }
  :where([data-page="flipbook-viewer"]) .fb-slab { width: 100%; left: 0; right: auto; border-radius: 3px; }
}
@media (prefers-reduced-motion: reduce) {
  :where([data-page="flipbook-viewer"]) .fb-leaf,
:where([data-page="flipbook-viewer"]) .fb-book,
:where([data-page="flipbook-viewer"]) .fb-track span,
:where([data-page="flipbook-viewer"]) .fb-slab { transition: none; }
  :where([data-page="flipbook-viewer"]) .fb-smoke i,
:where([data-page="flipbook-viewer"]) .fb-snow i { animation: none; }
}
:where([data-page="flipbook-viewer"]) .fb-body:fullscreen .fb-stage { padding: 20px 74px; }

/* ── page: gallery-face_results ──────────────────────────────────────────
   from src/views/gallery/face_results.ejs */
:where([data-page="gallery-face_results"]),
:where([data-page="gallery-face_results"]) * { margin:0;padding:0;box-sizing:border-box }
:where([data-page="gallery-face_results"]) body { font-family:'Inter','Segoe UI',system-ui,-apple-system,sans-serif; background:var(--bg);color:var(--text);min-height:100dvh; }
/* ── HEADER ── */
:where([data-page="gallery-face_results"]) .pg-header { padding:16px 24px;border-bottom: 1px solid var(--border); display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px; background:rgba(15,15,26,.92);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px); position:sticky;top:0;z-index:100; }
:where([data-page="gallery-face_results"]) .pg-header-left { display:flex;align-items:center;gap:12px }
:where([data-page="gallery-face_results"]) .pg-logo { height:32px;max-width:160px;object-fit:contain }
:where([data-page="gallery-face_results"]) .pg-branding h1 { font-size:16px;font-weight:800;letter-spacing:-.01em;line-height:1.2 }
:where([data-page="gallery-face_results"]) .pg-branding .event-name { font-size:11px;color:var(--accent);font-weight:600;text-transform:uppercase;letter-spacing:.04em }
:where([data-page="gallery-face_results"]) .pg-actions { display:flex;align-items:center;gap:10px;flex-wrap:wrap }
:where([data-page="gallery-face_results"]) .pg-meta-pill { background:var(--surface2); border: 1px solid var(--border);border-radius:8px;padding:5px 12px; font-size:12px;font-weight:600;color:var(--accent);white-space:nowrap; }
:where([data-page="gallery-face_results"]) .pg-greeting { font-size:12px;color:var(--muted);white-space:nowrap }
:where([data-page="gallery-face_results"]) .dl-all-btn { display:inline-flex;align-items:center;gap:6px; padding:8px 18px;border-radius:9px;border:none; background:var(--accent-fill);color:var(--accent-ink); font-size:12px;font-weight:700;cursor:pointer;transition:.18s; box-shadow:0 4px 16px var(--shadow-ink);white-space:nowrap; font-family:inherit; }
:where([data-page="gallery-face_results"]) .dl-all-btn:hover { transform:translateY(-1px);box-shadow:0 6px 24px var(--shadow-ink) }
:where([data-page="gallery-face_results"]) .dl-all-btn:disabled { opacity:.5;cursor:not-allowed;transform:none }
:where([data-page="gallery-face_results"]) .dl-all-btn svg { flex-shrink:0 }
/* ── WELCOME ── */
:where([data-page="gallery-face_results"]) .pg-welcome { padding:20px 24px;text-align:center; background:var(--surface2); border-bottom: 1px solid var(--border); }
:where([data-page="gallery-face_results"]) .pg-welcome h2 { font-size:20px;font-weight:700;margin-bottom:4px }
:where([data-page="gallery-face_results"]) .pg-welcome p { font-size:13px;color:var(--muted) }
/* ── GRID ── */
:where([data-page="gallery-face_results"]) .pg-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:12px;padding:20px 24px; }
:where([data-page="gallery-face_results"]) .pg-card { border-radius:12px;overflow:hidden;background:var(--surface); border: 1px solid var(--border);transition:.2s;position:relative; }
:where([data-page="gallery-face_results"]) .pg-card:hover { border-color: var(--border);transform:translateY(-2px);box-shadow:0 8px 32px rgba(0,0,0,.3) }
:where([data-page="gallery-face_results"]) .pg-card img { width:100%;aspect-ratio:4/3;object-fit:cover;display:block;cursor:pointer }
:where([data-page="gallery-face_results"]) .pg-card .info { padding:10px 12px;display:flex;align-items:center;justify-content:space-between;gap:6px }
:where([data-page="gallery-face_results"]) .pg-card .fname { font-size:12px;font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0 }
:where([data-page="gallery-face_results"]) .pg-card .fsize { font-size:10px;color:var(--muted);white-space:nowrap }
:where([data-page="gallery-face_results"]) .pg-card .dl-btn { padding:4px 10px;border-radius:6px;border: 1px solid var(--border); background:var(--accent-soft);color:var(--accent);font-size:11px;font-weight:600; cursor:pointer;transition:.15s;text-decoration:none;display:inline-flex; align-items:center;gap:4px;white-space:nowrap;flex-shrink:0; }
:where([data-page="gallery-face_results"]) .pg-card .dl-btn:hover { background:var(--accent-line) }
/* ── LIGHTBOX ── */
:where([data-page="gallery-face_results"]) #lb { display:none;position:fixed;inset:0;z-index:9999;flex-direction:column;background:rgba(0,0,0,.95) }
:where([data-page="gallery-face_results"]) #lb.open { display:flex }
:where([data-page="gallery-face_results"]) #lb-top { display:flex;align-items:center;gap:8px;padding:0 16px;height:52px; background:rgba(255,255,255,.03);border-bottom: 1px solid var(--border);flex-shrink:0; }
:where([data-page="gallery-face_results"]) .lb-close-btn { background:none;border:none;color:rgba(255,255,255,.5);font-size:20px;cursor:pointer;padding:0 6px;transition:.13s;line-height:1 }
:where([data-page="gallery-face_results"]) .lb-close-btn:hover { color:#fff }
:where([data-page="gallery-face_results"]) #lb-title { flex:1;font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0 10px;color:rgba(255,255,255,.8) }
:where([data-page="gallery-face_results"]) #lb-body { flex:1;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;min-height:0 }
:where([data-page="gallery-face_results"]) #lb-img { max-width:95%;max-height:95%;object-fit:contain;border-radius:4px }
:where([data-page="gallery-face_results"]) #lb-prev,
:where([data-page="gallery-face_results"]) #lb-next { position:absolute;top:50%;transform:translateY(-50%); background:rgba(0,0,0,.55);border: 1px solid var(--border);color:#fff; font-size:24px;padding:12px 14px;border-radius:10px;cursor:pointer;z-index:10; transition:.15s;backdrop-filter:blur(6px); }
:where([data-page="gallery-face_results"]) #lb-prev:hover,
:where([data-page="gallery-face_results"]) #lb-next:hover { background:rgba(255,255,255,.15) }
:where([data-page="gallery-face_results"]) #lb-prev { left:16px }
:where([data-page="gallery-face_results"]) #lb-next { right:16px }
:where([data-page="gallery-face_results"]) #lb-bottom { flex-shrink:0;padding:10px 20px;height:52px;background:rgba(0,0,0,.6); border-top: 1px solid var(--border);display:flex;align-items:center;gap:12px; }
:where([data-page="gallery-face_results"]) #lb-idx { font-size:12px;color:rgba(255,255,255,.6);white-space:nowrap }
:where([data-page="gallery-face_results"]) #lb-spacer { flex:1 }
:where([data-page="gallery-face_results"]) #lb-dl { display:inline-flex;align-items:center;gap:6px; background:var(--success-solid);border:none; color:#fff;padding:9px 20px;border-radius:9px;font-size:13px;font-weight:700; cursor:pointer;transition:.15s;font-family:inherit;white-space:nowrap; box-shadow:0 4px 16px var(--shadow-ink); }
:where([data-page="gallery-face_results"]) #lb-dl:hover { filter:brightness(1.12);transform:translateY(-1px) }
/* ── DOWNLOAD PROGRESS TOAST ── */
:where([data-page="gallery-face_results"]) #dl-toast { position:fixed;bottom:24px;left:50%;transform:translateX(-50%); background:rgba(12,8,24,.95);border: 1px solid var(--border); color:#fff;padding:14px 24px;border-radius:12px;font-size:13px;font-weight:600; z-index:99999;display:none;align-items:center;gap:12px; box-shadow:0 12px 40px rgba(0,0,0,.6);backdrop-filter:blur(12px);min-width:280px; }
:where([data-page="gallery-face_results"]) #dl-toast.show { display:flex }
:where([data-page="gallery-face_results"]) #dl-toast-bar { height:3px;background:rgba(255,255,255,.1);border-radius:99px;flex:1;overflow:hidden;min-width:120px }
:where([data-page="gallery-face_results"]) #dl-toast-fill { height:100%;background:var(--accent-fill);border-radius:99px;transition:width .3s;width:0% }
:where([data-page="gallery-face_results"]) #dl-toast-text { white-space:nowrap }
:where([data-page="gallery-face_results"]) .pg-footer { padding:24px;text-align:center;font-size:11px;color:var(--muted);border-top: 1px solid var(--border) }
:where([data-page="gallery-face_results"]) .pg-empty { text-align:center;padding:60px 24px;color:var(--muted);font-size:14px }
@media(max-width:600px) {
  :where([data-page="gallery-face_results"]) .pg-header { padding:12px 14px }
  :where([data-page="gallery-face_results"]) .pg-grid { grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px;padding:12px 14px }
  :where([data-page="gallery-face_results"]) .dl-all-btn { padding:6px 12px;font-size:11px }
  :where([data-page="gallery-face_results"]) #lb-top { padding:0 10px;height:46px }
  :where([data-page="gallery-face_results"]) #lb-bottom { padding:8px 12px;height:46px }
}

/* ── page: gallery-face_search ───────────────────────────────────────────
   from src/views/gallery/face_search.ejs */
:where([data-page="gallery-face_search"]),
:where([data-page="gallery-face_search"]) * { margin:0;padding:0;box-sizing:border-box }
:where([data-page="gallery-face_search"]) body { font-family:'Inter','Segoe UI',system-ui,-apple-system,sans-serif; background:var(--bg); color:var(--text);min-height:100dvh;display:flex;flex-direction:column; }
:where([data-page="gallery-face_search"]) .fr-container { flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center; padding:20px;max-width:480px;margin:0 auto;width:100%; }
:where([data-page="gallery-face_search"]) .fr-header { text-align:center;margin-bottom:24px }
:where([data-page="gallery-face_search"]) .fr-logo { height:40px;max-width:200px;object-fit:contain;margin-bottom:10px }
:where([data-page="gallery-face_search"]) .fr-title { font-size:22px;font-weight:800;letter-spacing:-.02em;margin-bottom:4px;color:var(--text) }
:where([data-page="gallery-face_search"]) .fr-subtitle { font-size:13px;color:var(--muted);line-height:1.5 }
:where([data-page="gallery-face_search"]) .fr-event { font-size:11px;font-weight:600;color:var(--accent);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px }
/* Camera */
:where([data-page="gallery-face_search"]) .camera-wrap { position:relative;width:100%;max-width:360px;aspect-ratio:3/4; border-radius:20px;overflow:hidden;background:#111;margin-bottom:20px; border: 2px solid var(--border); box-shadow:0 8px 40px var(--shadow-ink); }
:where([data-page="gallery-face_search"]) .camera-wrap video { width:100%;height:100%;object-fit:cover;display:block }
:where([data-page="gallery-face_search"]) .camera-wrap canvas { display:none }
:where([data-page="gallery-face_search"]) .camera-wrap .captured-img { width:100%;height:100%;object-fit:cover;display:none; position:absolute;top:0;left:0;z-index:2; }
:where([data-page="gallery-face_search"]) .camera-overlay { position:absolute;top:0;left:0;right:0;bottom:0;z-index:3; display:flex;align-items:center;justify-content:center; pointer-events:none; }
:where([data-page="gallery-face_search"]) .face-guide { width:55%;aspect-ratio:3/4;border: 2px dashed var(--border);border-radius:50%; animation:pulse 2s ease-in-out infinite; }
@keyframes pulse {0%,100%{opacity:.4;transform:scale(1)}50%{opacity:.8;transform:scale(1.02)}}
:where([data-page="gallery-face_search"]) .camera-status { position:absolute;bottom:14px;left:50%;transform:translateX(-50%);z-index:4; font-size:11px;font-weight:600;color:#fff;background:rgba(0,0,0,.7); padding:4px 14px;border-radius:20px;white-space:nowrap; }
:where([data-page="gallery-face_search"]) .capture-btn { margin:0 auto 20px;display:flex;align-items:center;justify-content:center;gap:8px; padding:12px 32px;border-radius:50px;border:none;font-size:14px;font-weight:700; cursor:pointer;transition:.2s;letter-spacing:.01em; background:var(--accent-fill);color:var(--accent-ink); box-shadow:0 4px 20px var(--shadow-ink); }
:where([data-page="gallery-face_search"]) .capture-btn:hover { transform:translateY(-1px);box-shadow:0 6px 28px var(--shadow-ink) }
:where([data-page="gallery-face_search"]) .capture-btn:active { transform:scale(.97) }
:where([data-page="gallery-face_search"]) .capture-btn:disabled { opacity:.4;cursor:not-allowed;transform:none }
:where([data-page="gallery-face_search"]) .retake-btn { background:var(--danger-soft);color:var(--danger);border: 1px solid var(--border); font-size:12px;font-weight:600;padding:8px 20px;border-radius:10px;cursor:pointer; margin:0 auto 12px;display:none; }
/* Form */
:where([data-page="gallery-face_search"]) .fr-form { width:100%;max-width:380px }
:where([data-page="gallery-face_search"]) .fr-form .field { margin-bottom:14px }
:where([data-page="gallery-face_search"]) .fr-form label { display:block;font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin-bottom:5px }
:where([data-page="gallery-face_search"]) .fr-form input,
:where([data-page="gallery-face_search"]) .fr-form select { width:100%;padding:10px 14px;border: 1px solid var(--border); border-radius:10px;background:var(--surface2);color:var(--text); font-size:13px;font-family:inherit;transition:.2s;outline:none; }
:where([data-page="gallery-face_search"]) .fr-form input:focus,
:where([data-page="gallery-face_search"]) .fr-form select:focus { border-color: var(--border);box-shadow:0 0 0 3px var(--ring) }
:where([data-page="gallery-face_search"]) .phone-row { display:flex;gap:8px }
:where([data-page="gallery-face_search"]) .phone-row select { width:130px;flex-shrink:0 }
:where([data-page="gallery-face_search"]) .phone-row input { flex:1 }
:where([data-page="gallery-face_search"]) .submit-btn { width:100%;padding:14px;border-radius:12px;border:none;font-size:15px;font-weight:700; cursor:pointer;transition:.2s;letter-spacing:.01em; background:var(--success-solid);color:#fff; box-shadow:0 4px 20px var(--shadow-ink);margin-top:6px; }
:where([data-page="gallery-face_search"]) .submit-btn:hover { transform:translateY(-1px);box-shadow:0 6px 28px var(--shadow-ink) }
:where([data-page="gallery-face_search"]) .submit-btn:disabled { opacity:.5;cursor:not-allowed;transform:none }
/* Loading overlay */
:where([data-page="gallery-face_search"]) .search-overlay { position:fixed;top:0;left:0;right:0;bottom:0;z-index:9999; background:rgba(15,15,26,.95);display:none; flex-direction:column;align-items:center;justify-content:center; }
:where([data-page="gallery-face_search"]) .search-overlay.active { display:flex }
:where([data-page="gallery-face_search"]) .scanning-anim { width:160px;height:160px;border-radius:50%; border: 3px solid var(--border); position:relative;margin-bottom:24px; }
:where([data-page="gallery-face_search"]) .scanning-anim::after { content:'';position:absolute;top:-3px;left:-3px;right:-3px;bottom:-3px; border-radius:50%;border:3px solid transparent;border-top-color:var(--text); animation:spin 1s linear infinite; }
@keyframes spin {to{transform:rotate(360deg)}}
:where([data-page="gallery-face_search"]) .scanning-anim .face-icon { width:100%;height:100%;display:flex;align-items:center;justify-content:center; font-size:60px; }
:where([data-page="gallery-face_search"]) .search-text { font-size:18px;font-weight:700;color:#fff;margin-bottom:8px }
:where([data-page="gallery-face_search"]) .search-sub { font-size:13px;color:var(--muted);text-align:center;max-width:280px;line-height:1.5 }
/* Result overlay */
:where([data-page="gallery-face_search"]) .result-overlay { position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000; background:rgba(15,15,26,.97);display:none; flex-direction:column;align-items:center;justify-content:center;padding:24px; }
:where([data-page="gallery-face_search"]) .result-overlay.active { display:flex }
:where([data-page="gallery-face_search"]) .result-icon { font-size:64px;margin-bottom:16px }
:where([data-page="gallery-face_search"]) .result-title { font-size:20px;font-weight:800;color:var(--text);margin-bottom:6px;text-align:center }
:where([data-page="gallery-face_search"]) .result-msg { font-size:13px;color:var(--muted);margin-bottom:20px;text-align:center;line-height:1.5;max-width:320px }
:where([data-page="gallery-face_search"]) .result-link { display:inline-flex;align-items:center;gap:8px;padding:12px 28px; border-radius:12px;background:var(--accent-fill); color:#fff;font-size:14px;font-weight:700;text-decoration:none; box-shadow:0 4px 20px var(--shadow-ink);transition:.2s; }
:where([data-page="gallery-face_search"]) .result-link:hover { transform:translateY(-1px) }
:where([data-page="gallery-face_search"]) .result-wa { margin-top:10px;font-size:12px;color:var(--success);display:flex;align-items:center;gap:5px; }
:where([data-page="gallery-face_search"]) .fr-footer { padding:16px;text-align:center;font-size:11px;color:var(--muted) }
/* Camera error */
:where([data-page="gallery-face_search"]) .camera-error { display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px; text-align:center;padding:30px;font-size:13px;color:var(--danger); }
/* The second line explains how to carry on without a camera, and was set
   inline at 75% — which is the only part of this message that matters to
   somebody whose camera just failed. */
:where([data-page="gallery-face_search"]) .camera-error div { opacity:1 !important; color:var(--text); }

/* ── page: gallery-register ──────────────────────────────────────────────
   from src/views/gallery/register.ejs */
@keyframes gateSlideDown {
  from { opacity:0; transform:translateY(-28px) scale(.97); }
  to   { opacity:1; transform:translateY(0)      scale(1);  }
}
:where([data-page="gallery-register"]) .gate-card { animation: gateSlideDown .46s cubic-bezier(.22,.68,0,1.2) both; }
:where([data-page="gallery-register"]) .pin-error-box { display:flex;align-items:flex-start;gap:10px; background:var(--danger-soft);border: 1px solid var(--border); color:var(--danger);border-radius:10px;padding:14px 16px;margin-bottom:18px; font-size:13px;line-height:1.5;animation:gateSlideDown .3s ease both; }
:where([data-page="gallery-register"]) .pin-error-icon { font-size:18px;flex-shrink:0;margin-top:1px; }
:where([data-page="gallery-register"]) .pin-error-title { font-weight:700;margin-bottom:2px; }
:where([data-page="gallery-register"]) .pin-error-sub { font-size:12px;opacity:.8; }
/* ── Couple name typography ── */
:where([data-page="gallery-register"]) .couple-logo { display:flex; align-items:center; justify-content:center; margin-bottom:10px; }
:where([data-page="gallery-register"]) .couple-logo img { max-height:56px; max-width:140px; object-fit:contain; }
:where([data-page="gallery-register"]) .couple-logo-icon { font-size:32px; opacity:.6; }
/* Uses Cormorant Garamond italic for elegance — no cursive script */
:where([data-page="gallery-register"]) .couple-name { font-family: 'Cormorant Garamond', serif; font-style: italic; font-weight: 400; font-size: clamp(28px, 6vw, 52px); color: var(--text); text-align: center; line-height: 1.2; margin: 0 0 4px; letter-spacing: .04em; animation: gateSlideDown .5s .1s cubic-bezier(.22,.68,0,1.2) both; }
:where([data-page="gallery-register"]) .couple-sub { font-family: 'Cormorant Garamond', serif; font-size: 11px; letter-spacing: .32em; text-transform: uppercase; color: var(--muted); text-align: center; margin-bottom: 22px; animation: gateSlideDown .5s .15s cubic-bezier(.22,.68,0,1.2) both; }

/* ── page: gallery-revoked ───────────────────────────────────────────────
   from src/views/gallery/revoked.ejs */
:where([data-page="gallery-revoked"]),
:where([data-page="gallery-revoked"]) *,
:where([data-page="gallery-revoked"]) *::before,
:where([data-page="gallery-revoked"]) *::after { box-sizing:border-box;margin:0;padding:0 }
:root:where([data-page="gallery-revoked"]) { --bg:#0b0d12; --surface:#13161f; --border:rgba(255,255,255,.08); --text:#f0f2f8; --muted:rgba(240,242,248,.62); --accent:#ef4444; --accent2:#f97316; }
html:where([data-page="gallery-revoked"]),
:where([data-page="gallery-revoked"]) body { height:100%; background:var(--bg); color:var(--text); font-family:'Inter',sans-serif; overflow-x:hidden; }
:where([data-page="gallery-revoked"]) .page { min-height:100vh; display:flex; flex-direction:column; align-items:center; justify-content:center; padding:40px 20px; position:relative; z-index:1; }
:where([data-page="gallery-revoked"]) .card { background:var(--surface); border:1px solid var(--border); border-radius:24px; padding:52px 44px; max-width:540px; width:100%; text-align:center; box-shadow:0 32px 80px rgba(0,0,0,.6); animation:fadeUp .55s cubic-bezier(.22,1,.36,1) both; }
@keyframes fadeUp {from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}
:where([data-page="gallery-revoked"]) .icon-ring { width:88px;height:88px; border-radius:50%; background:rgba(239,68,68,.09); border: 1.5px solid var(--border); display:flex;align-items:center;justify-content:center; margin:0 auto 28px; font-size:36px; animation:revokedPulse 2.8s ease-in-out infinite; }
@keyframes revokedPulse {
  0%,100%{box-shadow:0 0 0 0 var(--shadow-ink)}
  50%{box-shadow:0 0 0 14px var(--ring)}
}
:where([data-page="gallery-revoked"]) .badge { display:inline-flex;align-items:center;gap:6px; background:rgba(239,68,68,.1); border: 1px solid var(--border); color:#f87171; font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase; padding:4px 12px;border-radius:20px; margin-bottom:20px; }
:where([data-page="gallery-revoked"]) .headline { font-family:'Cormorant Garamond',Georgia,serif; font-size:clamp(26px,5.5vw,38px); font-weight:600; line-height:1.15; color:var(--text); margin-bottom:14px; }
:where([data-page="gallery-revoked"]) .sub { font-size:14px; line-height:1.75; color:var(--muted); margin-bottom:28px; }
:where([data-page="gallery-revoked"]) .sub strong { color:rgba(240,242,248,.75);font-weight:500 }
:where([data-page="gallery-revoked"]) .divider { border:none; border-top:1px solid var(--border); margin:28px 0; }
:where([data-page="gallery-revoked"]) .steps { text-align:left; display:flex;flex-direction:column;gap:12px; margin-bottom:32px; }
:where([data-page="gallery-revoked"]) .step { display:flex;align-items:flex-start;gap:12px; background:rgba(255,255,255,.03); border:1px solid var(--border); border-radius:12px; padding:13px 15px; }
:where([data-page="gallery-revoked"]) .step-num { flex-shrink:0; width:24px;height:24px;border-radius:50%; background:rgba(249,115,22,.12); border: 1px solid var(--border); color:#fb923c; font-size:11px;font-weight:700; display:flex;align-items:center;justify-content:center; }
:where([data-page="gallery-revoked"]) .step-title { font-size:13px;font-weight:600;color:var(--text);margin-bottom:2px }
:where([data-page="gallery-revoked"]) .step-desc { font-size:12px;color:var(--muted);line-height:1.5 }
/* ── CTA buttons ── */
:where([data-page="gallery-revoked"]) .btns { display:flex;align-items:center;justify-content:center; gap:12px;flex-wrap:wrap; }
:where([data-page="gallery-revoked"]) .cta-phone { display:inline-flex;align-items:center;gap:9px; background:var(--success-solid); color:#fff; font-size:14px;font-weight:700; padding:13px 28px; border-radius:12px; text-decoration:none; border:none;cursor:pointer; transition:opacity .15s,transform .15s; box-shadow:0 6px 24px var(--shadow-ink); }
:where([data-page="gallery-revoked"]) .cta-phone:hover { opacity:.88;transform:translateY(-2px) }
:where([data-page="gallery-revoked"]) .cta-back { display:inline-flex;align-items:center;gap:7px; color:var(--muted); font-size:13px;font-weight:500; text-decoration:none; transition:color .15s; background:rgba(255,255,255,.05); border:1px solid var(--border); border-radius:10px; padding:11px 20px; }
:where([data-page="gallery-revoked"]) .cta-back:hover { color:var(--text);border-color: var(--border) }
:where([data-page="gallery-revoked"]) .photographer-strip { display:flex;align-items:center;justify-content:center;gap:10px; background:rgba(255,255,255,.03); border:1px solid var(--border); border-radius:10px; padding:11px 16px; margin-bottom:24px; font-size:13px; color:var(--muted); }
:where([data-page="gallery-revoked"]) .photographer-strip strong { color:var(--text);font-weight:600 }
/* ── Footer ── */
:where([data-page="gallery-revoked"]) .footer { margin-top:32px; font-size:12px; color:rgba(240,242,248,.5); text-align:center; line-height:1.6; }

/* ── page: install-installer ─────────────────────────────────────────────
   from src/views/install/installer.ejs */
:where([data-page="install-installer"]),
:where([data-page="install-installer"]) *,
:where([data-page="install-installer"]) *::before,
:where([data-page="install-installer"]) *::after { box-sizing:border-box;margin:0;padding:0 }
/* Colours come from public.css. This block used to redefine them as
   themselves (--accent: var(--accent)) — a cycle, which CSS treats as invalid —
   and pointed the page and card backgrounds at the accent, so the installer
   rendered with no working colours at all. Only the card radius is its own. */
:root:where([data-page="install-installer"]) { --radius:12px }
:where([data-page="install-installer"]) body { font-family:Inter,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px }
:where([data-page="install-installer"]) .installer { width:100%;max-width:680px }
:where([data-page="install-installer"]) .logo { text-align:center;margin-bottom:36px }
:where([data-page="install-installer"]) .logo h1 { font-size:28px;font-weight:700;background:var(--accent-fill);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;display:inline-flex;align-items:center;gap:10px }
:where([data-page="install-installer"]) .logo h1 svg { stroke:var(--accent) }
:where([data-page="install-installer"]) .logo p { color:var(--muted);margin-top:6px;font-size:14px }
:where([data-page="install-installer"]) .steps { display:flex;margin-bottom:28px;border-radius:var(--radius);overflow:hidden;border:1px solid var(--border) }
:where([data-page="install-installer"]) .step-tab { flex:1;padding:12px 6px;text-align:center;font-size:12px;font-weight:600;color:var(--muted);border-right:1px solid var(--border);background:var(--surface) }
:where([data-page="install-installer"]) .step-tab:last-child { border-right:none }
:where([data-page="install-installer"]) .step-tab.active { background:var(--surface2);color:var(--accent) }
:where([data-page="install-installer"]) .step-tab.done { color:var(--success) }
:where([data-page="install-installer"]) .card { background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:32px }
:where([data-page="install-installer"]) .card h2 { font-size:20px;font-weight:700;margin-bottom:8px }
:where([data-page="install-installer"]) .card p.subtitle { color:var(--muted);font-size:14px;margin-bottom:24px;line-height:1.6 }
:where([data-page="install-installer"]) .alert { border-radius:8px;padding:14px 16px;margin-bottom:20px;font-size:14px;line-height:1.6;display:flex;gap:10px;align-items:flex-start }
:where([data-page="install-installer"]) .alert-error { background:var(--danger-soft);border: 1px solid var(--border);color:var(--danger) }
:where([data-page="install-installer"]) .alert-success { background:var(--success-soft);border: 1px solid var(--border);color:var(--success) }
:where([data-page="install-installer"]) .alert-warn { background:var(--warning-soft);border: 1px solid var(--border);color:var(--warning) }
:where([data-page="install-installer"]) .form-group { margin-bottom:18px }
:where([data-page="install-installer"]) .form-group label { display:block;font-size:13px;font-weight:600;margin-bottom:6px }
:where([data-page="install-installer"]) .form-group .hint { font-size:12px;color:var(--muted);margin-top:5px }
:where([data-page="install-installer"]) .form-group input,
:where([data-page="install-installer"]) .form-group select { width:100%;background:var(--surface);border:1px solid var(--border-strong);color:var(--text);border-radius:8px;padding:10px 14px;font-size:14px;outline:none;transition:.2s;font-family:inherit }
:where([data-page="install-installer"]) .form-group input:focus,
:where([data-page="install-installer"]) .form-group select:focus { border-color: var(--border);box-shadow:0 0 0 3px var(--ring) }
:where([data-page="install-installer"]) .form-row { display:grid;grid-template-columns:1fr 1fr;gap:16px }
@media(max-width:560px) {
  :where([data-page="install-installer"]) .form-row { grid-template-columns:1fr }
  :where([data-page="install-installer"]) .card { padding:22px }
}
:where([data-page="install-installer"]) .btn { display:inline-flex;align-items:center;gap:8px;padding:12px 24px;border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;transition:.2s;text-decoration:none;font-family:inherit }
:where([data-page="install-installer"]) .btn-primary { background:var(--accent-fill);color:var(--accent-ink) }
:where([data-page="install-installer"]) .btn-primary:hover { opacity:.92;transform:translateY(-1px) }
:where([data-page="install-installer"]) .btn-primary[disabled] { opacity:.6;cursor:wait;transform:none }
:where([data-page="install-installer"]) .req-list { list-style:none;display:flex;flex-direction:column;gap:8px;margin-bottom:24px }
:where([data-page="install-installer"]) .req-item { display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:8px;font-size:13px;border:1px solid var(--border) }
:where([data-page="install-installer"]) .req-item.pass { border-color: var(--border);background:var(--success-soft) }
:where([data-page="install-installer"]) .req-item.fail { border-color: var(--border);background:var(--danger-soft) }
:where([data-page="install-installer"]) .req-item.warn { border-color: var(--border);background:var(--warning-soft) }
:where([data-page="install-installer"]) .req-item .ic { width:22px;height:22px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0 }
:where([data-page="install-installer"]) .pass .ic { background:var(--success-soft);color:var(--success) }
:where([data-page="install-installer"]) .fail .ic { background:var(--danger-soft);color:var(--danger) }
:where([data-page="install-installer"]) .warn .ic { background:var(--warning-soft);color:var(--warning) }
:where([data-page="install-installer"]) .req-detail { color:var(--muted);font-size:12px;margin-top:2px;word-break:break-all }
:where([data-page="install-installer"]) .muted { color:var(--muted);font-size:13px }
:where([data-page="install-installer"]) .kv { border:1px solid var(--border);border-radius:10px;overflow:hidden;margin:18px 0 }
:where([data-page="install-installer"]) .kv div { display:flex;justify-content:space-between;padding:11px 16px;border-bottom:1px solid var(--border);font-size:14px }
:where([data-page="install-installer"]) .kv div:last-child { border-bottom:none }
:where([data-page="install-installer"]) .note { border-left: 3px solid var(--border);background:var(--success-soft);padding:13px 15px;border-radius:0 8px 8px 0;margin-bottom:20px;font-size:14px;line-height:1.6 }
:where([data-page="install-installer"]) pre { background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:14px;font-size:12px;overflow:auto;max-height:260px;white-space:pre-wrap;word-break:break-all;margin-top:10px }
:where([data-page="install-installer"]) .lock { display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--success);background:var(--success-soft);border: 1px solid var(--border);border-radius:99px;padding:4px 12px;margin-bottom:14px }

/* ── page: install-upgrade ───────────────────────────────────────────────
   from src/views/install/upgrade.ejs */
/* Local names mapped onto public.css. --bg and --card used to point at
     --text (an ink, near-black) and --fg at the accent, so the page painted
     dark text on a dark card. */
:root:where([data-page="install-upgrade"]) { --card:var(--surface);--line:var(--border);--fg:var(--text);--mut:var(--muted);--ok:var(--success);--warn:var(--warning);--err:var(--danger);--acc:var(--accent) }
:where([data-page="install-upgrade"]),
:where([data-page="install-upgrade"]) * { box-sizing:border-box }
:where([data-page="install-upgrade"]) body { margin:0;background:var(--bg);color:var(--fg);font:15px/1.6 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;padding:40px 20px }
:where([data-page="install-upgrade"]) .wrap { max-width:760px;margin:0 auto }
:where([data-page="install-upgrade"]) .card { background:var(--card);border:1px solid var(--line);border-radius:12px;padding:28px;margin-bottom:18px }
:where([data-page="install-upgrade"]) h1 { margin:0 0 6px;font-size:24px;letter-spacing:-.02em }
:where([data-page="install-upgrade"]) h2 { margin:0 0 12px;font-size:16px }
:where([data-page="install-upgrade"]) .sub { color:var(--mut);margin:0 0 22px }
:where([data-page="install-upgrade"]) .row { display:flex;justify-content:space-between;padding:9px 0;border-bottom:1px solid var(--line) }
:where([data-page="install-upgrade"]) .row:last-child { border-bottom:0 }
:where([data-page="install-upgrade"]) .k { color:var(--mut) }
:where([data-page="install-upgrade"]) code { background:var(--surface2);border:1px solid var(--line);border-radius:4px;padding:2px 6px;font-size:13px }
:where([data-page="install-upgrade"]) ul { margin:10px 0 0;padding-left:20px }
:where([data-page="install-upgrade"]) li { margin:5px 0 }
:where([data-page="install-upgrade"]) .btn { display:inline-block;background:var(--acc);color:var(--accent-ink);border:0;border-radius:8px;padding:12px 22px;font-size:15px;font-weight:600;cursor:pointer;text-decoration:none }
:where([data-page="install-upgrade"]) .btn.sec { background:transparent;border:1px solid var(--line);color:var(--fg) }
:where([data-page="install-upgrade"]) .note { border-left:3px solid var(--acc);background:var(--accent-soft);padding:14px 16px;border-radius:0 8px 8px 0;margin:18px 0 }
:where([data-page="install-upgrade"]) .note.ok { border-color:var(--ok);background:var(--success-soft) }
:where([data-page="install-upgrade"]) .note.err { border-color:var(--err);background:var(--danger-soft) }
:where([data-page="install-upgrade"]) pre { background:var(--surface2);border:1px solid var(--line);border-radius:8px;padding:14px;overflow-x:auto;font-size:12.5px;line-height:1.55;margin:12px 0 0;max-height:340px }
:where([data-page="install-upgrade"]) .ok { color:var(--ok) }
:where([data-page="install-upgrade"]) .err { color:var(--err) }
:where([data-page="install-upgrade"]) .mut { color:var(--mut) }

/* ── page: gallery-viewer ────────────────────────────────────────────────
   from src/views/gallery/viewer.ejs */
:where([data-page="gallery-viewer"]), :where([data-page="gallery-viewer"]) * { box-sizing:border-box;margin:0;padding:0 }
html:where([data-page="gallery-viewer"]),
:where([data-page="gallery-viewer"]) body { background:#111;color:#fff;font-family:'Inter',system-ui,sans-serif;-webkit-font-smoothing:antialiased;overflow-x:hidden }
:where([data-page="gallery-viewer"]) img { display:block;max-width:100% }
:where([data-page="gallery-viewer"]) a { color:inherit;text-decoration:none }
/* ── HERO ── */
:where([data-page="gallery-viewer"]) #hero { position:relative;width:100%;height:100dvh;min-height:100vh;overflow:hidden;display:flex;align-items:center;justify-content:center }
:where([data-page="gallery-viewer"]) .hbg { position:absolute;inset:0;background:#0a0a14 center/cover no-repeat }
:where([data-page="gallery-viewer"]) .hcont { position:relative;z-index:10;display:flex;flex-direction:column;align-items:center;text-align:center;padding:32px;max-width:900px;width:100% }
:where([data-page="gallery-viewer"]) .hname { font-family:'Cormorant Garamond',serif;font-style:italic;font-weight:400;font-size:clamp(44px,10vw,96px);line-height:1.1;color:#fff;text-shadow:0 2px 40px rgba(0,0,0,.6);margin-bottom:12px }
:where([data-page="gallery-viewer"]) .hdate { font-size:12px;letter-spacing:.3em;text-transform:uppercase;color:rgba(255,255,255,.65);font-weight:500;margin-bottom:38px }
:where([data-page="gallery-viewer"]) .hbtn { display:inline-flex;align-items:center;gap:8px;border: 1px solid var(--border);background:rgba(255,255,255,.07);color:#fff;font-size:10px;letter-spacing:.25em;text-transform:uppercase;font-weight:700;padding:13px 38px;border-radius:2px;cursor:pointer;transition:.18s;backdrop-filter:blur(8px) }
:where([data-page="gallery-viewer"]) .hbtn:hover { background:rgba(255,255,255,.18) }
:where([data-page="gallery-viewer"]) .hbtn svg { width:13px;height:13px;animation:hvbd 1.6s ease-in-out infinite }
@keyframes hvbd {0%,100%{transform:translateY(0)}50%{transform:translateY(5px)}}
/* ── TOOLBAR ── */
:where([data-page="gallery-viewer"]) #tb { position:sticky;top:0;z-index:100;background:#0d0d0d;border-bottom: 1px solid var(--border);display:flex;align-items:center;padding:0 18px;height:52px;gap:0 }
:where([data-page="gallery-viewer"]) .tbtab { font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.62);cursor:pointer;padding:0 16px;border-bottom:2px solid transparent;height:100%;display:flex;align-items:center;gap:7px;transition:.15s;user-select:none;white-space:nowrap;flex-shrink:0 }
:where([data-page="gallery-viewer"]) .tbtab:hover { color:rgba(255,255,255,.6) }
:where([data-page="gallery-viewer"]) .tbtab.on { color:#fff;border-bottom-color:var(--p) }
:where([data-page="gallery-viewer"]) .tbbadge { background:var(--p);color:#fff;border-radius:99px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:800;display:none;align-items:center;justify-content:center }
:where([data-page="gallery-viewer"]) .tbbadge.show { display:inline-flex }
:where([data-page="gallery-viewer"]) .tbsp { flex:1 }
:where([data-page="gallery-viewer"]) .tbsub { background:var(--p);color:#fff;border:none;padding:9px 24px;border-radius:7px;font-size:13px;font-weight:700;cursor:pointer;transition:.15s;white-space:nowrap;letter-spacing:.02em }
:where([data-page="gallery-viewer"]) .tbsub:hover:not(:disabled) { filter:brightness(1.18) }
/* A real disabled style rather than a ghost of the enabled one. Fading the
   brand fill toward a dark background cannot reach a readable ratio at any
   opacity, and this button is what tells a customer what happens when they
   have finished choosing. */
:where([data-page="gallery-viewer"]) .tbsub:disabled {
  background:var(--surface2); color:var(--muted); opacity:1; cursor:not-allowed;
}
:where([data-page="gallery-viewer"]) .tball-dl { display:inline-flex;align-items:center;gap:5px;background:rgba(255,255,255,.06);border: 1px solid var(--border);color:rgba(255,255,255,.7);font-size:12px;font-weight:600;padding:7px 14px;border-radius:5px;transition:.15s;margin-right:10px }
:where([data-page="gallery-viewer"]) .tball-dl:hover { background:rgba(255,255,255,.13) }
/* ── FOLDER NAV ── */
:where([data-page="gallery-viewer"]) .ef-nav { padding:20px 15px 4px;display:flex;align-items:center;gap:10px;flex-wrap:wrap }
:where([data-page="gallery-viewer"]) .ef-nav-back { display:inline-flex;align-items:center;gap:5px;background:rgba(255,255,255,.07);border: 1px solid var(--border);color:rgba(255,255,255,.7);font-size:12px;font-weight:600;padding:8px 16px;border-radius:6px;cursor:pointer;transition:.15s }
:where([data-page="gallery-viewer"]) .ef-nav-back:hover { background:rgba(255,255,255,.14);color:#fff }
:where([data-page="gallery-viewer"]) .ef-nav-title { font-size:16px;font-weight:700;color:#fff }
:where([data-page="gallery-viewer"]) .ef-nav-count { font-size:12px;color:rgba(255,255,255,.35) }
:where([data-page="gallery-viewer"]) .ef-sel-all { display:inline-flex;align-items:center;gap:5px;background:rgba(99,102,241,.12);border: 1px solid var(--border);color:rgba(99,102,241,.9);font-size:12px;font-weight:700;padding:8px 16px;border-radius:6px;cursor:pointer;transition:.15s;margin-left:auto }
:where([data-page="gallery-viewer"]) .ef-sel-all:hover { background:rgba(99,102,241,.22) }
:where([data-page="gallery-viewer"]) .ef-folder-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:14px;padding:15px }
:where([data-page="gallery-viewer"]) .ef-fcard { background:rgba(255,255,255,.04);border: 1px solid var(--border);border-radius:12px;padding:22px 18px;cursor:pointer;transition:border-color .18s,transform .15s,box-shadow .18s;text-align:center }
:where([data-page="gallery-viewer"]) .ef-fcard:hover { border-color: var(--border);transform:translateY(-3px);box-shadow:0 8px 24px rgba(0,0,0,.3) }
:where([data-page="gallery-viewer"]) .ef-fcard-icon { margin-bottom:10px }
:where([data-page="gallery-viewer"]) .ef-fcard-name { font-size:14px;font-weight:700;color:#fff;margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis }
:where([data-page="gallery-viewer"]) .ef-fcard-count { font-size:11px;color:rgba(255,255,255,.35) }
/* ── GRID ── */
:where([data-page="gallery-viewer"]) #gwrap { width:100%;background:#0d0d0d;min-height:300px }
:root:where([data-page="gallery-viewer"]) { --gc:5 }
:where([data-page="gallery-viewer"]) .pgrid { display:grid;grid-template-columns:repeat(2,1fr);gap:15px;width:100%;padding:15px }
@media(min-width:540px) {
  :where([data-page="gallery-viewer"]) .pgrid { grid-template-columns:repeat(3,1fr) }
}
@media(min-width:900px) {
  :where([data-page="gallery-viewer"]) .pgrid { grid-template-columns:repeat(var(--gc),1fr) }
}
/* Grid size picker — 3-dot dropdown */
:where([data-page="gallery-viewer"]) .grid-dropdown { position:relative;margin-right:8px }
:where([data-page="gallery-viewer"]) .grid-dot-btn { display:inline-flex;align-items:center;justify-content:center;background:rgba(255,255,255,.08);border: 1px solid var(--border);color:rgba(255,255,255,.7);font-size:16px;font-weight:700;width:34px;height:34px;border-radius:7px;cursor:pointer;transition:.13s;font-family:'Inter',sans-serif;letter-spacing:1px;line-height:1;padding:0;flex-shrink:0 }
:where([data-page="gallery-viewer"]) .grid-dot-btn:hover { background:rgba(255,255,255,.16);color:#fff }
:where([data-page="gallery-viewer"]) .grid-dot-btn.active { background:rgba(91,142,240,.22);border-color:var(--p);color:var(--p) }
:where([data-page="gallery-viewer"]) .grid-menu { display:none;position:absolute;top:calc(100% + 6px);right:0;background:#1a1e2a;border: 1px solid var(--border);border-radius:10px;padding:5px;min-width:152px;box-shadow:0 12px 40px rgba(0,0,0,.7);z-index:500 }
:where([data-page="gallery-viewer"]) .grid-menu.open { display:block }
:where([data-page="gallery-viewer"]) .grid-menu-item { display:flex;align-items:center;justify-content:space-between;padding:9px 13px;border-radius:7px;cursor:pointer;transition:.12s;color:rgba(255,255,255,.8);font-size:13px;font-weight:600;gap:10px }
:where([data-page="gallery-viewer"]) .grid-menu-item:hover { background:rgba(255,255,255,.07);color:#fff }
:where([data-page="gallery-viewer"]) .grid-menu-item.sel { background:rgba(91,142,240,.15);color:var(--p) }
:where([data-page="gallery-viewer"]) .grid-menu-item .gmi-badge { font-size:10px;color:rgba(255,255,255,.3);font-weight:500 }
:where([data-page="gallery-viewer"]) .grid-menu-item.sel .gmi-badge { color:var(--p);opacity:.7 }
:where([data-page="gallery-viewer"]) .gc { position:relative;overflow:hidden;cursor:pointer;background:#1a1a1a;aspect-ratio:1;transition:all .12s }
:where([data-page="gallery-viewer"]) .gc img { width:100%;height:100%;object-fit:cover;transition:transform .26s ease;pointer-events:none }
:where([data-page="gallery-viewer"]) .gc:hover img { transform:scale(1.04) }
/* Selected state */
:where([data-page="gallery-viewer"]) .gc.gsel { outline:3px solid var(--p);outline-offset:-3px }
:where([data-page="gallery-viewer"]) .gc.gsel::after { content:'';position:absolute;inset:0;background:color-mix(in srgb,var(--p) 18%,transparent);z-index:2;pointer-events:none }
/* Check badge */
:where([data-page="gallery-viewer"]) .gchk { position:absolute;top:8px;right:8px;width:26px;height:26px;border-radius:50%;background:var(--p);color:#fff;font-size:12px;font-weight:800;display:none;align-items:center;justify-content:center;box-shadow:0 2px 12px rgba(0,0,0,.6);z-index:5;transition:transform .15s }
:where([data-page="gallery-viewer"]) .gc.gsel .gchk { display:flex }
:where([data-page="gallery-viewer"]) .gc:hover .gchk { transform:scale(1.1) }
/* Hover overlay — click hint */
:where([data-page="gallery-viewer"]) .ghov { position:absolute;inset:0;z-index:3;pointer-events:none;opacity:0;transition:.18s;display:flex;flex-direction:column }
:where([data-page="gallery-viewer"]) .ghov-top { flex:1;background:rgba(0,0,0,.5) }
:where([data-page="gallery-viewer"]) .ghov-bot { padding:8px 10px;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:space-between }
:where([data-page="gallery-viewer"]) .gc:hover .ghov { opacity:1 }
:where([data-page="gallery-viewer"]) .ghov-preview { font-size:10px;font-weight:600;color:rgba(255,255,255,.75);display:flex;align-items:center;gap:4px }
:where([data-page="gallery-viewer"]) .ghov-dbl { font-size:10px;font-weight:600;color:rgba(255,255,255,.5) }
/* number indicator */
:where([data-page="gallery-viewer"]) .gnum { position:absolute;top:8px;left:8px;background:rgba(0,0,0,.6);color:rgba(255,255,255,.5);font-size:10px;font-weight:600;padding:2px 6px;border-radius:4px;z-index:4;line-height:1.4 }
/* Empty state */
:where([data-page="gallery-viewer"]) .empty { display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:90px 24px;min-height:380px }
:where([data-page="gallery-viewer"]) .empty .ei { font-size:52px;opacity:.4;margin-bottom:16px }
:where([data-page="gallery-viewer"]) .empty h3 { font-size:18px;font-weight:700;color:rgba(255,255,255,.65);margin-bottom:8px }
:where([data-page="gallery-viewer"]) .empty p { color:rgba(255,255,255,.28);font-size:14px;max-width:300px;line-height:1.6 }
/* Submitted notice strip */
:where([data-page="gallery-viewer"]) .sub-strip { background:rgba(34,197,94,.07);border-bottom: 1px solid var(--border);padding:12px 20px;display:flex;align-items:center;gap:10px;font-size:13px }
/* ── LIGHTBOX ── */
:where([data-page="gallery-viewer"]) #lb { display:none;position:fixed;inset:0;z-index:9000;flex-direction:column;background:#000 }
:where([data-page="gallery-viewer"]) #lb.open { display:flex }
:where([data-page="gallery-viewer"]) #lbtop { display:flex;align-items:center;gap:8px;padding:0 14px;height:52px;background:rgba(255,255,255,.03);border-bottom: 1px solid var(--border);flex-shrink:0 }
:where([data-page="gallery-viewer"]) .lbb { background:rgba(255,255,255,.1);border:none;color:#fff;padding:6px 11px;border-radius:6px;cursor:pointer;font-size:12px;transition:.13s;line-height:1;white-space:nowrap;display:inline-flex;align-items:center;gap:5px;font-family:'Inter',sans-serif }
:where([data-page="gallery-viewer"]) .lbb:hover { background:rgba(255,255,255,.22) }
:where([data-page="gallery-viewer"]) .lbb.on { background:var(--p) }
:where([data-page="gallery-viewer"]) .lbx { background:none;border:none;color:rgba(255,255,255,.45);font-size:22px;cursor:pointer;line-height:1;padding:0 4px;transition:.13s;flex-shrink:0 }
:where([data-page="gallery-viewer"]) .lbx:hover { color:#fff }
:where([data-page="gallery-viewer"]) #lbtitle { flex:1;font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0 8px;color:rgba(255,255,255,.8) }
:where([data-page="gallery-viewer"]) #lbzoom { font-size:11px;color:rgba(255,255,255,.3);min-width:34px;text-align:center }
/* Main image area — takes available height */
:where([data-page="gallery-viewer"]) #lbmain { flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden }
:where([data-page="gallery-viewer"]) #lbbody { flex:1;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;cursor:zoom-in;user-select:none;min-height:0 }
:where([data-page="gallery-viewer"]) #lbbody.zoomed { cursor:grab }
:where([data-page="gallery-viewer"]) #lbimg { max-width:100%;max-height:100%;object-fit:contain;transform-origin:center;transition:transform .22s ease;pointer-events:none }
:where([data-page="gallery-viewer"]) #lbprev,
:where([data-page="gallery-viewer"]) #lbnext { position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,.55);border: 1px solid var(--border);color:#fff;font-size:24px;padding:11px 13px;border-radius:8px;cursor:pointer;z-index:10;transition:.15s;backdrop-filter:blur(6px) }
:where([data-page="gallery-viewer"]) #lbprev:hover,
:where([data-page="gallery-viewer"]) #lbnext:hover { background:rgba(255,255,255,.18) }
:where([data-page="gallery-viewer"]) #lbprev { left:14px }
:where([data-page="gallery-viewer"]) #lbnext { right:14px }
/* ── ALWAYS-VISIBLE INFO BAR below image ── */
:where([data-page="gallery-viewer"]) #lbinfo { flex-shrink:0;background:rgba(10,10,20,.92);border-top: 1px solid var(--border);padding:12px 20px;display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap;overflow-x:visible }
:where([data-page="gallery-viewer"]) .lbif { display:flex;flex-direction:column;align-items:center;gap:2px;text-align:center }
:where([data-page="gallery-viewer"]) .lbik { font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:rgba(255,255,255,.28) }
:where([data-page="gallery-viewer"]) .lbiv { font-size:12px;color:rgba(255,255,255,.82);font-weight:500;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap }
@media(max-width:600px) {
  :where([data-page="gallery-viewer"]) #lbinfo { gap:14px 18px;padding:10px 12px }
  :where([data-page="gallery-viewer"]) .lbiv { font-size:11px;max-width:140px }
}
/* Bottom: index · tick button */
:where([data-page="gallery-viewer"]) #lbbot { padding:0 16px;height:60px;background:rgba(0,0,0,.6);border-top: 1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0 }
:where([data-page="gallery-viewer"]) #lbidx { font-size:12px;color:rgba(255,255,255,.28);white-space:nowrap }
:where([data-page="gallery-viewer"]) #lbsp { flex:1 }
/* TICK / SELECT BUTTON */
:where([data-page="gallery-viewer"]) #lbtick { display:inline-flex;align-items:center;gap:10px;padding:10px 28px;border-radius:9px;font-size:13px;font-weight:700;cursor:pointer;border: 2px solid var(--border);background:rgba(255,255,255,.07);color:#fff;transition:.18s;white-space:nowrap;font-family:'Inter',sans-serif }
:where([data-page="gallery-viewer"]) #lbtick:hover { background:rgba(255,255,255,.12) }
:where([data-page="gallery-viewer"]) #lbtick.on { background:rgba(34,197,94,.12);border-color: var(--border);color:#4ade80 }
:where([data-page="gallery-viewer"]) .tickring { width:22px;height:22px;border:2px solid currentColor;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:12px;transition:.18s;flex-shrink:0 }
:where([data-page="gallery-viewer"]) #lbtick.on .tickring { background:#4ade80;border-color: var(--border);color:#000 }
:where([data-page="gallery-viewer"]) #lbdl { display:inline-flex;align-items:center;gap:5px;background:rgba(255,255,255,.07);border: 1px solid var(--border);color:#fff;padding:8px 16px;border-radius:7px;font-size:12px;font-weight:600;transition:.15s;white-space:nowrap }
:where([data-page="gallery-viewer"]) #lbdl:hover { background:rgba(255,255,255,.16) }
/* ── SUCCESS MODAL (full-screen premium overlay) ── */
:where([data-page="gallery-viewer"]) #modal { display:none;position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.92);align-items:center;justify-content:center;padding:20px;backdrop-filter:blur(14px) }
:where([data-page="gallery-viewer"]) #modal.open { display:flex }
@keyframes mpop {0%{transform:scale(0);opacity:0}100%{transform:scale(1);opacity:1}}
/* Progress bar */
/* ── FOOTER ── */
:where([data-page="gallery-viewer"]) .gfooter { text-align:center;padding:44px 24px 32px;background:#090909;border-top: 1px solid var(--border) }
:where([data-page="gallery-viewer"]) .gfl { width:44px;height:2px;background:var(--p);border-radius:99px;margin:0 auto 16px }
:where([data-page="gallery-viewer"]) .gfn { font-size:18px;font-weight:700;color:rgba(255,255,255,.85);letter-spacing:-.01em }
:where([data-page="gallery-viewer"]) .gfs { font-size:12px;color:rgba(255,255,255,.62);margin:5px 0 3px }
:where([data-page="gallery-viewer"]) .gfc { font-size:11px;color:rgba(255,255,255,.5) }
/* Toast */
:where([data-page="gallery-viewer"]) #toast { position:fixed;bottom:90px;left:50%;transform:translateX(-50%);background:rgba(12,6,28,.97);color:#fff;padding:12px 26px;border-radius:10px;font-size:14px;font-weight:600;z-index:99999;border: 1px solid var(--border);pointer-events:none;white-space:nowrap;opacity:0;transition:opacity .3s;font-family:'Inter',sans-serif }
@media(max-width:600px) {
  :where([data-page="gallery-viewer"]) #tb { padding:0 8px }
  :where([data-page="gallery-viewer"]) #tbgap { display:none }
  :where([data-page="gallery-viewer"]) .tbtab { padding:0 10px;font-size:10px }
  :where([data-page="gallery-viewer"]) .tbsub { padding:8px 14px;font-size:12px }
}
/* ── Watermark overlays ── */
:where([data-page="gallery-viewer"]) #lb-wm { position:absolute;pointer-events:none;z-index:15; object-fit:contain;max-width:30%;max-height:30%; transition:opacity .2s; }
/* ── Selection flash overlay ── */
:where([data-page="gallery-viewer"]) #lb-sel-flash { position:absolute;inset:0; border:4px solid transparent; border-radius:6px; pointer-events:none; display:flex;align-items:center;justify-content:center; opacity:0; transition:opacity .22s ease,border-color .22s ease; z-index:20; }
:where([data-page="gallery-viewer"]) #lb-sel-flash.show { opacity:1; border-color: var(--border); background:rgba(0,0,0,.18); }
:where([data-page="gallery-viewer"]) #lb-sel-flash.fade-out { opacity:0; transition:opacity .25s ease; }
:where([data-page="gallery-viewer"]) #lb-sel-flash-inner { display:flex;flex-direction:column;align-items:center;gap:10px; background:rgba(0,0,0,.60); backdrop-filter:blur(8px); border: 2px solid var(--border); border-radius:16px; padding:20px 32px; transform:scale(.8); transition:transform .22s cubic-bezier(.22,1,.36,1); }
:where([data-page="gallery-viewer"]) #lb-sel-flash.show #lb-sel-flash-inner { transform:scale(1); }
:where([data-page="gallery-viewer"]) #lb-sel-check { width:54px;height:54px; background:rgba(34,197,94,.2); border: 2px solid var(--border); border-radius:50%; display:flex;align-items:center;justify-content:center; font-size:26px;color:#4ade80; font-weight:700; box-shadow:0 0 20px var(--ring); }
:where([data-page="gallery-viewer"]) #lb-sel-label { font-size:16px;font-weight:700; color:#4ade80; letter-spacing:.06em; text-shadow:0 1px 8px rgba(0,0,0,.8); }
/* ── MAX LIMIT MODAL CSS ── */
:where([data-page="gallery-viewer"]) #maxModal { display:none;position:fixed;inset:0;z-index:9999;align-items:center;justify-content:center;background:rgba(0,0,0,.55);backdrop-filter:blur(4px) }
:where([data-page="gallery-viewer"]) #maxModal.open { display:flex }
:where([data-page="gallery-viewer"]) #maxModalBox { background:var(--bg); border: 1px solid var(--border); border-radius:20px; padding:36px 40px; text-align:center; min-width:280px; max-width:380px; box-shadow:0 24px 64px rgba(0,0,0,.6); animation:mmPop .25s cubic-bezier(.34,1.56,.64,1); }
@keyframes mmPop {from{opacity:0;transform:scale(.82)}to{opacity:1;transform:scale(1)}}
:where([data-page="gallery-viewer"]) #maxModalIcon { font-size:46px;margin-bottom:14px;line-height:1 }
:where([data-page="gallery-viewer"]) #maxModalTitle { font-size:19px;font-weight:700;color:#fff;margin-bottom:7px;letter-spacing:.3px }
:where([data-page="gallery-viewer"]) #maxModalSub { font-size:13px;color:rgba(255,255,255,.55);margin-bottom:24px;line-height:1.5 }
:where([data-page="gallery-viewer"]) #maxModalBtn { display:inline-block; padding:10px 32px; background:var(--p); color:#fff; border:none; border-radius:50px; font-size:14px; font-weight:600; cursor:pointer; transition:opacity .15s; }
:where([data-page="gallery-viewer"]) #maxModalBtn:hover { opacity:.85 }

/* ── The light/dark switch ─────────────────────────────────────────────── */
/* The bar used to be a mobile-only hamburger holder. It now carries the theme
   switch, so it shows at every width — with the hamburger still mobile-only. */
.topbar-spacer { flex: 1 1 auto; }
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; flex: 0 0 auto;
  border: 1px solid var(--border); border-radius: 9px;
  background: var(--surface); color: var(--muted);
  cursor: pointer; padding: 0;
  transition: color var(--transition), background var(--transition);
}
.theme-toggle:hover { color: var(--text); background: var(--surface2); }
.theme-toggle svg { width: 18px; height: 18px; }
/* One icon at a time: the one showing is what pressing it gives you. */
.theme-toggle .theme-moon { display: none; }
[data-theme="dark"] .theme-toggle .theme-moon { display: none; }
[data-theme="dark"] .theme-toggle .theme-sun { display: block; }
:root:not([data-theme="dark"]) .theme-toggle .theme-sun { display: none; }
:root:not([data-theme="dark"]) .theme-toggle .theme-moon { display: block; }

/* Pinned to the corner, for a page with no bar to hang the switch from:
   sign in, register, forgot password. */
.theme-toggle.is-corner { position: fixed; top: 18px; right: 18px; z-index: 40; }


/* The requirements step now has two groups and its own actions, so it needs
   headings and a row of buttons the single Continue link never did. */
:where([data-page="install-installer"]) .req-group { font-size:13px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);margin:22px 0 10px }
:where([data-page="install-installer"]) .req-group:first-of-type { margin-top:6px }
:where([data-page="install-installer"]) .req-list { margin-bottom:8px }
:where([data-page="install-installer"]) .req-actions { display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:18px 0 10px }
:where([data-page="install-installer"]) .hint { font-size:12.5px;line-height:1.6;color:var(--muted);margin:0 0 12px }
:where([data-page="install-installer"]) .alert pre { margin:10px 0 0;padding:10px 12px;border-radius:8px;background:var(--surface2);color:var(--text);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.6;white-space:pre-wrap;word-break:break-all }
