/* ============================================================
   Movie Library — GL-003 "Graphite" design system
   Calm operator, precise instrument.
   Cool graphite near-black canvas; brass is the ONE signature
   accent, used sparingly (active states, checked checkbox,
   focus ring). Never wallpaper.
   ============================================================ */

/* ---- Tokens: DARK (default) ---- */
:root {
  --surface-bg: oklch(0.155 0.004 250);
  --surface-1: oklch(0.195 0.004 250);
  --surface-2: oklch(0.225 0.004 250);
  --surface-3: oklch(0.255 0.004 250);

  --border: oklch(0.30 0.005 250);
  --border-subtle: oklch(0.30 0.005 250 / 0.55);

  --fg: oklch(0.97 0.003 250);
  --fg-muted: oklch(0.66 0.005 250);
  --fg-subtle: oklch(0.64 0.005 250);

  --accent-brass: oklch(0.72 0.13 60);
  --accent-deep: oklch(0.64 0.135 58);
  --accent-soft: oklch(0.72 0.13 60 / 0.14);
  --text-on-brass: oklch(0.16 0.01 250);

  --focus-ring: oklch(0.72 0.13 60 / 0.70);

  --status-success: oklch(0.68 0.13 152);
  --status-error: oklch(0.65 0.20 25);
  /* Danger BUTTON fill tokens (GL-003 §2a). Separate from --status-error so
     the accent uses of --status-error (status text, toast border, checkbox
     error label, --fg-error-text base) stay untouched. The mid-lightness
     --status-error (0.65 L) does NOT pair with a single ink across themes+
     states: on the .btn--danger label (15px/600 → small text, 4.5:1 floor)
     it measured light-rest 3.36:1 (FAIL) and dark-hover 3.42:1 (FAIL).
     Fix: a stable near-white ink (--text-on-danger) plus per-theme fills dark
     enough that white clears 4.5:1 resting AND on the darker hover. Hue held
     at ~25 so it still reads "danger". Dark theme fills below; light overrides.
     Contrast (white ink oklch(0.98) vs fill), all ≥ 4.5:1:
       dark  rest  (0.55 L) → ~5.55:1
       dark  hover (0.48 L) → ~6.96:1
       light rest  (0.50 L) → ~6.52:1  (matches §2a precedent)
       light hover (0.44 L) → ~7.9:1 */
  --text-on-danger: oklch(0.98 0.003 250);
  --danger-fill: oklch(0.55 0.20 25);
  --danger-fill-hover: oklch(0.48 0.20 25);
  /* Login error text token. Dark theme keeps --status-error (5.14:1 on
     --surface-1). Light theme overrides this to a darker red for AA. */
  --fg-error-text: var(--status-error);
  /* Success text token (mirrors --fg-error-text). Dark keeps
     --status-success (6.71:1 on --surface-1). Light overrides darker for AA.
     Only small-text success usages point here; borders/pulses keep the raw
     --status-success accent. */
  --fg-success-text: var(--status-success);
  /* Quality/accent-soft chip text token (GL-003 §2a). The 4K chip paints
     small text (0.7rem/weight 600) on --accent-soft (brass @0.14) over
     --surface-1. Raw --accent-brass (0.72 L) computes ~4.02:1 there — a WCAG
     1.4.3 fail. Dark lifts to 0.82 L (brass hue 60, chroma trimmed to 0.11)
     → ~5.93:1. Light overrides darker below. Any accent-soft chip that needs
     legible small text binds here, not to --accent-brass. */
  --fg-quality-text: oklch(0.82 0.11 60);

  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  /* V2: define --space-xl. V1 referenced it (.content / .status padding) but
     never declared it, so it silently resolved to 0. Declared here so V2's
     content padding and the login card breathe as intended. */
  --space-xl: 40px;

  /* Derived, non-color primitives */
  --radius-card: 12px;
  --radius-ctrl: 8px;
  --radius-pill: 999px;
  --shadow-card: 0 1px 2px oklch(0 0 0 / 0.30);

  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Menlo", "Cascadia Code", "Consolas", monospace;

  --toolbar-h: auto;
  --grid-min: 220px;
}

