/* ============================================================================
   Little Collie Shepherd — design system (T-041)

   One source of truth for the whole app's look. Every colour, type size,
   space, radius, and elevation is a CSS custom property declared here; page
   templates consume the tokens and never hardcode a value. Restyling the app
   is editing this token block, not hunting across templates.

   Brand: warm, protective, humanistic — built *for* targeted/marginalised
   creators. A collie, not a Cerberus. The palette + type are propagated from
   the littlecollie.com house reference (warm sand ground, terracotta accent,
   Newsreader serif + Inter sans), not invented here.

   Themes: light is the default (matches the reference) and is what every
   first visit gets, whatever the OS is set to. Dark is derived and
   first-class — same warm family, not a cold inversion — but opt-in: it
   applies only when [data-theme="dark"] is on <html>, set by the theme
   toggle in base.html and remembered in localStorage.
   ========================================================================= */

/* ---- Webfonts — vendored, not linked (T-108) ------------------------------
   Newsreader and Inter ship *with* the app. Until T-108 they were named in the
   type tokens below and nothing loaded them, so the brand type appeared only on
   a machine that happened to have the families installed system-wide and every
   other visitor silently got the fallback stack. Naming a family is not
   shipping it; these rules are what ship it.

   Self-hosted rather than pulled from a font service, deliberately: a CDN link
   is a third-party request on every page load carrying the visitor's IP to a
   party this app otherwise never contacts, and it would owe the T-046 consent
   registry an entry. Vendored files cost bytes once, then cache.

   Variable files, weight axis 400–700 (the sheet uses 400/500/600/700), latin
   + latin-ext only — the catalogue is en-only; a locale needing Cyrillic or
   Greek adds its subsets then. No italic file: the sheet's one italic rule
   (.qc-summary--empty) synthesises, as it already did. Licences sit beside the
   files (both SIL OFL 1.1).

   src paths are relative to *this stylesheet*, so they resolve under /static/.
   They cannot go through asset_url() — CSS has no access to the Jinja helper —
   so a future font revision wants a new filename, not new bytes under the old
   one. tests/test_webfonts.py fails if a token names a family with no rule
   here, or if a src points at a file that is not in the repo.
   ------------------------------------------------------------------------ */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/newsreader-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Newsreader';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/newsreader-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ---- Light theme (default) — seeded from the littlecollie.com reference --- */
:root {
  /* Surfaces (warm sand ground → lighter as they rise toward the viewer) */
  --lc-bg:            #f0e8dc;  /* page ground */
  --lc-surface:       #faf7f3;  /* cards, tables, nav */
  --lc-surface-2:     #f4ece1;  /* table headers, insets, subtle raise */
  --lc-surface-3:     #ebe0d0;  /* pressed / hover wells */

  /* Ink */
  --lc-text:          #15110f;  /* primary text */
  --lc-text-muted:    #6b5d4f;  /* secondary text, labels (AA on all surfaces) */
  --lc-text-subtle:   #776654;  /* tertiary, timestamps (AA 4.5:1 on bg+surface) */

  /* Lines */
  --lc-border:        #e0d4c2;  /* default hairline */
  --lc-border-strong: #cbba9f;  /* emphasised edge, focus rings on fields */

  /* Brand accent — terracotta (primary action, focus, active).
     Three roles, split for AA: `--lc-accent` is the brand fill/border/ring hue
     (non-text UI, needs only 3:1); `--lc-accent-text` is a darkened terracotta
     for accent-coloured *text/icons* (≥4.5:1 on bg, surface, and the weak tint);
     `--lc-accent-strong` is a fill deep enough for white `--lc-on-accent` text
     (primary buttons) to clear 4.5:1. Same hue family — a legibility split, not
     a redesign (T-042; brand look is T-041). */
  --lc-accent:        #c66b4f;
  --lc-accent-hover:  #b1573b;
  --lc-accent-text:   #9e4429;  /* accent as text/icon (5.0–5.9:1 on light) */
  --lc-accent-strong: #a85436;  /* accent fill under white text (5.1:1) */
  --lc-accent-weak:   #f3e2d9;  /* tint fill behind accent content */
  --lc-on-accent:     #fffaf5;  /* text/icons on an accent-strong fill */

  /* Semantic — calm, not neon. Each pairs a readable fg with a soft tint bg.
     fg values are tuned to clear AA 4.5:1 on both their weak tint and surface. */
  --lc-success:       #457353;  --lc-success-weak: #e4eee2;
  --lc-danger:        #b3402f;  --lc-danger-weak:  #f6e2dd;
  --lc-warning:       #8c6016;  --lc-warning-weak: #f4e9d2;
  --lc-info:          #3a6a8c;  --lc-info-weak:    #e0eaf0;

  /* Source hues (kept distinct but pulled toward the warm palette) */
  --lc-youtube:       #b83c2a;  --lc-youtube-weak:   #f6e2dd;
  --lc-wordpress:     #3a6a8c;  --lc-wordpress-weak: #e0eaf0;
  --lc-patreon:       #755d8f;  --lc-patreon-weak:   #ece5f2;
  --lc-instagram:     #a02a6e;  --lc-instagram-weak: #f7e3ee;
  --lc-facebook:      #2f4f9e;  --lc-facebook-weak:  #e3e8f6;

  /* Type */
  --lc-font-serif: "Newsreader", "Iowan Old Style", "Palatino Linotype",
                   Palatino, Georgia, "Times New Roman", serif;
  --lc-font-sans:  "Inter", system-ui, -apple-system, "Segoe UI", Roboto,
                   "Helvetica Neue", Arial, sans-serif;
  --lc-font-mono:  "SFMono-Regular", ui-monospace, "Consolas", monospace;

  /* Type scale (1.20 minor-third, base 15px) */
  --lc-text-xs:   0.694rem;
  --lc-text-sm:   0.833rem;
  --lc-text-base: 1rem;
  --lc-text-md:   1.2rem;
  --lc-text-lg:   1.44rem;
  --lc-text-xl:   1.728rem;
  --lc-text-2xl:  2.074rem;

  /* Space scale (4px base) */
  --lc-space-1: 4px;   --lc-space-2: 8px;   --lc-space-3: 12px;
  --lc-space-4: 16px;  --lc-space-5: 24px;  --lc-space-6: 32px;
  --lc-space-7: 48px;  --lc-space-8: 64px;

  /* Radius */
  --lc-radius-sm: 6px;
  --lc-radius:    10px;
  --lc-radius-lg: 16px;
  --lc-radius-pill: 999px;

  /* Elevation — soft, warm-tinted shadows (protective, not harsh) */
  --lc-shadow-sm: 0 1px 2px rgba(60, 40, 24, 0.06);
  --lc-shadow:    0 2px 8px rgba(60, 40, 24, 0.08);
  --lc-shadow-lg: 0 12px 40px rgba(40, 26, 14, 0.18);

  /* Motion */
  --lc-motion: 0.15s ease;

  /* ---- Legacy aliases -----------------------------------------------------
     The pre-T-041 templates reference these names directly. Mapping them onto
     the new tokens re-skins every existing page for free and keeps this task
     visual-only; page-by-page rollout then removes the aliases in favour of the
     semantic names. Do not add new uses of these. */
  --bg: var(--lc-bg);
  --bg2: var(--lc-surface);
  --bg3: var(--lc-surface-2);
  --border: var(--lc-border);
  --text: var(--lc-text);
  --muted: var(--lc-text-muted);
  --primary: var(--lc-accent);
  --primary-hover: var(--lc-accent-hover);
  --green: var(--lc-success);
  --red: var(--lc-danger);
  --yellow: var(--lc-warning);
  --orange: #a0551b;  /* AA 4.5:1 on warning-weak + surface */
  --radius: var(--lc-radius-sm);
  --font: var(--lc-font-sans);
  --mono: var(--lc-font-mono);
}

/* ---- Dark theme — derived, same warm family (a dim room, not a cold one) -- */
/* Applied only when the toggle sets [data-theme="dark"]. The OS preference
   deliberately does NOT select it: light is the default for every first
   visit, dark is an explicit opt-in (see the head script in base.html). */
:root[data-theme="dark"] {
  --lc-bg:            #17120e;
  --lc-surface:       #211a15;
  --lc-surface-2:     #2b221b;
  --lc-surface-3:     #362a21;

  --lc-text:          #f0e8dc;
  --lc-text-muted:    #b3a493;
  --lc-text-subtle:   #9a8b7d;  /* AA 4.5:1 on dark surface/bg */

  --lc-border:        #372c23;
  --lc-border-strong: #4d3f33;

  --lc-accent:        #db8a6f;
  --lc-accent-hover:  #e8a288;
  --lc-accent-text:   #db8a6f;  /* light terracotta already clears 4.5:1 on dark */
  --lc-accent-strong: #db8a6f;  /* dark on-accent ink on it clears 4.5:1 */
  --lc-accent-weak:   #3a271e;
  --lc-on-accent:     #17120e;

  --lc-success:       #7fae8c;  --lc-success-weak: #223026;
  --lc-danger:        #e18472;  --lc-danger-weak:  #3a231d;
  --lc-warning:       #d6a445;  --lc-warning-weak: #362a17;
  --lc-info:          #7fabc8;  --lc-info-weak:    #1f2c34;

  --lc-youtube:       #e6836f;  --lc-youtube-weak:   #3a231d;
  --lc-wordpress:     #7fabc8;  --lc-wordpress-weak: #1f2c34;
  --lc-patreon:       #b299cf;  --lc-patreon-weak:   #2a2333;
  --lc-instagram:     #e08cbb;  --lc-instagram-weak: #38202d;
  --lc-facebook:      #93aae8;  --lc-facebook-weak:  #1e2436;

  --lc-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30);
  --lc-shadow:    0 2px 8px rgba(0, 0, 0, 0.40);
  --lc-shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.55);

  --orange: #d68a3f;
}

