/* ==========================================================================
   Precision Instrument — ripqueue theme
   --------------------------------------------------------------------------
   Dark mode first. Apple-inspired minimalism. Premium and understated.
   Deep observatory blue dominates; brass appears only on important actions
   and highlights. No gradients, no glow, no startup candy.

   Visual reference: vintage audio equipment, watch movements, astronomy,
   precision manufacturing.

   LAYER 1 (tokens) and LAYER 2 (primitives) are the shared house style and
   are byte-identical across ripqueue, riptidal and ripdisc. Never edit them
   in one project alone -- a change there is a change to all three. LAYER 3
   is ripqueue's own.
   ========================================================================== */

/* ==========================================================================
   LAYER 1 — Tokens
   ========================================================================== */

:root {
  /* Brand ------------------------------------------------------------- */
  --primary:          #213A5A;   /* deep observatory blue — dominant */
  --primary-hover:    #2D4E77;
  --accent:           #B08D57;   /* brass — important actions only */
  --accent-hover:     #C09A60;

  /* Surfaces ---------------------------------------------------------- */
  --bg:               #0E1218;
  --surface:          #171D25;
  --surface-elevated: #202833;
  --border:           #304050;

  /* Text -------------------------------------------------------------- */
  --text:             #E8EDF3;
  --text-secondary:   #AAB5C3;
  --text-muted:       #758293;

  /* Semantic ---------------------------------------------------------- */
  --success:          #4F8A63;
  --warning:          #C69243;
  --error:            #B45454;
  --info:             #4C78A8;

  /* Semantic, low-emphasis. Tints of the above for outline treatments.
     Kept as explicit values rather than opacity so they composite
     identically over --surface and --surface-elevated. */
  --success-dim:      #7FB894;
  --success-line:     #3D6B4D;
  --success-wash:     rgba(79, 138, 99, 0.12);
  --warning-dim:      #DBB477;
  --warning-line:     #6E5427;
  --warning-wash:     rgba(198, 146, 67, 0.12);
  --error-dim:        #D08A8A;
  --error-line:       #6E3535;
  --error-wash:       rgba(180, 84, 84, 0.12);
  --info-dim:         #7FA5CC;
  --info-line:        #3A5A7C;
  --info-wash:        rgba(76, 120, 168, 0.10);
  --accent-wash:      rgba(176, 141, 87, 0.12);

  /* Typography -------------------------------------------------------- */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
          system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "SF Mono", ui-monospace, SFMono-Regular, "JetBrains Mono",
          Menlo, Consolas, monospace;

  --fs-xs:   11px;
  --fs-sm:   12.5px;
  --fs-base: 14px;
  --fs-md:   15px;
  --fs-lg:   18px;
  --fs-xl:   24px;
  --fs-2xl:  32px;

  --lh-tight: 1.25;
  --lh-base:  1.55;

  /* Section eyebrows: uppercase, tracked out, muted. Used for every
     section label in the app so scanning feels instrument-like. */
  --eyebrow-size: 10.5px;
  --eyebrow-track: 0.09em;

  /* Geometry ---------------------------------------------------------- */
  --r-sm: 5px;
  --r-md: 7px;
  --r-lg: 10px;
  --r-xl: 14px;

  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 22px;
  --sp-6: 32px;
  --sp-7: 48px;

  /* Elevation. Shadows are deep and soft, never colored — the eye should
     read depth, not decoration. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.40);
  --shadow-lg: 0 18px 48px rgba(0, 0, 0, 0.55);

  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--info);

  --transition: 120ms cubic-bezier(0.32, 0.72, 0, 1);
}

/* This app is dark-mode first and does not ship a light palette. Declaring
   the scheme keeps form controls, scrollbars and the UA caret dark. */
:root { color-scheme: dark; }


/* ==========================================================================
   LAYER 2 — Primitives
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Numbers must never shift width — queue positions, years, runtimes,
   timestamps and counts all sit in columns. */
.tnum, table, input[type="number"], time, .pos, .count {
  font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4 {
  margin: 0 0 var(--sp-3);
  line-height: var(--lh-tight);
  letter-spacing: -0.011em;
  font-weight: 600;
}
h1 { font-size: var(--fs-2xl); letter-spacing: -0.021em; }
h2 { font-size: var(--fs-xl);  letter-spacing: -0.017em; }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }

p { margin: 0 0 var(--sp-3); }

a {
  color: var(--info-dim);
  text-decoration: none;
  transition: color var(--transition);
}
a:hover { color: var(--text); }

code, kbd, pre { font-family: var(--font-mono); font-size: 0.92em; }

hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: var(--sp-5) 0;
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--r-sm);
}