/* ---- Tokens: LIGHT override ---- */
[data-theme='light'] {
  --surface-bg: oklch(0.975 0.003 250);
  --surface-1: oklch(0.992 0.003 250);
  --surface-2: oklch(0.945 0.004 250);
  --surface-3: oklch(0.905 0.005 250);

  --border: oklch(0.86 0.005 250);
  --border-subtle: oklch(0.86 0.005 250 / 0.7);

  --fg: oklch(0.26 0.006 250);
  --fg-muted: oklch(0.46 0.005 250);
  --fg-subtle: oklch(0.53 0.005 250);

  --accent-brass: oklch(0.52 0.13 60);
  --accent-deep: oklch(0.46 0.12 60);
  --accent-soft: oklch(0.52 0.13 60 / 0.13);
  --text-on-brass: oklch(0.98 0.003 250);

  --focus-ring: oklch(0.52 0.13 60 / 0.55);

  /* Darker error red for small text on light --surface-1 (login card):
     shared --status-error (0.65 L) computes ~3.48:1 here (WCAG 1.4.3 fail);
     0.50 L lands ~6.52:1. Only affects .login__error, not toasts/borders. */
  --fg-error-text: oklch(0.50 0.20 25);

  /* Danger BUTTON fill tokens — light override (GL-003 §2a). The near-white
     --text-on-danger ink is stable across themes (re-declared for symmetry),
     so on light we only darken the fills. Light rest reuses the §2a-proven
     0.50 L red (white ink → ~6.52:1); hover deepens to 0.44 L (~7.9:1). Both
     clear the 15px/600 small-text 4.5:1 floor. */
  --text-on-danger: oklch(0.98 0.003 250);
  --danger-fill: oklch(0.50 0.20 25);
  --danger-fill-hover: oklch(0.44 0.19 25);

  /* Darker green for small success text on light --surface-1: shared
     --status-success (0.68 L) computes ~2.66:1 here (WCAG 1.4.3 fail);
     0.50 L lands ~5.37:1. Only affects small-text success, not borders/pulses. */
  --fg-success-text: oklch(0.50 0.15 152);

  /* Quality/accent-soft chip text on light (GL-003 §2a): the light brass
     0.52 L already clears — small text on --accent-soft (brass @0.13) over
     light --surface-1 computes ~5.11:1. Bound to the light brass value. */
  --fg-quality-text: oklch(0.52 0.13 60);

  --shadow-card: 0 1px 2px oklch(0 0 0 / 0.08);
}

/* ============================================================
   Reset / base
   ============================================================ */
*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface-bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.45;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

h1, h2, p, dl, dd, dt { margin: 0; }

img { max-width: 100%; display: block; }

.mono { font-family: var(--font-mono); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute;
  left: var(--space-sm);
  top: -60px;
  z-index: 100;
  padding: var(--space-sm) var(--space-md);
  background: var(--surface-2);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: var(--radius-ctrl);
  text-decoration: none;
  transition: top 120ms ease;
}
.skip-link:focus { top: var(--space-sm); }

/* Global focus-visible: brass ring */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ============================================================
   Toolbar (sticky)
   ============================================================ */
.toolbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in oklch, var(--surface-1) 94%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.toolbar__row {
  display: flex;
  gap: var(--space-md);
  align-items: center;
  flex-wrap: wrap;
}

.toolbar__row--primary { justify-content: space-between; }

.brand { display: flex; align-items: center; gap: var(--space-sm); flex: 0 0 auto; }
.brand__mark {
  width: 12px; height: 12px;
  border-radius: 3px;
  background: var(--accent-brass); /* the ONE brass moment in the header */
}
.brand__title { font-size: 1.05rem; font-weight: 650; letter-spacing: -0.01em; }

.search { flex: 1 1 260px; min-width: 180px; }
.search__input { width: 100%; }

.toolbar__meta { display: flex; align-items: center; gap: var(--space-md); flex: 0 0 auto; }