/* ============================================================================
   Base
   ========================================================================= */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 15px; }
body {
  font-family: var(--lc-font-sans);
  background: var(--lc-bg);
  color: var(--lc-text);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Flex column so the site footer (T-078) pins to the viewport bottom on
     short pages via `margin-top: auto`; content lanes are unaffected. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
h1, h2, h3, h4 { font-family: var(--lc-font-serif); font-weight: 600; line-height: 1.2; letter-spacing: -0.01em; }
/* Inline/prose links are underlined by default so they never rely on colour
   alone (WCAG 1.4.1). Component links that are already distinct by shape or
   placement — nav items, tabs, buttons, pills, cards, icon controls — opt out
   below. Accent-as-text uses the darkened `--lc-accent-text` for AA contrast. */
a { color: var(--lc-accent-text); text-decoration: underline; text-underline-offset: 0.15em; }
a:hover { text-decoration: underline; }
.nav-brand, .nav-links a, .tab, .sort-tab, .btn, .settings-tile, .badge,
.cat-label, .icon-link, .icon-btn, .theme-toggle, .health-toggle, .pagination a,
.stat-card--link, .video-group-title a,
/* `.th-sort` is a <button> where sorting happens in the browser and an <a> where
   it happens on the server (the paged staff directory). The button never
   underlined; the link would, and a column heading is a heading either way. */
.th-sort {
  text-decoration: none;
}
kbd {
  font-family: var(--lc-font-mono); font-size: 0.8em;
  background: var(--lc-surface-2); border: 1px solid var(--lc-border);
  border-radius: 4px; padding: 0 4px; color: var(--lc-text-muted);
}
input, select, textarea, button { font: inherit; }

/* Disclosure drawers (T-081). A <details> is a control and its <summary> is the
   whole of it — the only thing a reader can see, focus or activate. `.drawer` is
   the opt-in: it carries the house treatment (muted, weighted label) and is the
   only thing that drops the native marker. A <details> without it keeps the
   browser's triangle — plainer than the house style, still unmistakably a
   control. This reset was global until T-081, which meant a summary nobody
   remembered to style shipped as body text (T-075's override matrix, T-080).
   Every <details> ships a <summary>, and its label resolves through `t()`
   (NF-09) — omit it and the browser substitutes its own untranslated "Details".
   See /styleguide → Disclosure Drawer. */
details > summary { cursor: pointer; }
.drawer > summary { list-style: none; font-weight: 600; color: var(--lc-text-muted); }
.drawer > summary::-webkit-details-marker { display: none; }
::selection { background: var(--lc-accent-weak); color: var(--lc-text); }
:focus-visible { outline: 2px solid var(--lc-accent); outline-offset: 2px; }
main:focus { outline: none; }  /* the skip-link target shouldn't ring on focus */

/* Skip link — first tab stop; slides into view only when focused (WCAG 2.4.1). */
.skip-link {
  position: absolute; left: var(--lc-space-3); top: -60px; z-index: 300;
  background: var(--lc-surface); color: var(--lc-accent-text);
  border: 1px solid var(--lc-border-strong); border-radius: var(--lc-radius-sm);
  padding: 8px 14px; font-size: var(--lc-text-sm); font-weight: 600;
  text-decoration: none; transition: top var(--lc-motion);
}
.skip-link:focus { top: var(--lc-space-3); outline: 2px solid var(--lc-accent); outline-offset: 2px; }

/* Dry-run banner */
.dry-run-banner {
  background: var(--lc-warning-weak);
  color: var(--lc-warning);
  text-align: center;
  padding: 8px 16px;
  font-weight: 600;
  font-size: var(--lc-text-sm);
  border-bottom: 1px solid var(--lc-warning);
  position: sticky;
  top: 0;
  z-index: 100;
}

/* Passkey nudge bar (T-057): a calm, dismissible accent strip. Tokens only. */
.pk-nudge {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--lc-space-3);
  background: var(--lc-accent-weak);
  color: var(--lc-text);
  padding: 8px 16px;
  font-size: var(--lc-text-sm);
  border-bottom: 1px solid var(--lc-accent);
}
.pk-nudge__text { font-weight: 600; }
.pk-nudge__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-2); }
.pk-nudge__actions form { margin: 0; }

/* T-046 consent choice: both paths are equal-weight controls. */
.consent-banner {
  position: sticky; bottom: 0; z-index: 200;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--lc-space-3); margin: 0; padding: 10px 16px;
  background: var(--lc-surface); color: var(--lc-text); border-top: 1px solid var(--lc-border-strong);
  font-size: var(--lc-text-sm); box-shadow: var(--lc-shadow);
}
.consent-banner__copy { max-width: 48rem; }
.consent-banner__actions { display: flex; flex-wrap: wrap; gap: var(--lc-space-2); }
.consent-banner__actions .btn { min-width: 9rem; justify-content: center; }
.legal-doc { max-width: 54rem; margin: 0 auto; }
.legal-draft { margin-bottom: var(--lc-space-4); padding: var(--lc-space-3); border: 1px solid var(--lc-warning); background: var(--lc-warning-weak); color: var(--lc-warning); border-radius: var(--lc-radius-sm); }
.legal-doc__meta, .legal-doc__links { display: flex; flex-wrap: wrap; gap: var(--lc-space-3); margin: var(--lc-space-4) 0; color: var(--lc-text-muted); font-size: var(--lc-text-sm); }
.legal-doc__body { line-height: 1.6; }
.legal-doc__body h1 { margin-bottom: var(--lc-space-4); }
.legal-doc__body h2, .legal-doc__body h3, .legal-doc__body h4 { margin-top: var(--lc-space-7); margin-bottom: var(--lc-space-3); }
.legal-doc__body p, .legal-doc__body blockquote, .legal-doc__body ul, .legal-doc__body ol { margin-bottom: var(--lc-space-4); }
.legal-doc__body ul, .legal-doc__body ol { padding-left: var(--lc-space-5); }
.legal-doc__body > :first-child { margin-top: 0; }
.legal-doc__body table { width: 100%; border-collapse: collapse; margin: var(--lc-space-5) 0; }
.legal-doc__body th, .legal-doc__body td { padding: var(--lc-space-2); border: 1px solid var(--lc-border); text-align: left; }
.consent-manage, .legal-accept { max-width: 42rem; margin: var(--lc-space-6) auto; padding: var(--lc-space-5); border: 1px solid var(--lc-border); border-radius: var(--lc-radius-lg); background: var(--lc-surface); }

/* ============================================================================
   Nav
   ========================================================================= */
/* Mobile-first nav: the bar wraps and the links collapse behind the hamburger.
   The ≥768 range (Responsive section) restores the single-row inline nav. */
.nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 var(--lc-space-4);
  padding: 0 var(--lc-space-4);
  min-height: 56px;
  background: var(--lc-surface);
  border-bottom: 1px solid var(--lc-border);
}
.nav-brand {
  font-family: var(--lc-font-serif);
  font-weight: 600; color: var(--lc-text); font-size: var(--lc-text-md);
  display: flex; align-items: center; gap: var(--lc-space-2);
  letter-spacing: -0.01em; height: 56px;
}
.nav-brand { color: var(--lc-text); -webkit-tap-highlight-color: transparent; }
.nav-brand:hover, .nav-brand:visited, .nav-brand:active { color: var(--lc-text); text-decoration: none; }
.nav-logo { height: 30px; width: 30px; flex-shrink: 0; }
/* Links + theme toggle share one flex row that fills the bar; the toggle sits
   at the far right via margin-left:auto. On mobile this whole row collapses
   into the hamburger, so the toggle rides inside the menu (not the top bar). */
/* Links + theme toggle: stacked and hidden by default; the hamburger (:checked)
   reveals them. The ≥768 range turns this back into an inline row. */
.nav-links {
  display: none; flex-direction: column; align-items: stretch;
  width: 100%; gap: 2px; padding: 4px 0 12px; flex-basis: 100%; min-width: 0;
}
.nav-toggle-cb:checked ~ .nav-links { display: flex; }
.nav-links a {
  padding: 10px 12px; border-radius: var(--lc-radius-sm);
  color: var(--lc-text-muted); font-size: var(--lc-text-sm); font-weight: 500;
  transition: background var(--lc-motion), color var(--lc-motion);
}
.nav-links a:hover { color: var(--lc-text); text-decoration: none; background: var(--lc-surface-2); }
.nav-links a.active { color: var(--lc-accent-text); background: var(--lc-accent-weak); }
/* The staff console's way back to the app (T-050). Not a section of the console,
   so it does not sit with the section links: from the ≥768 range it takes the
   free space and carries itself, the theme toggle and log-out to the right end
   of the bar. In the collapsed mobile menu the row is a column, so it simply
   falls last — which is the same reading. */