::selection { background: var(--primary-hover); color: var(--text); }

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

/* Layout -------------------------------------------------------------- */
.container { width: 100%; max-width: 1120px; margin: 0 auto; padding: 0 var(--sp-4); }
.container--narrow { max-width: 720px; }
.container--content { max-width: 940px; }

.stack > * + * { margin-top: var(--sp-3); }
.stack-lg > * + * { margin-top: var(--sp-5); }

.row { display: flex; align-items: center; gap: var(--sp-2); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.spacer { flex: 1 1 auto; }

/* Eyebrow / section label --------------------------------------------- */
.eyebrow {
  font-size: var(--eyebrow-size);
  letter-spacing: var(--eyebrow-track);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-muted);
  margin: 0 0 var(--sp-3);
}

.muted { color: var(--text-muted); }
.secondary { color: var(--text-secondary); }
.small { font-size: var(--fs-sm); }

/* Navigation ---------------------------------------------------------- */
.nav {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 40;
}
.nav-inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  height: 54px;
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 var(--sp-4);
}
.nav-brand {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.012em;
}
/* The one brass mark in the chrome: a hairline under the wordmark. */
.nav-brand::after {
  content: "";
  display: block;
  height: 1.5px;
  width: 20px;
  margin-top: 3px;
  background: var(--accent);
  border-radius: 1px;
}
.nav-links { display: flex; align-items: center; gap: var(--sp-1); }
.nav-link {
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  font-weight: 500;
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--r-sm);
  transition: background var(--transition), color var(--transition);
}
.nav-link:hover { background: var(--surface-elevated); color: var(--text); }
.nav-link[aria-current="page"] { background: var(--primary); color: var(--text); }

/* Cards and panels ---------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
}
.card--elevated { background: var(--surface-elevated); box-shadow: var(--shadow-md); }
.card--flush { padding: 0; overflow: hidden; }

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.card-body { padding: var(--sp-5); }
.card-footer {
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--bg);
}

/* Buttons -------------------------------------------------------------
   Brass is the single primary action per view. Everything else is quiet. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 550;
  line-height: 1;
  padding: 9px var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface-elevated);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition), border-color var(--transition),
              color var(--transition), opacity var(--transition);
}
.btn:hover { background: var(--border); }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
  font-weight: 600;
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-secondary { background: var(--primary); border-color: var(--primary); color: var(--text); }
.btn-secondary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }

.btn-ghost { background: transparent; border-color: var(--border); color: var(--text-secondary); }
.btn-ghost:hover { background: var(--surface-elevated); color: var(--text); }

.btn-danger { background: transparent; border-color: var(--error-line); color: var(--error-dim); }
.btn-danger:hover { background: var(--error-wash); border-color: var(--error); color: var(--text); }

.btn-sm { font-size: var(--fs-xs); padding: 6px var(--sp-3); border-radius: var(--r-sm); }
.btn-lg { font-size: var(--fs-base); padding: 12px var(--sp-5); }

.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.42;
  cursor: not-allowed;
  pointer-events: none;
}

/* Icon-only button — used for the queue reorder arrows. */
.btn-icon {
  width: 30px; height: 30px;
  padding: 0;
  border-radius: var(--r-sm);
  color: var(--text-muted);
  background: transparent;
}
.btn-icon:hover { background: var(--surface-elevated); color: var(--accent); }