.result-count {
  color: var(--fg-muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.toolbar__row--filters {
  gap: var(--space-sm) var(--space-md);
}

.field { display: flex; flex-direction: column; gap: var(--space-xs); flex: 1 1 150px; min-width: 120px; }
.field--range { flex: 1 1 180px; }
.field--action { flex: 0 0 auto; align-self: flex-end; justify-content: flex-end; }

.field__label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-subtle);
  font-weight: 600;
}

.field__range { display: flex; align-items: center; gap: var(--space-xs); }
.field__range-sep { color: var(--fg-subtle); }

/* ============================================================
   Release-year dual-handle range slider
   ============================================================ */
.field--year { flex: 1 1 220px; min-width: 200px; }
/* fieldset reset for the Added-status group */
.field--added {
  border: none;
  margin: 0;
  padding: 0;
  min-inline-size: auto;
}
.field--added .field__label { padding: 0; }

.year-range {
  position: relative;
  height: 28px;
  display: flex;
  align-items: center;
}
/* The rail + active fill sit behind the two range inputs. */
.year-range__track {
  position: absolute;
  left: 0;
  right: 0;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  border: 1px solid var(--border);
}
.year-range__fill {
  position: absolute;
  top: -1px;
  bottom: -1px;
  background: var(--accent-brass); /* brass on the active span */
  border-radius: var(--radius-pill);
}
/* Two overlaid native range inputs — transparent tracks, real thumbs.
   pointer-events:none on the element, re-enabled on the thumbs, so both
   thumbs stay grabbable even where they overlap. */
.year-range__input {
  position: absolute;
  left: 0;
  right: 0;
  width: 100%;
  margin: 0;
  height: 28px;
  background: none;
  pointer-events: none;
  appearance: none;
  -webkit-appearance: none;
}
.year-range__input::-webkit-slider-runnable-track {
  height: 28px;
  background: none;
  border: none;
}
.year-range__input::-moz-range-track {
  height: 28px;
  background: none;
  border: none;
}
.year-range__input::-webkit-slider-thumb {
  pointer-events: auto;
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent-brass);
  border: 2px solid var(--surface-1);
  box-shadow: 0 0 0 1px var(--accent-deep);
  cursor: grab;
  margin-top: 6px; /* center the 16px thumb on the 28px track */
}
.year-range__input::-moz-range-thumb {
  pointer-events: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent-brass);
  border: 2px solid var(--surface-1);
  box-shadow: 0 0 0 1px var(--accent-deep);
  cursor: grab;
}
.year-range__input:active::-webkit-slider-thumb { cursor: grabbing; }
.year-range__input:active::-moz-range-thumb { cursor: grabbing; }
.year-range__input:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.year-range__input:focus-visible::-moz-range-thumb {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
/* The max input sits on top; without this the min thumb can become
   unreachable at the far right. Both share the same stacking area but the
   pointer-events trick keeps each thumb hittable. */
.year-range__input--max { z-index: 2; }
.year-range__input--min { z-index: 3; }

.year-range__readout {
  margin: var(--space-xs) 0 0;
  font-size: 0.8rem;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
}

/* Added-status filter checkboxes (toolbar) */
.added-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  align-items: center;
  min-height: 28px;
}
.checkbox--filter { cursor: pointer; }

/* ============================================================
   Form controls
   ============================================================ */
.input {
  font: inherit;
  color: var(--fg);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-ctrl);
  padding: var(--space-sm) 10px;
  width: 100%;
  transition: border-color 120ms ease, background-color 120ms ease;
}
.input::placeholder { color: var(--fg-subtle); }
.input:hover { border-color: color-mix(in oklch, var(--border) 60%, var(--fg-subtle)); }
.input:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
  border-color: var(--accent-brass);
}
.input--year { width: 78px; flex: 0 0 auto; }

.select {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: 28px;
  background-image: linear-gradient(45deg, transparent 50%, var(--fg-muted) 50%),
                    linear-gradient(135deg, var(--fg-muted) 50%, transparent 50%);
  background-position: calc(100% - 15px) center, calc(100% - 10px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  /* Keep the native option popup legible in the active theme. */
  color-scheme: light dark;
  text-overflow: ellipsis;
}
/* Native option list — themed where the browser honors it. */
.select option { background: var(--surface-2); color: var(--fg); }

.btn {
  font: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  gap: var(--space-xs);
  border-radius: var(--radius-ctrl);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--fg);
  transition: background-color 120ms ease, border-color 120ms ease;
  white-space: nowrap;
}
.btn:hover { background: var(--surface-3); }
.btn:disabled { opacity: 0.6; cursor: default; }
.btn--ghost { background: transparent; }
.btn--ghost:hover { background: var(--surface-2); }
.btn--ghost:disabled:hover { background: transparent; }

.theme-toggle { gap: var(--space-sm); }
.theme-toggle__icon {
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid var(--fg-muted);
  box-shadow: inset -3px -3px 0 0 var(--fg-muted);
}
[data-theme='light'] .theme-toggle__icon {
  border-color: var(--accent-brass);
  box-shadow: none;
  background: var(--accent-brass);
}
.theme-toggle__label { font-size: 0.85rem; }

/* ============================================================
   Content / grid
   ============================================================ */
.content { padding: var(--space-lg) var(--space-md) var(--space-xl); }

.status {
  text-align: center;
  color: var(--fg-muted);
  padding: var(--space-xl) var(--space-md);
  font-size: 0.95rem;
}
.status--error { color: var(--status-error); }
.status__retry { margin-top: var(--space-md); }

.grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Cap at 10 columns max while staying full-width: the per-column minimum is
     the larger of --grid-min (responsive floor for narrow screens) and the
     width a 1/10 column would need (accounting for the 9 inter-column gaps).
     On wide screens the 1/10 term wins → never more than 10 per row, cards
     grow to fill; on narrow screens --grid-min wins → columns drop as before. */
  grid-template-columns: repeat(
    auto-fill,
    minmax(
      max(var(--grid-min), calc((100% - 9 * var(--space-lg)) / 10)),
      1fr
    )
  );
  gap: var(--space-lg);
}

/* ============================================================
   Card
   ============================================================ */
.card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-card);
  /* keep 1216 cards cheap to lay out */
  content-visibility: auto;
  contain-intrinsic-size: auto 420px;
  transition: border-color 140ms ease;
}
.card:hover { border-color: var(--border-subtle); }
.card:focus-within { border-color: color-mix(in oklch, var(--border) 40%, var(--accent-brass)); }
/* applyFilters() toggles el.hidden; the explicit display:flex above overrides the
   UA [hidden] { display:none } rule, so filtered-out cards would keep rendering.
   Restore hiding with a class+attribute selector that outranks .card, plus
   !important as belt-and-suspenders since .card sets display directly. A
   display:none element is removed from layout, so the grid reflows with no gaps. */
.card[hidden] { display: none !important; }

/* Poster */
.card__poster {
  position: relative; /* anchor for the .card__quality overlay label */
  aspect-ratio: 2 / 3;
  background: var(--surface-2);
  overflow: hidden;
}
.card__poster-img {
  width: 100%; height: 100%;
  object-fit: cover;
}
.card__poster-placeholder {
  position: absolute; inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-md);
  text-align: center;
  background:
    radial-gradient(circle at 50% 30%, var(--surface-3), var(--surface-2));
}
.card__poster-placeholder-text {
  color: var(--fg-muted);
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* When img is shown, hide placeholder */
.card__poster-img:not([hidden]) + .card__poster-placeholder { display: none; }

/* Body */
.card__body {
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  flex: 1;
}

.card__heading {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  flex-wrap: wrap;
}
.card__title {
  font-size: 1rem;
  font-weight: 640;
  letter-spacing: -0.01em;
  line-height: 1.25;
}
.card__year {
  color: var(--fg-muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
.card__artist {
  color: var(--fg-muted);
  font-size: 0.85rem;
  margin-top: calc(-1 * var(--space-xs));
}
.card__artist:empty { display: none; }

/* Quality chip — small overlay label pinned to the poster's bottom-right
   corner (the .card__poster is position:relative so this anchors to it).
   Neutral base for all tiers; 4K gets a subtle brass accent to reward the
   best copy. Legibility over arbitrary poster art: a semi-opaque dark
   backdrop (color-mix on --surface-2) + backdrop-blur + drop shadow keep
   the label readable over any photo. Uses design tokens only (no hardcoded
   colors/spacing/radius). */
.card__quality {
  position: absolute;
  bottom: var(--space-xs);
  right: var(--space-xs);
  z-index: 1;
  display: inline-flex;
  align-items: center;
  padding: 1px var(--space-xs);
  border: 1px solid color-mix(in oklch, var(--border) 55%, transparent);
  border-radius: var(--radius-ctrl);
  /* Opaque backdrop: the poster must NOT show through, or text contrast would
     depend on the poster pixels (bright lower-thirds fail WCAG 1.4.3). Solid
     --surface-2 is the contrast floor; blur + shadow below are decoration only
     and are NOT relied on for the ratio. */
  background: var(--surface-2);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  box-shadow: 0 1px 4px oklch(0 0 0 / 0.35);
  color: var(--fg-muted);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.4;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}
/* Match the .card[hidden] / .login[hidden] precedent: the display rule above
   would otherwise defeat the UA [hidden] rule, so force it off when hidden. */
.card__quality[hidden] { display: none !important; }
/* 4K: the premium tier — brass accent, still legible over the poster.
   The whole chip must be OPAQUE (poster can't show through), so the brass
   tint is composited onto solid --surface-2 using opaque --accent-brass
   rather than the alpha-based --accent-soft. Kept at a light ~5% brass mix
   so the backing stays bright enough that light-theme --fg-quality-text
   clears WCAG AA (>=4.5:1); dark theme keeps ample headroom at this ratio. */
.card__quality[data-quality='4K'] {
  border-color: color-mix(in oklch, var(--border) 30%, var(--accent-brass));
  background: color-mix(in oklch, var(--surface-2) 95%, var(--accent-brass));
  color: var(--fg-quality-text);
}

.card__apple {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  align-self: flex-start;
  color: var(--accent-brass);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 550;
  padding: 2px 0;
  border-bottom: 1px solid transparent;
}
.card__apple:hover { border-bottom-color: var(--accent-brass); }
.card__apple-icon {
  width: 12px; height: 12px;
  border: 1.5px solid currentColor;
  border-radius: 3px;
  position: relative;
}
.card__apple-icon::after {
  content: "";
  position: absolute;
  inset: 3px 3px auto auto;
  width: 4px; height: 4px;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
}

/* Meta: folder / file */
.card__meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px var(--space-sm);
  align-items: baseline;
  margin-top: var(--space-xs);
}
.card__meta-key {
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg-subtle);
  font-weight: 600;
}
.card__meta-val {
  font-size: 0.75rem;
  color: var(--fg-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: default;
}
/* click / Enter / Space copies the full value to the clipboard */
.card__meta-val.is-copyable {
  cursor: pointer;
  border-radius: var(--radius-ctrl);
  padding: 1px var(--space-xs);
  margin: -1px calc(-1 * var(--space-xs));
  transition: color 120ms ease, background-color 120ms ease;
}
.card__meta-val.is-copyable:hover,
.card__meta-val.is-copyable:focus-visible {
  color: var(--fg);
  background: var(--accent-soft);
}
.card__meta-val.is-copyable:active { color: var(--accent-brass); }

/* Controls: edit + added + comments */
.card__controls {
  margin-top: auto;
  padding-top: var(--space-sm);
  border-top: 1px solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Edit button: aligns to the start of the column so it reads as an action,
   not a full-width block. Sits above the Added checkbox + Comments. */
.card__edit {
  align-self: flex-start;
}

/* Checkbox */
.checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  cursor: pointer;
  position: relative;
  user-select: none;
}
.checkbox__input {
  position: absolute;
  opacity: 0;
  width: 0; height: 0;
}
.checkbox__box {
  width: 18px; height: 18px;
  flex: 0 0 auto;
  border: 1.5px solid var(--border);
  border-radius: 5px;
  background: var(--surface-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.checkbox__box::after {
  content: "";
  width: 5px; height: 9px;
  border: solid var(--text-on-brass);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) scale(0);
  transition: transform 120ms ease;
  margin-top: -1px;
}
.checkbox__input:checked + .checkbox__box {
  background: var(--accent-brass); /* brass on active — the checked moment */
  border-color: var(--accent-brass);
}
.checkbox__input:checked + .checkbox__box::after { transform: rotate(45deg) scale(1); }
.checkbox__input:focus-visible + .checkbox__box {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.checkbox__label { font-size: 0.9rem; }

/* saved pulse */
.checkbox__pulse {
  position: absolute;
  left: 9px; top: 50%;
  width: 18px; height: 18px;
  border-radius: 6px;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: none;
  opacity: 0;
}
.checkbox.is-saved .checkbox__pulse {
  animation: save-pulse 620ms ease-out;
}
@keyframes save-pulse {
  0%   { opacity: 0.55; box-shadow: 0 0 0 0 var(--status-success); }
  100% { opacity: 0; box-shadow: 0 0 0 10px transparent; }
}
.checkbox.is-error .checkbox__label { color: var(--status-error); }

/* Disclosure (comments) */
.disclosure { display: flex; flex-direction: column; }
.disclosure__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  background: none;
  border: none;
  color: var(--fg-muted);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  padding: var(--space-xs) 0;
  border-radius: 4px;
  align-self: flex-start;
}
.disclosure__trigger:hover { color: var(--fg); }
.disclosure__chevron {
  width: 7px; height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 140ms ease;
}
.disclosure__trigger[aria-expanded="true"] .disclosure__chevron {
  transform: rotate(45deg);
}
.disclosure__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent-brass); /* indicator: this card has a comment */
}
.disclosure__panel { padding-top: var(--space-sm); }

.textarea {
  font: inherit;
  width: 100%;
  color: var(--fg);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-ctrl);
  padding: var(--space-sm);
  resize: vertical;
  min-height: 60px;
}
.textarea::placeholder { color: var(--fg-subtle); }
.textarea:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
  border-color: var(--accent-brass);
}

.card__comments-status {
  display: block;
  font-size: 0.72rem;
  margin-top: var(--space-xs);
  min-height: 1em;
  color: var(--fg-subtle);
}
.card__comments-status.is-saved { color: var(--fg-success-text, var(--status-success)); }
.card__comments-status.is-error { color: var(--status-error); }

/* ============================================================
   Toasts
   ============================================================ */
.toast-region {
  position: fixed;
  bottom: var(--space-lg);
  right: var(--space-lg);
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  max-width: min(360px, calc(100vw - 2 * var(--space-md)));
  pointer-events: none;
}
.toast {
  background: var(--surface-3);
  color: var(--fg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--status-error);
  border-radius: var(--radius-ctrl);
  padding: var(--space-sm) var(--space-md);
  font-size: 0.85rem;
  box-shadow: 0 4px 16px oklch(0 0 0 / 0.35);
  animation: toast-in 180ms ease-out;
}
.toast--error { border-left-color: var(--status-error); }
.toast--success { border-left-color: var(--status-success); }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 640px) {
  .content { padding: var(--space-md) var(--space-sm) var(--space-lg); }
  .grid { gap: var(--space-md); --grid-min: 150px; }
  .toolbar { padding: var(--space-sm); }
  .toolbar__meta { width: 100%; justify-content: space-between; }
  .field { flex: 1 1 100%; }
  .field--action { align-self: stretch; }
  .field--action .btn { width: 100%; justify-content: center; }
  .toast-region { left: var(--space-sm); right: var(--space-sm); bottom: var(--space-sm); }
}

@media (max-width: 400px) {
  .grid { grid-template-columns: 1fr; --grid-min: 100%; }
}

/* ============================================================
   Login gate (V2) — GL-003 "Graphite", brass used only on the
   primary action + focus ring. Reuses .input / .field / .brand tokens.
   ============================================================ */
.login-body { display: flex; min-height: 100vh; }