.nav-exit { color: var(--lc-text-subtle); }
.nav-exit:hover { color: var(--lc-text); }
/* Visually-hidden checkbox drives the CSS-only menu; the label is the hamburger. */
.nav-toggle-cb { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; }
.nav-toggle {
  display: flex; align-items: center; justify-content: center;
  margin-left: auto; width: 44px; height: 56px;
  font-size: 1.5em; color: var(--lc-text-muted); cursor: pointer; user-select: none;
}
.nav-toggle:hover { color: var(--lc-text); }
.nav-toggle-cb:focus-visible ~ .nav-toggle {
  color: var(--lc-text); outline: 2px solid var(--lc-accent); outline-offset: -2px;
}

/* Theme toggle. Mobile-first: rides inside the collapsed menu as a labelled
   row; the ≥768 range collapses it to the icon-only button in the top bar. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  height: 34px; margin: 8px 0 0; width: auto; align-self: flex-start;
  padding: 0 12px; gap: 8px;
  border: 1px solid var(--lc-border); border-radius: var(--lc-radius-sm);
  background: var(--lc-surface); color: var(--lc-text-muted);
  cursor: pointer; transition: background var(--lc-motion), color var(--lc-motion);
}
.theme-toggle::after { content: "Theme"; font-size: var(--lc-text-sm); }
.theme-toggle:hover { background: var(--lc-surface-2); color: var(--lc-text); }
/* The icon names the destination: moon = "switch to dark". Unset resolves to
   light, so the moon is the default face. */
.theme-toggle .theme-toggle__sun { display: none; }
.theme-toggle .theme-toggle__moon { display: block; }
:root[data-theme="dark"] .theme-toggle .theme-toggle__sun { display: block; }
:root[data-theme="dark"] .theme-toggle .theme-toggle__moon { display: none; }

/* System Health control (T-137). Mobile-first like the theme toggle beside it:
   a labelled row inside the collapsed menu, collapsing to an icon-only button in
   the top bar at ≥768. The label is real markup rather than CSS `content`, so it
   is translatable and is the control's accessible name in both layouts. */
.health-toggle {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  height: 34px; margin: 8px 0 0; width: auto; align-self: flex-start;
  padding: 0 12px; gap: 8px;
  border: 1px solid var(--lc-border); border-radius: var(--lc-radius-sm);
  background: var(--lc-surface); color: var(--lc-text-muted);
  cursor: pointer; transition: background var(--lc-motion), color var(--lc-motion);
}
.health-toggle:hover { background: var(--lc-surface-2); color: var(--lc-text); }
.health-toggle__label { font-size: var(--lc-text-sm); }
/* Fault face. Colour is the glance; the sr-only state text in the button is what
   carries the same fact to a reader who cannot use it (T-042). */
.health-toggle--fault {
  color: var(--lc-danger); border-color: var(--lc-danger); background: var(--lc-danger-weak);
}
.health-toggle--fault:hover { color: var(--lc-danger); background: var(--lc-danger-weak); }

/* Log-out control (T-073). Mobile-first, it rides inside the collapsed menu
   below the theme toggle; the ≥768 range floats it to the right end of the bar. */
.nav-logout { margin: var(--lc-space-2) 0 0; align-self: flex-start; }

/* ============================================================================
   Layout
   ========================================================================= */
/* Mobile-first: base targets the smallest screen; the six min-width ranges in
   the Responsive section layer larger-screen enhancements on top (T-042). */
/* `width: 100%` matters: body is a flex column (T-078 footer), and a flex
   item with auto side margins doesn't stretch — without it .main shrinks to
   fit-content and narrow pages (Settings) collapse to a mobile-like column. */
.main { width: 100%; max-width: 1200px; margin: 0 auto; padding: var(--lc-space-4) var(--lc-space-4) var(--lc-space-7); }

.page-header { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--lc-space-3); margin-bottom: var(--lc-space-6); }
.page-header h1 { font-size: var(--lc-text-2xl); }
/* Breadcrumb inside a page title: a parent link + separator, e.g.
   "Settings / Sources". The parent reads as one step back, not a headline. */
.page-header h1 .crumb { color: var(--lc-text-muted); font-weight: 600; }
.page-header h1 .crumb:hover { color: var(--lc-accent-text); text-decoration: none; }
.page-header h1 .crumb-sep { color: var(--lc-text-subtle); font-weight: 400; margin: 0 6px; }

/* Section tab bar (T-063) — the sub-nav inside Pens and Train. Folder-style
   tabs sitting on a hairline; the active tab lifts onto the page surface. */
.tabs { display: flex; gap: 2px; flex-wrap: wrap; border-bottom: 1px solid var(--lc-border); margin-bottom: var(--lc-space-5); }
.tab {
  padding: 8px 18px; border-radius: var(--lc-radius-sm) var(--lc-radius-sm) 0 0;
  color: var(--lc-text-muted); font-weight: 600; font-size: var(--lc-text-sm);
  border: 1px solid transparent; border-bottom: none; margin-bottom: -1px;
  transition: background var(--lc-motion), color var(--lc-motion);
}
.tab:hover { color: var(--lc-text); background: var(--lc-surface-2); text-decoration: none; }
.tab.active { color: var(--lc-accent-text); background: var(--lc-surface); border-color: var(--lc-border); border-bottom-color: var(--lc-surface); }
/* Short context line under a section's tab bar. */
.section-note { margin: calc(-1 * var(--lc-space-3)) 0 var(--lc-space-5); font-size: var(--lc-text-sm); }
.section-note-row { display: flex; align-items: center; justify-content: space-between; gap: var(--lc-space-3); flex-wrap: wrap; }
.section-note-row .section-note { margin-bottom: var(--lc-space-4); }

.section { margin-bottom: var(--lc-space-6); }
.section h2 {
  font-family: var(--lc-font-sans);
  font-size: var(--lc-text-sm); font-weight: 600; color: var(--lc-text-muted);
  text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: var(--lc-space-3);
}

/* Destructive-action zone (T-049 account deletion): visibly fenced off. */
.section--danger {
  border: 1px solid var(--lc-danger);
  border-radius: var(--lc-radius-sm);
  padding: var(--lc-space-4);
}
.section--danger h2 { color: var(--lc-danger); }

.two-col { display: grid; grid-template-columns: 1fr; gap: var(--lc-space-5); margin-bottom: var(--lc-space-6); }

/* Global site footer (T-078): the persistent route to the legal documents and
   the cookie-consent control once the banner is gone. Quiet by design — muted
   ink on the card surface — but AA: `--lc-text-muted` clears 4.5:1 on
   `--lc-surface`, links keep the global underline + focus ring. `margin-top:
   auto` pins it to the viewport bottom on short pages (body is a flex column). */
.site-footer {
  margin-top: auto;
  background: var(--lc-surface);
  border-top: 1px solid var(--lc-border);
  padding: var(--lc-space-4);
  font-size: var(--lc-text-sm);
}
.site-footer__links {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--lc-space-2) var(--lc-space-5);
  max-width: 1200px; margin: 0 auto;
}
/* Footer links opt out of the global underline (grouped in a labelled nav
   landmark, so not colour-alone — same rationale as the top nav); the
   underline returns on hover/focus as the interaction affordance. */
.site-footer a { color: var(--lc-text-muted); text-decoration: none; }
.site-footer a:hover, .site-footer a:focus-visible { color: var(--lc-text); text-decoration: underline; }

/* ============================================================================
   Connected sources — one card per platform (T-083)
   ========================================================================= */
/* Each source is a self-contained card because each is independently gated:
   separately billed, separately approved by the platform, separately configured
   on this server. A shared container would draw them as moving together. The
   grid auto-fits, so a build with two sources and a build with five both look
   deliberate without a breakpoint per count. */
.source-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: var(--lc-space-4);
  margin-bottom: var(--lc-space-6);
}
.source-card {
  display: flex; flex-direction: column;
  background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); box-shadow: var(--lc-shadow-sm);
  padding: var(--lc-space-4) var(--lc-space-5);
}
.source-card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--lc-space-3); margin-bottom: var(--lc-space-3);
}
/* The platform's own name, so it reads as a title rather than as the muted
   uppercase section label `.section h2` applies. */
.source-card__title {
  font-family: var(--lc-font-sans);
  font-size: var(--lc-text-base); font-weight: 600; color: var(--lc-text);
  text-transform: none; letter-spacing: 0; margin: 0;
}
/* The body grows; the actions stay pinned to the bottom, so cards holding
   different numbers of connections still line their buttons up. */
.source-card__body { flex: 1 1 auto; }
.source-card__body .table { margin-bottom: 0; }
/* A card is narrower than a page, and the values in these tables are addresses
   and account names with no spaces to break on. Let them wrap rather than making
   every card holding a long URL scroll sideways — but not the action cell, or a
   squeezed column breaks the button's own label down the middle. */
.source-card__body .table td { overflow-wrap: anywhere; }
.source-card__body .table td:last-child { overflow-wrap: normal; white-space: nowrap; }
.source-card__actions {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--lc-space-2);
  margin-top: var(--lc-space-4);
  padding-top: var(--lc-space-3);
  border-top: 1px solid var(--lc-border);
}
.source-card__actions p { margin: 0; }
/* One CTA shape for every source. Before this the consent-screen sources drew an
   inline-width link and the site-token one drew a full-width submit inside a
   form, so the cards implied the flows differed in effort as well as in kind. */
.source-card__cta { align-self: stretch; text-align: center; }
/* A precondition the platform imposes (e.g. the account type it will talk to) —
   quiet, but inside the card it applies to rather than in a page-wide footnote
   the reader has to match up to a platform themselves. */
.source-card__requirement,
.source-card__shared {
  margin: var(--lc-space-3) 0 0;
  font-size: var(--lc-text-sm); color: var(--lc-text-muted);
}
.source-card__shared { margin-top: 0; }
/* A platform the tenant's plan excludes (T-092). Recessed and dashed, never
   hidden: the card is how a customer discovers the platform exists at all. The
   distinction is carried by surface and border rather than by dimming the text —
   an `opacity` that reads as "not yours yet" also drops every string on the card
   below the AA contrast T-042 holds the rest of the app to, and it would grey
   out the tier badge and the upgrade CTA, which are the actionable parts. */
.source-card--locked { background: var(--lc-surface-2); border-style: dashed; }
/* The tier that unlocks something — an offer, not a status, so it borrows the
   accent rather than a semantic success/danger tint. */
.badge--tier {
  background: var(--lc-accent-weak); color: var(--lc-accent-text);
  border-color: var(--lc-accent);
}
/* The plan a deep-linked upgrade prompt is pointing at. */
.plan-card.is-intent { border-color: var(--lc-accent); box-shadow: var(--lc-shadow-sm); }

/* ============================================================================
   Stat cards
   ========================================================================= */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--lc-space-4); margin-bottom: var(--lc-space-6); }
.stat-card {
  background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); padding: var(--lc-space-4) var(--lc-space-5);
  box-shadow: var(--lc-shadow-sm);
}
.stat-card--alert { border-color: var(--lc-warning); }
/* Clickable card (T-137): the whole card is the target, not a link inside it —
   one tab stop, one accessible name, one focus ring. `display: block` on the <a>
   keeps the card's own box model; the colour reset stops the global link colour
   repainting the number. */
.stat-card--link {
  display: block; color: inherit;
  transition: background var(--lc-motion), border-color var(--lc-motion), box-shadow var(--lc-motion);
}
.stat-card--link:hover { background: var(--lc-surface-2); box-shadow: var(--lc-shadow); }
.stat-card--link:visited, .stat-card--link:active { color: inherit; }
.stat-value { font-family: var(--lc-font-serif); font-size: var(--lc-text-2xl); font-weight: 600; line-height: 1.1; }
.stat-label { color: var(--lc-text-muted); font-size: var(--lc-text-sm); margin-top: var(--lc-space-1); }
.text-green { color: var(--lc-success); }
.text-red { color: var(--lc-danger); }
.text-amber { color: var(--lc-warning); }

/* Sparkline */
.sparkline {
  display: flex; gap: var(--lc-space-2); align-items: flex-end; height: 88px;
  background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); padding: var(--lc-space-3) var(--lc-space-4) var(--lc-space-2);
  box-shadow: var(--lc-shadow-sm);
}
.spark-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: var(--lc-space-1); height: 100%; }
.spark-bar-wrap { flex: 1; display: flex; align-items: flex-end; width: 100%; }
.spark-bar { width: 100%; min-height: 2px; background: var(--lc-accent); border-radius: 3px 3px 0 0; transition: height 0.2s; }
.spark-label { font-size: var(--lc-text-xs); color: var(--lc-text-subtle); white-space: nowrap; }

/* ============================================================================
   Tables
   ========================================================================= */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Nothing butts up against the bottom of a table. A table is a hard visual edge,
   and a paragraph or a stat list starting immediately under it reads as part of
   the last row rather than as the next thing.

   An adjacent-sibling rule rather than a bottom margin on the table itself,
   because most tables in this app are the last thing in their `.section` and
   already get that section's space — adding a margin as well would double it
   everywhere to fix the few places something actually follows. This applies only
   where there IS a next sibling, which is exactly the case that was wrong. */
.table-scroll + * { margin-top: var(--lc-space-5); }
.table {
  width: 100%; border-collapse: collapse; background: var(--lc-surface);
  border: 1px solid var(--lc-border); border-radius: var(--lc-radius); overflow: hidden;
  box-shadow: var(--lc-shadow-sm);
}
.table th {
  background: var(--lc-surface-2); color: var(--lc-text-muted);
  font-size: var(--lc-text-xs); text-transform: uppercase; letter-spacing: 0.05em;
  padding: 10px 14px; text-align: left; font-weight: 600;
}
.table td { padding: 10px 14px; border-top: 1px solid var(--lc-border); vertical-align: top; }
.table tr:hover td { background: var(--lc-surface-2); }
.table--log td, .table--inject td { font-size: var(--lc-text-sm); }

/* Sortable column headers (T-070). Each `<th data-sort-type>` wraps its label in
   a full-cell `<button.th-sort>`: keyboard-operable, accessible name = the label.
   JS only flips `aria-sort` on click; the direction glyph is derived from it here
   so state lives in one place. Neutral columns show a faint ↕ affordance. */
.table--sortable th[data-sort-type] { padding: 0; }
.th-sort {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 10px 14px; margin: 0; background: none; border: 0; cursor: pointer;
  font: inherit; font-weight: 600; color: inherit;
  text-transform: inherit; letter-spacing: inherit; text-align: left;
}
.th-sort:hover { background: var(--lc-surface-3); color: var(--lc-text); text-decoration: none; }
/* The link form has to be told; `a:visited` would otherwise recolour a heading
   the moment somebody sorted by it once. */
a.th-sort, a.th-sort:visited, a.th-sort:active { color: inherit; }

/* A filter row above a table: label, field and button on one line with room to
   breathe, wrapping to a column on a narrow screen. */
.filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-3); }
.th-sort:focus-visible { outline: 2px solid var(--lc-accent); outline-offset: -2px; }
.th-sort__ind { flex: 0 0 auto; font-size: 0.9em; line-height: 1; }
.th-sort__ind::after { content: '↕'; opacity: 0.4; }
th[aria-sort] .th-sort { color: var(--lc-text); }
th[aria-sort="ascending"] .th-sort__ind::after { content: '▲'; opacity: 1; }
th[aria-sort="descending"] .th-sort__ind::after { content: '▼'; opacity: 1; }

/* ============================================================================
   Badges — soft tinted pills off the semantic tokens
   ========================================================================= */
.badge {
  display: inline-block; padding: 2px 9px; border-radius: var(--lc-radius-pill);
  font-size: var(--lc-text-xs); font-weight: 600; letter-spacing: 0.02em;
  background: var(--lc-surface-2); color: var(--lc-text-muted); border: 1px solid var(--lc-border);
}
.badge--high { background: var(--lc-danger-weak); color: var(--lc-danger); border-color: var(--lc-danger); }
.badge--medium { background: var(--lc-warning-weak); color: var(--lc-warning); border-color: var(--lc-warning); }
.badge--category, .badge--keyword, .badge--handle, .badge--pattern, .badge--regex, .badge--author_url {
  background: var(--lc-accent-weak); color: var(--lc-accent-text); border-color: var(--lc-accent);
}
.badge--score { background: var(--lc-surface-2); color: var(--lc-text); }
.badge--likes { background: var(--lc-success-weak); color: var(--lc-success); border-color: var(--lc-success); }
.badge--stage { background: var(--lc-surface-2); color: var(--lc-text-muted); }
.badge--source-youtube { background: var(--lc-youtube-weak); color: var(--lc-youtube); border-color: var(--lc-youtube); }
.badge--source-wordpress { background: var(--lc-wordpress-weak); color: var(--lc-wordpress); border-color: var(--lc-wordpress); }
.badge--source-patreon { background: var(--lc-patreon-weak); color: var(--lc-patreon); border-color: var(--lc-patreon); }
.badge--source-instagram { background: var(--lc-instagram-weak); color: var(--lc-instagram); border-color: var(--lc-instagram); }
.badge--source-facebook { background: var(--lc-facebook-weak); color: var(--lc-facebook); border-color: var(--lc-facebook); }
.badge--source-moderator { background: var(--lc-success-weak); color: var(--lc-success); border-color: var(--lc-success); }
.badge--source-rule-workshop { background: var(--lc-warning-weak); color: var(--lc-warning); border-color: var(--lc-warning); }
.badge--action-auto-removed { background: var(--lc-danger-weak); color: var(--lc-danger); border-color: var(--lc-danger); }
.badge--action-approved-removed { background: var(--lc-danger-weak); color: var(--lc-danger); border-color: var(--lc-danger); }
.badge--action-queued-review { background: var(--lc-warning-weak); color: var(--lc-warning); border-color: var(--lc-warning); }
.badge--action-stage3-queued { background: var(--lc-warning-weak); color: var(--orange); border-color: var(--orange); }
.badge--action-approved-kept, .badge--action-passed, .badge--action-restored { background: var(--lc-success-weak); color: var(--lc-success); border-color: var(--lc-success); }
.badge--unavailable { background: var(--lc-danger-weak); color: var(--lc-danger); border-color: var(--lc-danger); }
.badge--parse-failure { background: var(--lc-warning-weak); color: var(--orange); border-color: var(--orange); }
.badge--retry-success { background: var(--lc-success-weak); color: var(--lc-success); border-color: var(--lc-success); }

/* ============================================================================
   Utilities
   ========================================================================= */