/* Forms ---------------------------------------------------------------- */
.field { display: block; margin-bottom: var(--sp-4); }
.label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 550;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.hint { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 5px; }

.input, .select, .textarea {
  width: 100%;
  font-family: inherit;
  font-size: var(--fs-base);
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 9px var(--sp-3);
  transition: border-color var(--transition), background var(--transition);
}
.input::placeholder, .textarea::placeholder { color: var(--text-muted); }
.input:hover, .select:hover, .textarea:hover { border-color: #3B4E62; }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--info);
  background: var(--surface);
  box-shadow: 0 0 0 3px var(--info-wash);
}
.textarea { min-height: 92px; resize: vertical; line-height: var(--lh-base); }

.select {
  appearance: none;
  padding-right: var(--sp-6);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23758293' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
}

.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--error); }
.field-error { font-size: var(--fs-xs); color: var(--error-dim); margin-top: 5px; }

/* Search field — the wizard's centerpiece. */
.search {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 0 var(--sp-3);
}
.search:focus-within { border-color: var(--info); box-shadow: 0 0 0 3px var(--info-wash); }
.search input {
  flex: 1;
  border: 0; background: transparent; color: var(--text);
  font-family: inherit; font-size: var(--fs-md);
  padding: 11px 0;
}
.search input:focus { outline: none; }
/* The wrapper already indicates focus via :focus-within. Without this the
   global :focus-visible rule draws a second ring around the input itself,
   which reads as a box inside a box. */
.search input:focus-visible { box-shadow: none; }

/* Tables --------------------------------------------------------------- */

/* Wide tables scroll inside their own box rather than stretching the page.
   This lived only in the mobile media query, which is backwards: a narrow
   screen is not where a wide table causes trouble. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Columns size to their content and give the rest to the widest one, so a
   table does not spread its few columns across the full width with acres
   between them. */
.table { width: 100%; table-layout: auto; border-collapse: collapse; font-size: var(--fs-sm); }
.table td:not(:last-child), .table th:not(:last-child) { white-space: nowrap; }
.table td:first-child, .table th:first-child { width: 40%; }
.table th {
  text-align: left;
  font-size: var(--eyebrow-size);
  letter-spacing: var(--eyebrow-track);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-muted);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.table td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--surface-elevated);
  color: var(--text-secondary);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover td { background: var(--surface-elevated); }
.table td.title { color: var(--text); font-weight: 500; }
.table td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Queue position marker — brass, because it is the thing the admin
   manipulates and the thing the requester waits on. */