.login {
  min-height: 100vh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-xl) var(--space-md);
}
/* showLogin()/showApp() toggle el.hidden; the explicit display:flex above overrides
   the UA [hidden] { display:none } rule, so the login view would keep rendering
   even when hidden (stacked above the app). Restore hiding with a class+attribute
   selector that outranks .login, plus !important as belt-and-suspenders since
   .login sets display directly. Same fix pattern as .card[hidden] above. */
.login[hidden] { display: none !important; }

.login__card {
  width: 100%;
  max-width: 360px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.brand--login { justify-content: center; margin-bottom: var(--space-xs); }

.login__hint {
  color: var(--fg-muted);
  font-size: 0.9rem;
  text-align: center;
  margin: 0;
}

.login__field { gap: var(--space-xs); }

/* Primary action: the one brass moment on the login card. */
.btn--primary {
  background: var(--accent-brass);
  border-color: var(--accent-brass);
  color: var(--text-on-brass);
  font-weight: 600;
  justify-content: center;
}
.btn--primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.btn--primary:disabled { opacity: 0.6; cursor: default; }

/* Destructive action (Feature A: delete a film). Binds to Iris's dedicated
   danger tokens (--danger-fill / --danger-fill-hover, per-theme) with the
   stable --text-on-danger near-white ink, which clears WCAG AA on both the
   dark and light fills — the earlier --status-error + --text-on-brass pairing
   failed contrast. Inherits the .btn base (44px min-height, radius, padding)
   and the global :focus-visible brass ring; neither is re-declared here. */
.btn--danger {
  background: var(--danger-fill);
  border-color: var(--danger-fill);
  color: var(--text-on-danger);
  font-weight: 600;
}
.btn--danger:hover {
  background: var(--danger-fill-hover);
  border-color: var(--danger-fill-hover);
}
.btn--danger:disabled { opacity: 0.6; cursor: default; }
.btn--danger:disabled:hover {
  background: var(--danger-fill);
  border-color: var(--danger-fill);
}

.login__submit { width: 100%; margin-top: var(--space-xs); }

.login__error {
  color: var(--fg-error-text, var(--status-error));
  font-size: 0.85rem;
  text-align: center;
  margin: 0;
}

/* Log-out control lives in the toolbar meta row. */
.logout-btn { font-size: 0.85rem; }

/* Re-scan quality control — toolbar meta row sibling. */
.rescan-quality-btn { font-size: 0.85rem; }

/* ============================================================
   Add-film button (toolbar meta)
   ============================================================ */
.add-film-btn {
  font-size: 0.85rem;
  gap: var(--space-xs);
}
.add-film-btn__plus {
  font-size: 1.05em;
  line-height: 1;
  font-weight: 700;
}

/* ============================================================
   Add-film dialog (V2.0) — native <dialog>, GL-003 "Graphite".
   Brass only on the Save action + focus ring. Reuses .field /
   .input / .textarea / .checkbox / .btn primitives.
   ============================================================ */
.dialog {
  /* reset UA dialog chrome; we paint our own card */
  padding: 0;
  border: none;
  background: transparent;
  color: var(--fg);
  max-width: min(560px, calc(100vw - 2 * var(--space-md)));
  width: 100%;
  max-height: calc(100vh - 2 * var(--space-lg));
  border-radius: var(--radius-card);
  box-shadow: 0 12px 40px oklch(0 0 0 / 0.45);
}
.dialog::backdrop {
  background: oklch(0.12 0.004 250 / 0.62);
  backdrop-filter: blur(3px);
}

.dialog__card {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 2 * var(--space-lg));
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.dialog__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border-subtle);
  flex: 0 0 auto;
}
.dialog__title {
  font-size: 1.05rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}
.dialog__close {
  padding: var(--space-xs) var(--space-sm);
  font-size: 1.2rem;
  line-height: 1;
}

/* Scrollable body so a tall form stays usable at ~360px wide. */
.dialog__body {
  padding: var(--space-lg);
  overflow-y: auto;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.dialog__lead {
  color: var(--fg-muted);
  font-size: 0.9rem;
  margin: 0;
}

.dialog__lookup { display: flex; flex-direction: column; gap: var(--space-sm); }
.dialog__lookup-row {
  display: flex;
  gap: var(--space-sm);
  align-items: stretch;
}
.dialog__lookup-row .input { flex: 1 1 auto; }
.dialog__lookup-row .btn { flex: 0 0 auto; }

.dialog__divider {
  border: none;
  border-top: 1px solid var(--border-subtle);
  margin: 0;
}

.dialog__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-md);
}
.dialog__field--wide { grid-column: 1 / -1; }
.dialog__field--check {
  grid-column: 1 / -1;
  flex-direction: row;
  align-items: center;
}

.dialog__req { color: var(--accent-brass); }

.dialog__status {
  font-size: 0.82rem;
  margin: 0;
  min-height: 1em;
  color: var(--fg-muted);
}
.dialog__status.is-info { color: var(--fg-muted); }
.dialog__status.is-success { color: var(--fg-success-text, var(--status-success)); }
.dialog__status.is-error { color: var(--fg-error-text, var(--status-error)); }
.dialog__status--form { font-weight: 550; }
.dialog__status:empty { display: none; }

.dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
  padding: var(--space-md) var(--space-lg);
  border-top: 1px solid var(--border-subtle);
  flex: 0 0 auto;
}

@media (max-width: 480px) {
  .dialog { max-width: none; }
  .dialog__grid { grid-template-columns: 1fr; }
  .dialog__head,
  .dialog__body,
  .dialog__footer { padding-left: var(--space-md); padding-right: var(--space-md); }
  .dialog__footer .btn { flex: 1 1 auto; justify-content: center; }
}

/* ============================================================
   Options dialog rows (V2.0) — each maintenance action is a labeled
   row (title + description on the left, action button on the right).
   Reuses .dialog / .btn / .dialog__status primitives; no new tokens.
   ============================================================ */
.options-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}
.options-row__text { min-width: 0; }
.options-row__title {
  font-size: 0.95rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  margin: 0;
}
.options-row__desc {
  color: var(--fg-muted);
  font-size: 0.85rem;
  margin: var(--space-xs) 0 0;
}
.options-row__action { flex: 0 0 auto; }

.options-import-choice {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-sm) 0 0;
}
.options-import-choice[hidden] { display: none !important; }
.options-import-choice__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

@media (max-width: 480px) {
  .options-row {
    align-items: flex-start;
    flex-direction: column;
    gap: var(--space-sm);
  }
  .options-row__action { align-self: stretch; justify-content: center; }
  .options-import-choice__actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* ============================================================
   Changed-quality review panel (Feature B) — dismiss-only
   Sits inside .content above the grid. A calm card on GL-003 surfaces,
   neutral by default (no brass wallpaper). Each row shows a title + an
   "old → new" delta and a Remove button; a Clear-all lives in the header.
   Remove/Clear buttons inherit the .btn base (44px min-height) so they meet
   the target-size floor. Reads correctly light + dark and at 375/768/1280.
   ============================================================ */
.changed-panel {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  margin-bottom: var(--space-lg);
  overflow: hidden;
}
/* Match the .card[hidden] precedent: nothing here forces display, but keep the
   attribute authoritative so a hidden panel never leaks a stray border. */
.changed-panel[hidden] { display: none !important; }

.changed-panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border-subtle);
}
.changed-panel__title {
  font-size: 0.95rem;
  font-weight: 650;
  letter-spacing: -0.01em;
}
.changed-panel__clear { font-size: 0.85rem; }

.changed-panel__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.changed-panel__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-lg);
  border-top: 1px solid var(--border-subtle);
}
.changed-panel__row:first-child { border-top: none; }

.changed-panel__text {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-sm);
  min-width: 0;
}
.changed-panel__row-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--fg);
  overflow-wrap: anywhere;
}
.changed-panel__delta {
  font-size: 0.82rem;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.changed-panel__remove {
  font-size: 0.85rem;
  flex: 0 0 auto;
}

@media (max-width: 640px) {
  .changed-panel__head,
  .changed-panel__row {
    padding-left: var(--space-md);
    padding-right: var(--space-md);
  }
  .changed-panel__row {
    align-items: flex-start;
    gap: var(--space-sm);
  }
}

/* ============================================================
   Reduced motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