.muted { color: var(--lc-text-muted); }
.mono { font-family: var(--lc-font-mono); font-size: 0.85em; }
.text-truncate { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nowrap { white-space: nowrap; }  /* keep a glyph + its label on one line (e.g. ● FAILED) */
/* Visually hidden but exposed to assistive tech (skip-link target, live region). */
.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;
}
.empty-state {
  text-align: center; padding: var(--lc-space-8) var(--lc-space-5);
  color: var(--lc-text-muted); font-size: var(--lc-text-md);
  background: var(--lc-surface); border: 1px dashed var(--lc-border-strong);
  border-radius: var(--lc-radius-lg);
}
/* An empty state standing directly above the stat grid takes the grid's own gap
   as its spacing (T-137), so the quiet-state message reads as the first row of
   the same rhythm rather than a block shoved against the cards. Scoped to that
   position — the queue's empty states sit alone and keep the base spacing. */
.empty-state--stat-gap { margin-bottom: var(--lc-space-4); }
.queue-hint { margin-bottom: var(--lc-space-4); font-size: var(--lc-text-sm); }

/* ============================================================================
   Buttons
   ========================================================================= */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 16px; border-radius: var(--lc-radius-sm); font-size: var(--lc-text-sm);
  font-weight: 600; cursor: pointer; border: 1px solid var(--lc-border-strong);
  background: var(--lc-surface); color: var(--lc-text);
  transition: background var(--lc-motion), border-color var(--lc-motion), color var(--lc-motion);
}
.btn:hover { background: var(--lc-surface-2); text-decoration: none; }
.btn--primary { background: var(--lc-accent-strong); border-color: var(--lc-accent-strong); color: var(--lc-on-accent); }
.btn--primary:hover { background: var(--lc-accent-hover); border-color: var(--lc-accent-hover); }
.btn--danger { background: var(--lc-danger-weak); border-color: var(--lc-danger); color: var(--lc-danger); }
.btn--danger:hover { background: var(--lc-danger); color: var(--lc-on-accent); }
.btn--success { background: var(--lc-success-weak); border-color: var(--lc-success); color: var(--lc-success); }
.btn--success:hover { background: var(--lc-success); color: var(--lc-on-accent); }
.btn--warning { background: var(--lc-warning-weak); border-color: var(--lc-warning); color: var(--lc-warning); }
.btn--warning:hover { background: var(--lc-warning); color: var(--lc-on-accent); }
.btn--secondary { background: var(--lc-surface-2); color: var(--lc-text-muted); border-color: var(--lc-border); }
.btn--secondary:hover { background: var(--lc-surface-3); color: var(--lc-text); }
.btn--sm { padding: 6px 12px; font-size: var(--lc-text-xs); }  /* touch base; tighter at ≥768 */

/* Settings action footer (T-076): one convention for a page/form's controls — the
   primary/approve control on the right, a button-styled Back on the left, divided from
   the content above. On narrow viewports it stacks with the primary on top and Back at
   the bottom, both full-width. Back-only pages get a single left-aligned control. */
.form-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-3);
  justify-content: space-between;
  margin-top: var(--lc-space-6); padding-top: var(--lc-space-4);
  border-top: 1px solid var(--lc-border);
}
@media (max-width: 767px) {
  .form-actions { flex-direction: column-reverse; align-items: stretch; }
  .form-actions .btn { width: 100%; justify-content: center; }
}

/* Same-day look-back offer panel (T-090). Stacked panels above the source grid;
   its own action row rather than .form-actions, which is the settings *page*
   footer convention (T-076) and carries a top border this panel must not draw. */
.lookback-offers { display: flex; flex-direction: column; gap: var(--lc-space-3); }
.lookback-offer p { margin: var(--lc-space-2) 0 0; }
.lookback-offer__actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-3);
  margin-top: var(--lc-space-4);
}
@media (max-width: 767px) {
  .lookback-offer__actions { flex-direction: column-reverse; align-items: stretch; }
  .lookback-offer__actions form,
  .lookback-offer__actions .btn { width: 100%; justify-content: center; }
}

/* Comment column icons (T-028) */
.comment-actions { display: inline-flex; align-items: center; gap: 6px; }
.icon-link, .icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;  /* touch base; 30px at ≥768 */
  border: none; background: none; cursor: pointer;
  color: var(--lc-text-muted); border-radius: var(--lc-radius-sm);
  text-decoration: none; transition: background var(--lc-motion), color var(--lc-motion);
}
.icon-link:hover, .icon-btn:hover { background: var(--lc-surface-2); }
.icon-btn:hover { color: var(--lc-text); }
.icon-link--youtube { color: var(--lc-youtube); }
.icon-link--wordpress { color: var(--lc-wordpress); }
.icon-link--instagram { color: var(--lc-instagram); }
.icon-link--facebook { color: var(--lc-facebook); }
.icon-link:focus-visible, .icon-btn:focus-visible {
  outline: 2px solid var(--lc-accent); outline-offset: -2px;
}
.cmt-icon { display: block; }

/* ============================================================================
   Queue
   ========================================================================= */
.queue-list { display: flex; flex-direction: column; gap: var(--lc-space-3); }

/* Three-zone triage row (T-068). Mobile-first: the rail spans a stacked
   author-over-comment column; at ≥768 the three zones sit side by side. Padding
   lives on the zones so the rail runs flush to the rounded card edge. */
.queue-card {
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr);
  grid-template-areas: "rail author" "rail comment";
  background: var(--lc-surface); border: 1px solid var(--lc-border); border-radius: var(--lc-radius);
  overflow: hidden; outline: none;
  transition: border-color var(--lc-motion), box-shadow var(--lc-motion);
  box-shadow: var(--lc-shadow-sm);
}
.queue-card:focus, .queue-card.focused {
  border-color: var(--lc-accent);
  box-shadow: 0 0 0 3px var(--lc-accent-weak);
}
/* V/R/K mnemonic underlines show only on the targeted (accent-border) card, so
   it's clear which comment the v/r/k keys will act on. */
.queue-card u { text-decoration: none; }
.queue-card:focus u, .queue-card.focused u { text-decoration: underline; }

/* Severity rail — plain tone (Phase 1); the Phase 2 glyph fills this slot. */
.qc-rail { grid-area: rail; background: var(--lc-border-strong); }
.qc-rail--high   { background: var(--lc-danger); }
.qc-rail--medium { background: var(--lc-warning); }
.qc-rail--low    { background: var(--lc-border-strong); }

/* Author zone */
.qc-author {
  grid-area: author; padding: var(--lc-space-4) var(--lc-space-4) var(--lc-space-2);
  display: flex; flex-direction: column; gap: var(--lc-space-1);
}
.qc-author-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-2); }
.qc-author-meta { font-size: var(--lc-text-sm); margin: 0; }
.qc-watched { margin: 2px 0 0; }
.qc-anon { background: var(--lc-surface-2); color: var(--lc-text-muted); border-color: var(--lc-border-strong); text-transform: lowercase; }
.qc-author-actions { display: flex; flex-wrap: wrap; gap: var(--lc-space-2); margin-top: var(--lc-space-2); }

/* Comment zone */
.qc-comment {
  grid-area: comment;
  padding: var(--lc-space-2) var(--lc-space-4) var(--lc-space-4);
}
/* Source icon in its own top-right row — never overlaps the bubble. Zero height
   when there's no link so the bubble sits flush. */
.qc-comment-head { display: flex; justify-content: flex-end; min-height: 0; }
.qc-watch-reason { font-size: var(--lc-text-sm); margin: 0 0 var(--lc-space-2); }

/* Fog bubble. Dotted border while the comment is hidden, solid once revealed;
   the View/Hide toggle sits top-right, so the text carries right padding to
   clear it. */
.qc-bubble {
  position: relative;
  background: var(--lc-surface-2); border: 1px dashed var(--lc-border-strong);
  border-radius: var(--lc-radius-sm); padding: var(--lc-space-3);
  margin: 0 0 var(--lc-space-3);
}
.qc-bubble--revealed { border-style: solid; border-color: var(--lc-border); }
.qc-toggle {
  position: absolute; top: var(--lc-space-2); right: var(--lc-space-2);
  display: inline-flex;
}
.qc-summary, .qc-text-slot, .qc-text { padding-right: 68px; }  /* clear the toggle */
.qc-summary { margin: 0; font-size: var(--lc-text-sm); }
.qc-summary--empty { font-style: italic; }
.qc-text-slot { margin: 0; }
.qc-text { margin: 0; font-size: var(--lc-text-sm); }

/* In-situ `More` clamp for long revealed text (pure-CSS checkbox toggle). */
.qc-reveal--clamped .qc-text {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
  overflow: hidden;
}
.qc-more-cb:checked ~ .qc-text { -webkit-line-clamp: none; display: block; overflow: visible; }
.qc-more {
  display: inline-block; margin-top: var(--lc-space-1); cursor: pointer;
  font-size: var(--lc-text-sm); font-weight: 600; color: var(--lc-accent-text);
}
.qc-more::after { content: 'More'; }
.qc-more-cb:checked ~ .qc-more::after { content: 'Less'; }
.qc-more-cb:focus-visible ~ .qc-more { outline: 2px solid var(--lc-accent); outline-offset: 2px; }

.qc-actions { display: flex; flex-wrap: wrap; gap: var(--lc-space-2); align-items: center; }
/* Remove/Keep stay left; the less-common actions push to the right. */
.qc-actions-secondary {
  display: flex; flex-wrap: wrap; gap: var(--lc-space-2); align-items: center;
  margin-left: auto;
}
/* Fuzzy time reads as hover-able (exact time in the title). */
.fuzzy-time { cursor: help; text-decoration-line: underline; text-decoration-style: dotted; text-underline-offset: 2px; }
.card-header { margin-bottom: 10px; }
.card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-2); margin-bottom: var(--lc-space-1); }
.author-name { font-weight: 600; }
.card-video { font-size: var(--lc-text-sm); color: var(--lc-text-muted); }
.card-body { margin-bottom: var(--lc-space-3); }
.card-error {
  color: var(--lc-danger); background: var(--lc-danger-weak); border: 1px solid var(--lc-danger);
  border-radius: var(--lc-radius-sm); padding: 8px 12px; margin-bottom: var(--lc-space-2); font-size: var(--lc-text-sm);
}
/* Cross-link inherits the banner's danger colour — the accent link colour is not
   contrast-checked against the danger-weak background. */
.card-error a, .card-error .link-btn { color: inherit; }
/* A button that reads as a link. The System Health panel is a dialog rather than
   a place (T-137), so the control that opens it is a button — but it sits mid
   sentence in the fault banner and must look like the link it replaced. */
.link-btn {
  appearance: none; background: none; border: 0; padding: 0;
  font: inherit; color: var(--lc-accent-text); cursor: pointer;
  text-decoration: underline; text-underline-offset: 0.15em;
}
.comment-text { white-space: pre-wrap; word-break: break-word; }
.comment-text-sm { font-size: var(--lc-text-sm); white-space: pre-wrap; word-break: break-word; }
.comment-normalized { font-size: var(--lc-text-sm); margin-top: 6px; }
.card-actions { display: flex; flex-wrap: wrap; gap: var(--lc-space-2); align-items: center; }
.inline-form { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.inline-form input { flex: 1; min-width: 120px; }

/* ============================================================================
   Forms
   ========================================================================= */
.form-stack { display: flex; flex-direction: column; gap: 10px; }
.form-stack label { display: flex; flex-direction: column; gap: 4px; font-size: var(--lc-text-sm); color: var(--lc-text-muted); }
.form-stack input, .form-stack select, .form-stack textarea {
  background: var(--lc-surface); border: 1px solid var(--lc-border-strong); border-radius: var(--lc-radius-sm);
  color: var(--lc-text); padding: 8px 12px;
}
.form-stack input:focus, .form-stack select:focus, .form-stack textarea:focus {
  outline: none; border-color: var(--lc-accent); box-shadow: 0 0 0 3px var(--lc-accent-weak);
}
.form-stack input::placeholder, .form-stack textarea::placeholder { color: var(--lc-text-subtle); }
.checkbox-label { flex-direction: row !important; align-items: center; gap: var(--lc-space-2); }
.form-row { display: flex; align-items: center; gap: var(--lc-space-3); }

/* Confidence appetite (T-075): the auto-remove confidence is a named posture tier
   (strict / balanced / lax), not a raw slider — the tenant sets intent, LCS owns the
   number. A per-line appetite <select> shows only when that line auto-removes; the
   [hidden] attribute drives the toggle (JS + server-rendered initial state). */
.appetite-field[hidden] { display: none; }
.appetite-select { min-width: 220px; max-width: 100%; }

/* Per-theme overrides drawer (T-080/T-081): `.drawer` supplies the pointer, the
   marker reset and the muted, weighted label; this adds the gap under it.
   :focus-visible supplies the focus ring. */
.matrix-overrides > summary { margin-bottom: var(--lc-space-2); }

/* Import page (T-069): per-pen format notes */
.import-formats > div { display: flex; flex-direction: column; gap: 4px; padding: var(--lc-space-3) 0; border-bottom: 1px solid var(--lc-border); }
.import-formats > div:last-child { border-bottom: none; }
.import-formats dt { font-weight: 600; }
.import-formats dd { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-3); }

/* Export page (T-085): per-pen download rows. Reuses .import-formats for the
   format notes; this is only the list of narrower downloads under the main one. */
.export-pens { list-style: none; padding: 0; margin: var(--lc-space-3) 0 0; }
.export-pens li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-3); padding: 4px 0; }

/* Filter bar */
.filter-bar { display: flex; flex-wrap: wrap; gap: var(--lc-space-2); margin-bottom: var(--lc-space-4); align-items: center; }
.filter-bar input, .filter-bar select {
  background: var(--lc-surface); border: 1px solid var(--lc-border-strong); border-radius: var(--lc-radius-sm);
  color: var(--lc-text); padding: 7px 12px; font-size: var(--lc-text-sm);
}
.filter-bar input:focus, .filter-bar select:focus { outline: none; border-color: var(--lc-accent); box-shadow: 0 0 0 3px var(--lc-accent-weak); }

/* Pagination */
.pagination { display: flex; align-items: center; gap: var(--lc-space-3); margin-top: var(--lc-space-4); justify-content: center; }

/* ============================================================================
   Alerts
   ========================================================================= */
.alert { padding: 12px 16px; border-radius: var(--lc-radius); margin-bottom: var(--lc-space-4); font-size: var(--lc-text-sm); }
.alert--error { background: var(--lc-danger-weak); border: 1px solid var(--lc-danger); color: var(--lc-danger); }
.alert--success { background: var(--lc-success-weak); border: 1px solid var(--lc-success); color: var(--lc-success); }
.alert--warning { background: var(--lc-warning-weak); border: 1px solid var(--lc-warning); color: var(--lc-warning); }
/* Neither a fault nor a success — an offer or a piece of context (T-092), e.g.
   "this platform needs a different tier". Accent rather than a semantic status
   colour, so it does not read as something having gone wrong. */
.alert--info { background: var(--lc-accent-weak); border: 1px solid var(--lc-accent); color: var(--lc-accent-text); }
.alert strong { color: var(--lc-text); }
.alert ul { margin: 6px 0 0 16px; }

/* Denylist rule rows */
.rule-row--rule-workshop td:first-child { border-left: 3px solid var(--lc-warning); }
.rule-row--system td:first-child { border-left: 3px solid var(--lc-text-subtle); }
.rule-row--moderator td:first-child { border-left: 3px solid var(--lc-success); }

/* Queue controls */
.queue-controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--lc-space-3); margin-bottom: var(--lc-space-5); }
.sort-tabs { display: flex; gap: 2px; background: var(--lc-surface-2); border: 1px solid var(--lc-border); border-radius: var(--lc-radius-sm); padding: 3px; }
.sort-tab { padding: 5px 14px; border-radius: var(--lc-radius-sm); font-size: var(--lc-text-sm); color: var(--lc-text-muted); text-decoration: none; transition: background var(--lc-motion), color var(--lc-motion); }
.sort-tab:hover { color: var(--lc-text); background: var(--lc-surface); text-decoration: none; }
.sort-tab.active { background: var(--lc-surface); color: var(--lc-accent-text); font-weight: 600; box-shadow: var(--lc-shadow-sm); }
#queue-filter { width: 100%; padding-left: var(--lc-space-3); }  /* 12px left, matching Flock (.form-stack) inputs; mobile-first, widens at Desktop */
#queue-filter::placeholder { color: var(--lc-text-subtle); }

/* Video groups */
.video-group { margin-bottom: var(--lc-space-6); }
.video-group-header { display: flex; align-items: baseline; gap: 10px; padding: 0 0 var(--lc-space-2); border-bottom: 1px solid var(--lc-border); margin-bottom: var(--lc-space-3); }
.video-group-title { font-family: var(--lc-font-serif); font-size: var(--lc-text-md); font-weight: 600; }
.video-group-title a { color: var(--lc-text); }
.video-group-title a:hover { color: var(--lc-accent-text); text-decoration: none; }
.video-group-count { font-size: var(--lc-text-sm); }

/* Category groups */
.category-group { margin-bottom: var(--lc-space-4); }
.category-group-header { display: flex; align-items: center; gap: var(--lc-space-2); padding: 6px 0; margin-bottom: var(--lc-space-2); }
.cat-count { font-size: var(--lc-text-sm); }
.group-actions { display: flex; gap: 6px; margin-left: auto; }
.cat-label {
  display: inline-block; padding: 3px 11px; border-radius: var(--lc-radius-pill);
  font-size: var(--lc-text-xs); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
}
.cat-label--harmful       { background: var(--lc-danger-weak);  color: var(--lc-danger);   border: 1px solid var(--lc-danger); }
.cat-label--spam          { background: var(--lc-warning-weak); color: var(--orange);      border: 1px solid var(--orange); }
.cat-label--misinfo       { background: var(--lc-warning-weak); color: var(--lc-warning);  border: 1px solid var(--lc-warning); }
.cat-label--controversial { background: var(--lc-patreon-weak); color: var(--lc-patreon);  border: 1px solid var(--lc-patreon); }
.cat-label--most-liked    { background: var(--lc-success-weak); color: var(--lc-success);  border: 1px solid var(--lc-success); }
.cat-label--high-risk     { background: var(--lc-warning-weak); color: var(--orange);      border: 1px solid var(--orange); }
.cat-label--other         { background: var(--lc-surface-2);    color: var(--lc-text-muted); border: 1px solid var(--lc-border); }

/* Kbd hints: visible only on focused card */
.queue-card:not(.focused):not(:focus) .card-actions kbd { visibility: hidden; }

/* Ban author button */
.btn--ban { background: var(--lc-danger-weak); border-color: var(--lc-danger); color: var(--lc-danger); }
.btn--ban:hover { background: var(--lc-danger); color: var(--lc-on-accent); }

/* Ban prompt */
.ban-prompt {
  background: var(--lc-danger-weak); border: 1px solid var(--lc-danger); border-radius: var(--lc-radius);
  padding: var(--lc-space-4);
}
.ban-prompt-inner { display: flex; align-items: center; gap: var(--lc-space-4); flex-wrap: wrap; }
.ban-prompt-inner p { flex: 1; font-size: var(--lc-text-sm); }
.ban-prompt-actions { display: flex; gap: var(--lc-space-2); }

/* Text selection popup */
.selection-popup {
  position: absolute; z-index: 200;
  background: var(--lc-surface); border: 1px solid var(--lc-accent);
  border-radius: var(--lc-radius-sm); padding: 6px 8px;
  box-shadow: var(--lc-shadow-lg);
  white-space: nowrap;
}
.selection-popup::before {
  content: ''; position: absolute; top: -6px; left: 50%; transform: translateX(-50%);
  border: 6px solid transparent; border-bottom-color: var(--lc-accent);
  border-top: none; width: 0; height: 0;
}

/* HTMX indicator */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator { display: inline; }
.htmx-request.htmx-indicator { display: inline; }

/* Rule Workshop */
.workshop-panel {
  margin-top: var(--lc-space-3); padding: var(--lc-space-4); border-radius: var(--lc-radius);
  background: var(--lc-warning-weak); border: 1px solid var(--lc-warning);
}
.workshop-proposal-header {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--lc-space-2); margin-bottom: var(--lc-space-2);
}
.workshop-rule-value {
  font-family: var(--lc-font-mono); font-size: 0.9em;
  background: var(--lc-surface); padding: 2px 6px; border-radius: 4px;
}
.workshop-description { font-size: var(--lc-text-sm); margin-bottom: 10px; }
.fp-preview {
  font-size: var(--lc-text-sm); padding: 8px 12px; border-radius: var(--lc-radius-sm);
  background: var(--lc-success-weak); border: 1px solid var(--lc-success); color: var(--lc-success);
  margin-bottom: 10px;
}
.fp-preview--warning {
  background: var(--lc-warning-weak); border-color: var(--lc-warning); color: var(--lc-warning);
}
.workshop-details { margin-bottom: 10px; font-size: var(--lc-text-sm); }
.workshop-details-body { padding-top: var(--lc-space-2); display: flex; flex-direction: column; gap: var(--lc-space-2); }
.workshop-details-body ul { margin: 4px 0 0 16px; }
.workshop-details-body li { margin-bottom: 2px; }
.workshop-actions { display: flex; gap: var(--lc-space-2); flex-wrap: wrap; }
.badge--confidence-high { background: var(--lc-danger-weak); color: var(--lc-danger); border-color: var(--lc-danger); }
.badge--confidence-medium { background: var(--lc-warning-weak); color: var(--lc-warning); border-color: var(--lc-warning); }
.badge--type { background: var(--lc-surface); color: var(--lc-text-muted); border-color: var(--lc-border); }

/* Modal (T-029) */
.modal {
  position: fixed; inset: 0; margin: auto;
  max-width: 640px; width: calc(100% - 32px);
  background: var(--lc-surface); color: var(--lc-text);
  border: 1px solid var(--lc-border); border-radius: var(--lc-radius-lg);
  padding: var(--lc-space-5); box-shadow: var(--lc-shadow-lg);
}
.modal::backdrop { background: rgba(20, 12, 6, 0.55); }
.modal-title { font-family: var(--lc-font-serif); margin: 0 0 4px; }
.modal-meta { font-size: var(--lc-text-sm); color: var(--lc-text-muted); margin: 0 0 var(--lc-space-3); word-break: break-all; }
.modal .comment-text {
  background: var(--lc-surface-2); border: 1px solid var(--lc-border); border-radius: var(--lc-radius-sm);
  padding: var(--lc-space-3); margin: 0 0 var(--lc-space-3); max-height: 50vh; overflow: auto;
}
.modal-actions { display: flex; justify-content: flex-end; margin: 0; }

/* System Health panel (T-137). One card per source inside the shared modal;
   scrolls within the dialog rather than growing it past the viewport. */
.health-cards {
  list-style: none; margin: 0 0 var(--lc-space-4); padding: 0;
  display: grid; gap: var(--lc-space-3);
  max-height: 60vh; overflow-y: auto;
}
.health-card {
  background: var(--lc-surface-2); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius-sm); padding: var(--lc-space-3);
}
.health-card:focus-visible { outline: 2px solid var(--lc-accent); outline-offset: 2px; }
.health-card--fault { border-color: var(--lc-danger); background: var(--lc-danger-weak); }
/* Paused, not failed (T-120): an expected, self-clearing stop — over the API
   allowance, so ingestion stood down to leave headroom for moderator actions. */
.health-card--paused { border-color: var(--lc-warning); background: var(--lc-warning-weak); }
.health-card__head {
  display: flex; flex-wrap: wrap; gap: var(--lc-space-2);
  align-items: baseline; justify-content: space-between;
}
.health-card__source { font-weight: 600; }
.health-card__status { font-size: var(--lc-text-sm); font-weight: 600; }
/* Label/value pairs stack on narrow screens and pair up once there is room. */
.health-card__facts {
  margin: var(--lc-space-2) 0 0; display: grid; gap: 2px var(--lc-space-3);
  font-size: var(--lc-text-sm);
}
.health-card__facts dt { color: var(--lc-text-muted); }
.health-card__facts dd { margin: 0 0 var(--lc-space-2); }
.health-card__facts dd:last-child { margin-bottom: 0; }

/* ============================================================================
   T-037 onboarding + settings
   ========================================================================= */
.onboarding-remaining { margin: 8px 0 12px; padding-left: 20px; }
.onboarding-remaining li { margin: 2px 0; }

.onboarding-steps { list-style: none; padding: 0; margin: var(--lc-space-5) 0; }
.onboarding-step {
  display: flex; gap: var(--lc-space-4); align-items: flex-start;
  background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); padding: var(--lc-space-4) var(--lc-space-5); margin-bottom: var(--lc-space-3);
  box-shadow: var(--lc-shadow-sm);
}
/* "Done" is signalled by the green check mark, not by fading the whole step:
   opacity on text-bearing content dropped its contrast below AA (T-042). */
.onboarding-step.is-done { background: var(--lc-surface-2); }
.onboarding-step__mark {
  flex: 0 0 32px; width: 32px; height: 32px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--lc-surface-2); color: var(--lc-text-muted); font-weight: 600;
}
.onboarding-step.is-done .onboarding-step__mark { background: var(--lc-success); color: var(--lc-on-accent); }
.onboarding-step__body { flex: 1; }
.onboarding-step__body h2 { margin: 0 0 4px; font-size: var(--lc-text-md); }
.onboarding-step__body p { margin: 0 0 8px; color: var(--lc-text-muted); }

.settings-grid, .plan-grid {
  display: grid; gap: var(--lc-space-4); margin: var(--lc-space-5) 0;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
.settings-tile {
  display: block; background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); padding: var(--lc-space-4) var(--lc-space-5); color: var(--lc-text);
  text-decoration: none; transition: border-color var(--lc-motion), box-shadow var(--lc-motion);
  box-shadow: var(--lc-shadow-sm);
}
.settings-tile:hover { border-color: var(--lc-accent); text-decoration: none; box-shadow: var(--lc-shadow); }
.settings-tile h2 { margin: 0 0 6px; font-size: var(--lc-text-md); }
.settings-tile p { margin: 0; color: var(--lc-text-muted); font-size: var(--lc-text-sm); }

.plan-card {
  background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); padding: var(--lc-space-4) var(--lc-space-5);
  box-shadow: var(--lc-shadow-sm);
}
.plan-card.is-current { border-color: var(--lc-accent); box-shadow: 0 0 0 2px var(--lc-accent-weak); }
.plan-card h2 { margin: 0 0 8px; }
.plan-limits { list-style: none; padding: 0; margin: 0 0 12px; }
.plan-limits li { padding: 2px 0; color: var(--lc-text-muted); font-size: var(--lc-text-sm); }

/* ============================================================================
   Style guide (T-041 internal component gallery)
   ========================================================================= */
.sg-hero {
  background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius-lg); padding: var(--lc-space-6);
  margin-bottom: var(--lc-space-6); box-shadow: var(--lc-shadow-sm);
}
.sg-hero h1 { font-size: var(--lc-text-2xl); margin-bottom: var(--lc-space-2); }
.sg-hero p { color: var(--lc-text-muted); max-width: 60ch; }
.sg-swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--lc-space-3); }
.sg-swatch { border: 1px solid var(--lc-border); border-radius: var(--lc-radius-sm); overflow: hidden; background: var(--lc-surface); }
.sg-swatch__chip { height: 56px; }
.sg-swatch__meta { padding: 8px 10px; font-size: var(--lc-text-xs); }
.sg-swatch__name { font-weight: 600; }
.sg-swatch__val { color: var(--lc-text-subtle); font-family: var(--lc-font-mono); }
.sg-row { display: flex; flex-wrap: wrap; gap: var(--lc-space-3); align-items: center; margin-bottom: var(--lc-space-3); }
.sg-type-sample { margin-bottom: var(--lc-space-3); }
.sg-type-sample .label { font-size: var(--lc-text-xs); color: var(--lc-text-subtle); font-family: var(--lc-font-mono); }
.sg-panel {
  background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); padding: var(--lc-space-5); box-shadow: var(--lc-shadow-sm);
}

/* ============================================================================
   Responsive — mobile-first (T-042)

   The base styles above target the smallest screen. These six min-width ranges
   are the breakpoint AUTHORITY for the app (owner-set); larger-screen
   enhancements layer on here — never a max-width override. Orientation is not a
   separate axis: the layout keys off width alone, so a phone in landscape simply
   crosses into the next range. Verified with no horizontal panning at every
   range in portrait and landscape (scripts/a11y_audit.py is AA; a viewport probe
   covers overflow).

     320px  Mobile Small     480px  Mobile Large     768px  Tablet
     1024px Desktop          1200px Large Desktop     1440px Extra Large

   Data tables are stacked cards on mobile by default (`.table--stack`) and
   become real tables at the Tablet range. A stacked table drops its header row
   and re-lays each row as a self-contained card of label/value pairs (cells
   carry `data-label`); a cell with no label spans full width. This keeps a wide
   table (e.g. the Action Log's free-text Detail) readable top-to-bottom on a
   phone instead of squeezing sideways.
   ========================================================================= */

/* ---- Base (mobile): data tables reflow to stacked cards ----------------- */
.table--stack { border: 0; background: none; box-shadow: none; }
.table--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.table--stack tr {
  display: block; background: var(--lc-surface); border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius); box-shadow: var(--lc-shadow-sm);
  margin-bottom: var(--lc-space-3); padding: var(--lc-space-1) 0;
}
.table--stack tr:hover td { background: none; }
.table--stack td {
  display: flex; justify-content: space-between; gap: var(--lc-space-4);
  border-top: 0; padding: 8px var(--lc-space-4); text-align: right;
}
.table--stack td + td { border-top: 1px solid var(--lc-border); }
.table--stack td::before {
  content: attr(data-label); text-align: left; flex: 0 0 auto;
  font-size: var(--lc-text-xs); font-weight: 600; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--lc-text-muted);
}
.table--stack td[data-label=""], .table--stack td:not([data-label]) { text-align: left; }
.table--stack td[data-label=""]::before, .table--stack td:not([data-label])::before { content: none; }
.table-scroll:has(.table--stack) { overflow-x: visible; }

/* ---- 320px · Mobile Small ----------------------------------------------- */
@media (min-width: 320px) {
  /* The base above is authored for ≤320px; this is the explicit floor of the
     six-range system. Enhancements that need even the smallest phone go here. */
}

/* ---- 480px · Mobile Large ----------------------------------------------- */
@media (min-width: 480px) {
  .card-actions { gap: var(--lc-space-3); }
}

/* ---- 768px · Tablet — inline nav, tables become tables ------------------ */
@media (min-width: 768px) {
  /* Nav collapses back into a single inline row; hamburger disappears. */
  .nav { flex-wrap: nowrap; gap: var(--lc-space-5); padding: 0 var(--lc-space-5); height: 56px; }
  .nav-toggle, .nav-toggle-cb { display: none; }
  .nav-links {
    display: flex; flex-direction: row; align-items: center;
    width: auto; gap: 2px; padding: 0; flex: 1; flex-basis: auto;
  }
  .nav-links a { padding: 6px 12px; }
  /* Two `margin-left:auto` in one flex row **share** the free space rather than
     the first one taking it — which parked this link mid-bar, orphaned between
     the sections and the controls. So the exit link claims the space and the
     toggle's own auto margin is cancelled *only* where an exit link precedes it.
     The sibling selector keeps the app bar, which has no exit link, untouched. */
  .nav-links a.nav-exit { margin-left: auto; }
  .theme-toggle { margin: 0 0 0 auto; width: 34px; height: 34px; padding: 0; gap: 0; align-self: auto; }
  .nav-exit ~ .theme-toggle { margin-left: 0; }
  .theme-toggle::after { content: none; }
  /* System Health collapses to an icon; its label stays in the DOM as the
     accessible name rather than being removed with `display: none`. */
  .health-toggle { margin: 0 0 0 var(--lc-space-2); width: 34px; height: 34px; padding: 0; gap: 0; align-self: auto; }
  .health-toggle__label {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .health-card__facts { grid-template-columns: auto 1fr; }
  .health-card__facts dd { margin-bottom: 0; }
  /* Log-out control sits inline at the right end, just past the theme toggle. */
  .nav-logout { margin: 0 0 0 var(--lc-space-3); align-self: auto; }

  /* Content gains room; touch targets tighten to pointer sizes. */
  .main { padding: var(--lc-space-6) var(--lc-space-5) var(--lc-space-8); }
  .two-col { grid-template-columns: 1fr 1fr; }
  .btn { padding: 7px 16px; }
  .btn--sm { padding: 3px 10px; }
  .icon-link, .icon-btn { width: 30px; height: 30px; }

  /* Stacked cards revert to a normal data table. */
  .table--stack { border: 1px solid var(--lc-border); background: var(--lc-surface); box-shadow: var(--lc-shadow-sm); }
  .table--stack thead { position: static; width: auto; height: auto; overflow: visible; clip: auto; }
  .table--stack tr { display: table-row; background: none; border: 0; border-radius: 0; box-shadow: none; margin: 0; padding: 0; }
  .table--stack tr:hover td { background: var(--lc-surface-2); }
  .table--stack td, .table--stack td + td {
    display: table-cell; text-align: left; border-top: 1px solid var(--lc-border); padding: 10px 14px;
  }
  .table--stack td::before { content: none; }
  .table-scroll:has(.table--stack) { overflow-x: auto; }

  /* Triage row opens into three side-by-side zones: rail · author · comment. */
  .queue-card {
    grid-template-columns: 8px minmax(180px, 240px) minmax(0, 1fr);
    grid-template-areas: "rail author comment";
  }
  .qc-author {
    padding: var(--lc-space-4);
    border-right: 1px solid var(--lc-border);
    justify-content: flex-start;
  }
  .qc-comment { padding: var(--lc-space-4); padding-right: var(--lc-space-4); }
}

/* ---- 1024px · Desktop --------------------------------------------------- */
@media (min-width: 1024px) {
  #queue-filter { width: 600px; }
}

/* ---- 1200px · Large Desktop --------------------------------------------- */
@media (min-width: 1200px) {
  /* .main already caps at 1200px; give the ground gutters a little more air. */
  .main { padding-left: var(--lc-space-6); padding-right: var(--lc-space-6); }
}

/* ---- 1440px · Extra Large ----------------------------------------------- */
@media (min-width: 1440px) {
  .main { max-width: 1320px; }
}

/* A numbered setup flow (T-083): the steps a customer takes outside Shepherd,
   in order, with the form that belongs to one of them nested inside its step
   rather than floating free. */
.setup-steps { margin: var(--lc-space-5) 0 var(--lc-space-6); padding-left: var(--lc-space-5); }
.setup-steps > li { margin-bottom: var(--lc-space-5); }
.setup-steps > li::marker { color: var(--lc-text-muted); font-weight: 600; }
.setup-steps h2 { margin: 0 0 var(--lc-space-1); }
.setup-steps p { margin: 0 0 var(--lc-space-3); max-width: 60ch; }
.setup-steps .form-stack { max-width: 32rem; }

/* ---- Source cards keep their tables stacked at every width --------------- */
/* `.table--stack` un-stacks at the Tablet range because it assumes the table has
   the page's width to work with. Inside a source card it never does — a card is
   ~380px whatever the viewport — so a four-column connection table squeezes a URL
   into a four-line ribbon and the action into a sliver. Re-apply the stacked
   presentation here, after every breakpoint, so it holds at all widths.

   Toned down from the mobile original: the row is already inside a card, so it
   drops the nested border, radius and shadow and keeps only the label/value
   layout that made stacking worth doing. */
.source-card .table--stack { border: 0; background: none; box-shadow: none; }
.source-card .table--stack thead {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0);
}
.source-card .table--stack tr {
  display: block; background: none; border: 0; box-shadow: none;
  border-radius: 0; margin: 0 0 var(--lc-space-2); padding: 0;
}
.source-card .table--stack tr + tr {
  border-top: 1px solid var(--lc-border); padding-top: var(--lc-space-2);
}
.source-card .table--stack tr:hover td { background: none; }
.source-card .table--stack td, .source-card .table--stack td + td {
  display: flex; justify-content: space-between; gap: var(--lc-space-4);
  border-top: 0; padding: 3px 0; text-align: right;
}
.source-card .table--stack td::before {
  content: attr(data-label); text-align: left; flex: 0 0 auto;
  font-size: var(--lc-text-xs); font-weight: 600; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--lc-text-muted);
}
.source-card .table--stack td[data-label=""]::before,
.source-card .table--stack td:not([data-label])::before { content: none; }
.source-card .table--stack td[data-label=""],
.source-card .table--stack td:not([data-label]) { justify-content: flex-end; }
.source-card .table-scroll:has(.table--stack) { overflow-x: visible; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.001ms !important; }
}