.pos {
  display: inline-block;
  min-width: 2.1em;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* Dialogs -------------------------------------------------------------- */
dialog, .modal {
  background: var(--surface-elevated);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  padding: 0;
  width: min(560px, calc(100vw - 2 * var(--sp-4)));
}
dialog::backdrop { background: rgba(4, 7, 11, 0.68); }
.modal-header {
  padding: var(--sp-5) var(--sp-5) var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.modal-title { font-size: var(--fs-lg); font-weight: 600; margin: 0; }
.modal-body { padding: var(--sp-5); }
.modal-footer {
  display: flex; justify-content: flex-end; gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--bg);
  border-radius: 0 0 var(--r-xl) var(--r-xl);
}

/* Banners -------------------------------------------------------------- */
.banner {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface-elevated);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.banner-info    { border-color: var(--info-line);    background: var(--info-wash); }
.banner-success { border-color: var(--success-line); background: var(--success-wash); }
.banner-warning { border-color: var(--warning-line); background: var(--warning-wash); }
.banner-error   { border-color: var(--error-line);   background: var(--error-wash); }

/* Wizard stepper ------------------------------------------------------- */
.stepper { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.step { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--text-muted); }
.step-mark {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border);
  font-size: var(--fs-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.step[aria-current="step"] { color: var(--text); }
.step[aria-current="step"] .step-mark { border-color: var(--accent); color: var(--accent); }
.step[data-complete] { color: var(--text-secondary); }
.step[data-complete] .step-mark { border-color: var(--success-line); color: var(--success-dim); background: var(--success-wash); }
.step-sep { width: 22px; height: 1px; background: var(--border); }

/* Charts ---------------------------------------------------------------
   Ordered categorical ramp. Brass sits first because in this app the
   highlighted series is the one the viewer came for. */
:root {
  --chart-1: #B08D57;
  --chart-2: #4C78A8;
  --chart-3: #4F8A63;
  --chart-4: #7FA5CC;
  --chart-5: #C69243;
  --chart-6: #6E8299;
  --chart-grid: #202833;
  --chart-axis: #758293;
}

/* Empty state ----------------------------------------------------------- */
.empty {
  text-align: center;
  padding: var(--sp-7) var(--sp-5);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}


/* ==========================================================================
   LAYER 3 — ripqueue status chips  (project-specific)
   --------------------------------------------------------------------------
   Seven statuses, four semantic colors. Progression along the normal path is
   carried by treatment (outline -> solid) within a hue rather than by
   inventing new hues, so the happy path reads as one calm gradient and only
   genuine exceptions interrupt a scan.

     requested   muted     the inbox, not yet acted on
     approved    info      outline   in the master queue
     pending_rip info      solid     disc in hand
     ripped      success   outline   archived, not yet live
     published   success   solid     terminal, available in Plex
     challenged  warning   solid     exception, needs the admin
     rejected    error     solid     exception, terminal unless challenged
     withdrawn   muted               requester canceled
   ========================================================================== */

.chip {
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-xs);
  font-weight: 550;
  letter-spacing: 0.01em;
  line-height: 1;
  padding: 4px 11px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  white-space: nowrap;
}

.chip--requested,
.chip--withdrawn   { color: var(--text-secondary); border-color: var(--border);       background: var(--surface); }
/* Wanted, but nothing to rent yet. Dashed rather than a new hue: it is parked,
   not failed, and the palette's four semantic colors are already spoken for. */
.chip--unavailable { color: var(--text-secondary); border-color: var(--text-muted);   background: transparent;
                     border-style: dashed; }
.chip--approved    { color: var(--info-dim);       border-color: var(--info-line);    background: var(--info-wash); }
.chip--pending_rip { color: var(--bg);             border-color: var(--info);         background: var(--info); }
.chip--ripped      { color: var(--success-dim);    border-color: var(--success-line); background: var(--success-wash); }
.chip--published   { color: var(--bg);             border-color: var(--success);      background: var(--success); }
.chip--challenged  { color: var(--bg);             border-color: var(--warning);      background: var(--warning); }
.chip--rejected    { color: var(--bg);             border-color: var(--error);        background: var(--error); }

/* Format badge — Blu-ray availability is the whole point of the tool, so
   it gets a mark of its own, distinct from status. */
.fmt {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 3px 7px;
  border-radius: 4px;
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.fmt--bluray { color: var(--info-dim);    border-color: var(--info-line); }
.fmt--4k     { color: var(--accent);      border-color: var(--accent);    background: var(--accent-wash); }
.fmt--dvd    { color: var(--text-muted);  border-color: var(--border); }
.fmt--none   { color: var(--error-dim);   border-color: var(--error-line); }
/* Already in the library, asked for at better quality. Brass, because it is
   the fact an administrator most needs to notice before approving. */
.fmt--upgrade { color: var(--accent); border-color: var(--accent); background: var(--accent-wash); }
/* Wanted, but not rentable yet. Informational rather than alarming: nothing
   is wrong, it simply is not available. */
.fmt--watchlist { color: var(--info-dim); border-color: var(--info-line); background: var(--info-wash); }

/* Result rows in the submission wizard -------------------------------------
   A row is a <label>. Its radio only arrives once the format check comes back,
   and only for a disc stocked on Blu-ray or 4K — so an unselectable title has
   nothing to click rather than a control that quietly does nothing. */

.pick { display: block; cursor: pointer; }
.pick input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }

.pick-body {
  display: flex;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg);
  transition: border-color var(--transition), background var(--transition),
              opacity var(--transition);
}
.pick:hover .pick-body { border-color: #3B4E62; }
.pick:has(input:checked) .pick-body { border-color: var(--accent); background: var(--accent-wash); }
.pick:has(input:focus-visible) .pick-body { box-shadow: var(--focus-ring); }

/* Not stocked on Blu-ray: still listed, visibly out of reach. */
/* A watch-list row is selectable, just plainly not the happy path. */
.pick:has(.pick-choice--watchlist) .pick-body { opacity: 0.8; }
.pick:has(.pick-choice--watchlist) input:checked ~ * { opacity: 1; }
.pick:has(.pick-choice--unknown) { cursor: default; }
.pick:has(.pick-choice--unknown) .pick-body { opacity: 0.48; }

.pick-art {
  width: 40px; height: 58px; flex: none;
  object-fit: cover; border-radius: 3px;
  background: var(--surface-elevated);
}
/* DVDInbox has no artwork for many titles, in either payload. Say so quietly
   rather than leaving a hole that reads as a failed image. */
.pick-art--none {
  display: grid; place-items: center;
  color: var(--text-muted);
  background: transparent;
  border: 1px dashed var(--border);
}

.pick-main { flex: 1; min-width: 0; }
.pick-title { color: var(--text); font-weight: 550; }
.pick-overview {
  color: var(--text-muted); font-size: var(--fs-xs); margin-top: 3px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

.pick-choice {
  display: flex; align-items: center; gap: var(--sp-2);
  flex: none; margin-left: auto;
}
.pick-tick {
  width: 14px; height: 14px; flex: none;
  border-radius: 50%;
  border: 1px solid var(--border);
  transition: border-color var(--transition), background var(--transition);
}
.pick:has(input:checked) .pick-tick { border-color: var(--accent); background: var(--accent); }

/* htmx request indicators */
.htmx-indicator { opacity: 0; transition: opacity var(--transition); }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }


/* Staleness marker for cache-served results (see CLAUDE.md: degrade, never
   crash). Deliberately quiet — informative, not alarming. */
.stale {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-style: italic;
}


/* ==========================================================================
   Motion and responsive
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media (max-width: 640px) {
  :root { --fs-2xl: 26px; --fs-xl: 20px; }
  .card { padding: var(--sp-4); border-radius: var(--r-md); }
  .card-header, .card-body, .card-footer { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .nav-inner { gap: var(--sp-3); }
  .nav-link { padding: var(--sp-1) var(--sp-2); }
  /* Wide tables scroll in their own container rather than the page body. */
  .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .stepper { gap: var(--sp-2); }
  .step-label { display: none; }
}


/* Settings. Each row is a control with a sentence under it saying where the
   value came from, so the note has to read as part of the field rather than
   as body text that happens to sit nearby. */
.setting { padding-bottom: var(--sp-4); margin-bottom: var(--sp-4);
           border-bottom: 1px solid var(--border); }
.setting:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.setting-note { margin-top: var(--sp-2); display: flex; flex-wrap: wrap;
                gap: var(--sp-2); align-items: center; }

/* A list that reads as prose rather than as a bulleted list: used where each
   line explains one control, and the bullets would be louder than the text. */
.plain-list { list-style: none; padding: 0; }
.plain-list li { margin-bottom: var(--sp-2); }
.plain-list li:last-child { margin-bottom: 0; }

/* Saying a slow thing is happening. */
.working { align-items: center; gap: var(--sp-3); padding: var(--sp-2) 0;
           color: var(--text-secondary); font-size: var(--fs-sm); }
.spinner { width: 15px; height: 15px; flex: none;
           border: 2px solid var(--border); border-top-color: var(--accent);
           border-radius: 50%; animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }
