/* ─────────────────────────────────────────────────────────────────────────────
   Nalanda — "the manuscript ledger"
   Hand-written design system. No framework, no build step.
   Grounded in the Pala-era scriptorium of Nalanda itself: palm-leaf paper,
   lampblack ink, indigo working accent, vermilion rubrication (red ink is for
   circulation and danger — as in the manuscripts), turmeric gold for ratings.
   Titles are Eczar, a Devanagari-first face whose Latin keeps the stroke energy;
   the brand hangs from its headstroke (śirorekhā). Dark mode is the lamp-lit
   reading room. Monospace stays the registrar's voice: ids, counts, dates.
   ──────────────────────────────────────────────────────────────────────────── */

/* Vendored via @fontsource/eczar (scripts/vendor.mjs) — never a CDN. */
@font-face {
  font-family: 'Eczar';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/vendor/fonts/eczar-latin-600-normal.woff2') format('woff2');
}
@font-face {
  font-family: 'Eczar';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/vendor/fonts/eczar-latin-700-normal.woff2') format('woff2');
}

:root {
  /* native controls (date pickers, checkboxes, file buttons, scrollbars) follow the theme */
  color-scheme: light;
  /* palette — light: the prepared palm leaf */
  --paper: #f6f2e7;
  --surface: #fdfaf1;
  --surface-2: #efe8d8;
  --line: #e5ddc9;
  --line-strong: #cdc2a6;
  --ink: #221d14;
  --ink-2: #5d564a;
  --ink-3: #746b58; /* 4.7:1 on paper — it carries 10–11px labels */
  --accent: #35447e;
  --accent-strong: #273766;
  --accent-ink: #fdfaf1;
  --accent-tint: rgba(53, 68, 126, 0.09);
  --brass: #a8770f;
  --stamp: #b93a1c;
  --stamp-tint: rgba(185, 58, 28, 0.08);
  --shadow: 0 1px 2px rgba(34, 29, 20, 0.06), 0 4px 16px rgba(34, 29, 20, 0.05);
  /* every dropdown's arrow — a <select>'s and a filter menu's alike: a chevron stroked in --ink-2. A data URI can't
     read a custom property, so the colour is written out here and again for dark. */
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%235d564a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  /* type */
  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --mono: ui-monospace, 'SF Mono', 'Cascadia Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
  --serif: 'Eczar', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, ui-serif, serif;

  --radius: 7px;
  --radius-sm: 5px;
  /* Every control in a row — input, select, button, a filter menu's summary, the view toggle — is this tall, sets
     its text at this size, and sets that one line in line-height: normal, centred. Same box, same line box, same
     font size: the same baseline, so a row of controls reads as one level line. (With line-height 1 on buttons and
     1.55 on summaries, or 13px fields beside 12.5px buttons, Chromium rounded their baselines a pixel apart.) */
  --control-h: 32px;
  --control-text: 13px;
  --sidebar-w: 236px;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    /* dark: the lamp-lit reading room — warm blacks, pigments glowing */
    --paper: #171310;
    --surface: #1e1915;
    --surface-2: #272016;
    --line: #2e2820;
    --line-strong: #463d2e;
    --ink: #ece4d2;
    --ink-2: #ab9f88;
    --ink-3: #968b73; /* 5.5:1 on paper, 4.7:1 on a hovered table row */
    --accent: #8ba0d9;
    --accent-strong: #a5b7e6;
    --accent-ink: #10141f;
    --accent-tint: rgba(139, 160, 217, 0.13);
    --brass: #d3a02f;
    --stamp: #e06a45;
    --stamp-tint: rgba(224, 106, 69, 0.12);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 20px rgba(0, 0, 0, 0.35);
    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23ab9f88' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  }
}

/* ── base ─────────────────────────────────────────────────────────────────── */

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

/* display rules on .btn/.tab etc. must never defeat the hidden attribute */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 13.5px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0 0 0.5rem; line-height: 1.25; }
h1 { font-family: var(--serif); font-size: 22px; font-weight: 600; letter-spacing: 0; }
h2 { font-size: 14px; font-weight: 600; }
h3 { font-size: 13.5px; font-weight: 600; }
h4 { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
p { margin: 0 0 0.75rem; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); text-decoration: underline; text-underline-offset: 2px; }
/* A link inside running text is underlined: indigo against ink is too close to tell apart by colour alone. */
p:not(.back-link) a:not(.btn):not(.tag) { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
hr { border: none; border-top: 1px solid var(--line); margin: 1.25rem 0; }
code { font-family: var(--mono); font-size: 0.92em; background: var(--surface-2); padding: 0.1em 0.4em; border-radius: 4px; }
table { border-collapse: collapse; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 2px; }

/* Text for screen readers only, the app's one class for it: an empty table header's name, a button's name saying
   which item it acts on, a chart's table, a closed sidebar section's "unread". */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* The first Tab stop on every app page (layout.tsx): out of sight until focused, then a small indigo tab at the
   top left, over the sidebar and the phone bar alike. */
.skip-link { position: absolute; top: 0.5rem; left: 0.5rem; z-index: 60; padding: 0.45rem 0.8rem; border-radius: var(--radius-sm); background: var(--accent); color: var(--accent-ink); font-size: 12.5px; font-weight: 600; box-shadow: var(--shadow); }
.skip-link:not(:focus) { width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip-link:hover, .skip-link:focus { color: var(--accent-ink); text-decoration: none; }
/* what the skip link or an htmx swap focuses is a landing place, not a control: no ring round the whole page */
#main:focus, [data-focus-landing]:focus { outline: none; }

.mono { font-family: var(--mono); }
.muted { color: var(--ink-2); }
.prewrap { white-space: pre-line; }
.error { color: var(--stamp); }

/* the registrar's eyebrow: small, tracked, mono */
.eyebrow {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-3);
  margin: 0 0 0.6rem;
}

/* ── app shell ────────────────────────────────────────────────────────────── */

.app { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
/* main-column grid item: same min-width hygiene as .content, so unbreakable
   content (long words/URLs) can never widen the column past the viewport */
.app > div { min-width: 0; }

.sidebar {
  border-right: 1px solid var(--line);
  background: var(--surface);
  padding: 1.1rem 0.9rem 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 1.3rem;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

/* brand block — the śirorekhā: the wordmark hangs from its vermilion headstroke,
   the way Devanagari letters hang from theirs. Doubles as the catalog card's
   red top-rule. It lives here, and only here. */
.brand { display: block; padding: 0 0.35rem; text-decoration: none; }
.brand:hover { text-decoration: none; }
.brand-name {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--ink);
}
.brand-sub { font-family: var(--mono); font-size: 10px; letter-spacing: 0.12em; color: var(--ink-3); text-transform: uppercase; margin-top: 0.1rem; }
.brand-deva { font-family: var(--sans); font-size: 11px; letter-spacing: 0; }
.brand-rule { margin-bottom: 0.2rem; border-top: 2px solid var(--stamp); position: relative; }
.brand-rule::after { content: ''; position: absolute; left: 0; right: 0; top: 2px; border-top: 1px solid var(--stamp); opacity: 0.55; }
/* the narrow sidebar needs a slightly denser sub-line to stay on one line */
.sidebar .brand-sub { font-size: 9.5px; letter-spacing: 0.08em; }

/* Navigation (ARCH.md §16 #62): three pinned links, then sections that fold. Each section is a <details> whose
   <summary> is its eyebrow, with a chevron that turns as it opens; it opts out of the global details frame. */
.nav { display: flex; flex-direction: column; gap: 2px; }
.nav-pinned, .nav-links { display: flex; flex-direction: column; gap: 1px; }
.nav-pinned { margin-bottom: 0.75rem; }
.sidebar .nav-section { border: none; border-radius: 0; padding: 0; margin: 0; background: none; }
.sidebar .nav-section::after { content: none; }
.nav-section[open] > .nav-links { margin: 1px 0 0.6rem; }
.nav-summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 28px; /* the hit target: past 24px, the size of a nav link */
  padding: 0 0.45rem;
  margin: 0;
  border-radius: var(--radius-sm);
  font-weight: 400; /* the global summary rule bolds panel titles */
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent; /* a phone's tap flash isn't one of our colours; :hover and focus show it */
}
.nav-section[open] > .nav-summary { margin: 0; } /* the global details[open] summary rule adds a bottom margin */
.nav-summary::-webkit-details-marker { display: none; }
.nav-summary:hover { background: var(--surface-2); }
.nav-summary:hover .nav-eyebrow { color: var(--ink-2); }
/* the chevron: two borders of a small square, pointing right while closed and down once open */
.nav-summary::after {
  content: '';
  flex: none;
  width: 6px;
  height: 6px;
  margin-right: 2px;
  border-right: 1.5px solid var(--ink-3);
  border-bottom: 1.5px solid var(--ink-3);
  transform: rotate(-45deg);
  transition: transform 120ms ease;
}
.nav-section[open] > .nav-summary::after { transform: rotate(45deg); }
/* tracked a little tighter than .eyebrow, so "Sharing & connections" keeps one line beside its unread count */
.nav-eyebrow { flex: 1; min-width: 0; font-family: var(--mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); }
/* a closed section still says something in it is unread; open, each link shows its own */
.nav-summary .nav-summary-unread { margin-left: 0; }
.nav-section[open] .nav-summary-unread { display: none; }

.nav-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.32rem 0.45rem;
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  font-size: 13px;
  text-decoration: none;
}
.nav-link:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.nav-link.active { background: var(--accent-tint); color: var(--accent-strong); font-weight: 600; }
.nav-count { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
/* Unread: indigo, a working signal — vermilion stays for circulation and danger. */
.nav-unread { margin-left: auto; min-width: 1.35rem; padding: 0 0.35rem; border-radius: 999px; background: var(--accent); color: var(--accent-ink); font-family: var(--mono); font-size: 10.5px; line-height: 1.35rem; text-align: center; }
.nav-link.active .nav-count { color: var(--accent-strong); }

.sidebar-foot { margin-top: auto; display: flex; flex-direction: column; gap: 1px; padding-top: 0.9rem; border-top: 1px solid var(--line); }
.whoami { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: var(--ink-3); padding: 0 0.45rem 0.4rem; text-transform: uppercase; }

.content { min-width: 0; padding: 1.6rem 2rem 3.5rem; }
.content-inner { max-width: 1080px; margin: 0 auto; }

.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem; flex-wrap: wrap; }
.page-head h1 { margin: 0; }
.page-head .sub { color: var(--ink-2); font-family: var(--mono); font-size: 11px; letter-spacing: 0.05em; }
.page-actions { display: flex; gap: 0.5rem; align-items: center; }

section { margin-bottom: 2rem; }

/* topbar (mobile only) */
.mobile-bar { display: none; }

/* ── buttons ──────────────────────────────────────────────────────────────── */

.btn, button, [role='button'] {
  font-family: var(--sans);
  font-size: var(--control-text);
  font-weight: 600;
  line-height: normal;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0 0.8rem;
  height: var(--control-h);
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.btn:hover, button:hover, [role='button']:hover { background: var(--surface-2); text-decoration: none; color: var(--ink); }
.btn:disabled, button:disabled { opacity: 0.45; cursor: not-allowed; }

button[type='submit'], .btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
button[type='submit']:hover, .btn-primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); color: var(--accent-ink); }
/* A submit marked .btn is a secondary action: without this, button[type='submit'] outranks .btn and fills it indigo. */
button.btn { background: var(--surface); border-color: var(--line-strong); color: var(--ink); }
button.btn:hover { background: var(--surface-2); border-color: var(--line-strong); color: var(--ink); }

.btn-quiet, button.btn-quiet { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn-quiet:hover { background: var(--surface-2); color: var(--ink); }

.btn-danger, button.btn-danger { background: transparent; border-color: var(--line-strong); color: var(--stamp); }
.btn-danger:hover { background: var(--stamp-tint); border-color: var(--stamp); color: var(--stamp); }

button.linklike { background: none; border: none; height: auto; line-height: 1; padding: 0.32rem 0.45rem; color: var(--ink-2); font-weight: 400; font-size: 13px; justify-content: flex-start; width: 100%; border-radius: var(--radius-sm); }
button.linklike:hover { background: var(--surface-2); color: var(--ink); }

.inline { display: inline; }
.inline-form { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.inline-form input, .inline-form select { width: auto; margin: 0; }
/* A label in a single row keeps its text on the row's centre line — the stacked-form margin below would lift it. */
.inline-form label { display: inline-flex; align-items: center; margin: 0; }

/* ── forms ────────────────────────────────────────────────────────────────── */

label { display: block; font-size: 12px; font-weight: 600; color: var(--ink-2); margin-bottom: 0.9rem; }
label small { font-weight: 400; color: var(--ink-3); }

input, select, textarea {
  font-family: var(--sans);
  font-size: var(--control-text);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 0 0.6rem;
  height: var(--control-h);
  line-height: normal;
  width: 100%;
  margin-top: 0.3rem;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
textarea { height: auto; padding: 0.5rem 0.6rem; line-height: 1.5; resize: vertical; }
/* One arrow for every dropdown (the browser's own sits hard against the right edge, far from the text): the
   --chevron, 0.6rem in from the edge, with the same 0.4rem to the text that a filter menu's summary keeps. */
select:not([multiple]) {
  -webkit-appearance: none;
  appearance: none;
  padding-right: calc(1rem + 10px);
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  background-size: 10px 6px;
  text-overflow: ellipsis;
  /* where a select isn't given a width (filter bars, inline forms), it is as wide as the choice showing, not the
     longest option; a form's full-width select keeps its width. Browsers without it size to the longest option. */
  field-sizing: content;
}
input::placeholder, textarea::placeholder { color: var(--ink-3); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
input[type='checkbox'] { width: auto; height: auto; margin: 0 0.4rem 0 0; accent-color: var(--accent); }
/* input:focus's faint ring is no indicator on a checkbox, whose border doesn't change colour: it gets the outline */
input[type='checkbox']:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
input[type='date'] { font-family: var(--mono); }
input[inputmode='numeric'] { font-family: var(--mono); }
/* A monospace field's line box, centred like a regular one, rounds to a baseline a pixel off the regular field or
   button beside it in Chromium; trimmed to cap height and baseline, as the pills are, it sits on the row's line.
   (Safari needs nothing, and trimming changes nothing there; nor does it change a date field, in either.) */
input[inputmode='numeric'], input.mono, .money-input input { text-box: trim-both cap alphabetic; }
/* A date's figures sit in the field's own shadow tree, which the trim can't reach, and Chromium centres them half a
   pixel high: wherever a row fell between pixels, they showed level with the button beside them or a whole pixel
   above it, half the time each. A pixel of padding above them moves them down the half, level at every position
   measured; Safari lays its date fields out otherwise, and is unmoved by it. */
input[type='date']::-webkit-datetime-edit { padding-top: 1px; }
input[type='file'] { height: auto; padding: 0.25rem; }
input[type='file']::file-selector-button {
  font: 600 12.5px var(--sans);
  height: 24px;
  padding: 0 0.7rem;
  margin-right: 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink);
  cursor: pointer;
}

.form-card { max-width: 640px; }
/* a standalone search box (overview, /search) keeps clear of the frame or table below it */
form[role='search'] { margin-bottom: 1rem; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0 1rem; }

details { border: 1px solid var(--line-strong); border-radius: var(--radius); padding: 0.6rem 0.9rem; margin: 0.75rem 0; background: var(--surface); }
details summary { cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
details[open] summary { margin-bottom: 0.75rem; }

/* ── panels, notices ──────────────────────────────────────────────────────── */

/* the manuscript frame: every framed panel carries the same double rule that
   the headstroke carries — a strong outer line with a hairline echo inside */
.panel, .stat-row, .data-table, .auth-card, .candidate, details {
  position: relative;
}
.panel::after, .stat-row::after, .data-table::after, .auth-card::after, .candidate::after, details::after {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) - 3px);
  pointer-events: none;
}

.panel { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius); padding: 1rem 1.1rem; }

.notice {
  font-size: 12.5px;
  background: var(--accent-tint);
  border: 1px solid transparent;
  border-left: 3px solid var(--accent);
  color: var(--ink);
  padding: 0.55rem 0.8rem;
  border-radius: var(--radius-sm);
  margin: 0 0 0.75rem;
}
output.notice { display: block; } /* a status message (<output>, bulk edit's result) set as any notice */
article.notice { border: 1px solid var(--line); border-left: 3px solid var(--accent); background: var(--surface); padding: 0.9rem 1rem; }
/* An htmx request that failed (§16 #65): one message, app-wide, in the page's own flow after <main> and sticky at the
   bottom of the view — so it is seen wherever the control was, and nothing sits under it for good: scrolled to the
   end, it is in its own slot below the page. Rubricated like a refused form's error, on an opaque ground (it passes
   over the page), aligned with the content column. Empty, it takes no room and draws nothing. */
.app-status {
  position: sticky;
  bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
  z-index: 20;
  display: block;
  margin: 0 max(2rem, calc((100% - 1080px) / 2)) 0.75rem;
  padding: 0.6rem 0.85rem;
  font-size: 12.5px;
  color: color-mix(in srgb, var(--stamp) 85%, var(--ink));
  background: linear-gradient(var(--stamp-tint), var(--stamp-tint)), var(--surface);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--stamp);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
}
.app-status:empty { padding: 0; margin-block: 0; border: 0; box-shadow: none; background: none; }
p.error { font-size: 12.5px; color: color-mix(in srgb, var(--stamp) 85%, var(--ink)); background: var(--stamp-tint); border-left: 3px solid var(--stamp); padding: 0.55rem 0.8rem; border-radius: var(--radius-sm); }

/* ── stat row (ledger totals) ─────────────────────────────────────────────── */

.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
.stat { padding: 0.9rem 1.1rem; border-left: 1px solid var(--line); }
.stat:first-child { border-left: none; }
.stat-n { font-family: var(--mono); font-size: 22px; font-weight: 600; letter-spacing: -0.02em; }
.stat-n.warn { color: var(--stamp); }
.stat-label { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink-3); margin-top: 0.1rem; }
.stat-detail { font-family: var(--mono); font-size: 10px; color: var(--ink-3); margin-top: 0.3rem; }

/* ── data table ───────────────────────────────────────────────────────────── */

/* overflow-x: long content scrolls within the panel instead of clipping columns */
.data-table { width: 100%; background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius); overflow-x: auto; }
.data-table table { width: 100%; }
.data-table th {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-3);
  text-align: left;
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  white-space: nowrap;
}
.data-table td { padding: 0.45rem 0.75rem; border-bottom: 1px solid var(--line); font-size: 13px; vertical-align: middle; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover { background: color-mix(in srgb, var(--accent-tint) 50%, transparent); } /* half the tint: muted text keeps 4.5:1 */
.data-table tbody tr:hover > td:first-child { box-shadow: inset 2px 0 0 var(--accent); } /* and an indigo rule marks the row */
.data-table td.num, .data-table td.date { font-family: var(--mono); font-size: 12px; color: var(--ink-2); white-space: nowrap; }
.data-table .actions-cell { text-align: right; white-space: nowrap; width: 1%; }
/* row actions stay on one line — the cell is narrow, and a wrapped pair reads as two rows */
.data-table .actions-cell .inline-form { flex-wrap: nowrap; justify-content: flex-end; }
/* a share URL is one long unbreakable token; let it fold instead of widening the table */
.break-anywhere { overflow-wrap: anywhere; }
/* a due date and its Overdue pill: one line when there's room, the pill under the date on a phone */
.data-table td.due-cell { white-space: normal; }
.data-table td.due-cell > span { white-space: nowrap; }
.data-table + p { margin-top: 0.75rem; }

/* A table of things to act on (.data-table.cards) becomes a stack of cards on a phone, instead of scrolling its
   buttons off the edge. Still a <table>: the header row stays for screen readers, moved off screen, and each cell
   shows its column's name from data-label — as alt-less generated content, so it isn't read out a second time.
   A cell without a data-label is the card's title, full width; the actions cell is the card's foot. */
@media (max-width: 640px) {
  .data-table.cards { background: none; border: none; border-radius: 0; overflow: visible; }
  .data-table.cards::after { content: none; }
  .data-table.cards table { display: block; }
  .data-table.cards thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .data-table.cards tbody { display: grid; gap: 0.6rem; }
  .data-table.cards tr { display: grid; gap: 0.4rem; padding: 0.75rem 0.9rem; background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius); }
  .data-table.cards tbody tr:hover { background: var(--surface); }
  .data-table.cards tbody tr:hover > td:first-child { box-shadow: none; }
  .data-table.cards td,
  .data-table.cards td.num,
  .data-table.cards td.date,
  .data-table.cards .hide-sm { display: block; position: relative; min-width: 0; width: auto; padding: 0 0 0 6.75rem; border: none; text-align: left; white-space: normal; overflow-wrap: anywhere; }
  .data-table.cards td[data-label]::before {
    content: attr(data-label);
    content: attr(data-label) / '';
    position: absolute;
    left: 0;
    top: 0.2em;
    width: 6.25rem;
    font-family: var(--mono);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-3);
  }
  .data-table.cards td:not([data-label]) { padding-left: 0; }
  /* a cell holding a form takes the card's width, its label above it */
  .data-table.cards td.card-stack { padding-left: 0; }
  .data-table.cards td.card-stack::before { position: static; display: block; width: auto; margin-bottom: 0.35rem; }
  .data-table.cards .sub-settings select { max-width: none; }
  .data-table.cards td:empty { display: none; }
  .data-table.cards .actions-cell { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: flex-end; align-items: center; margin-top: 0.35rem; padding: 0.65rem 0 0; border-top: 1px solid var(--line); }
  .data-table.cards .actions-cell .inline-form { flex-direction: row; flex-wrap: wrap; justify-content: flex-end; align-items: center; }
  .data-table.cards .actions-cell button,
  .data-table.cards .actions-cell .btn { white-space: nowrap; height: 30px; min-height: 30px; padding: 0 0.75rem; }
}

/* max-width caps the title column under auto table layout so a long title
   truncates with an ellipsis instead of crowding every other column out */
.cell-title { display: flex; align-items: center; gap: 0.65rem; min-width: 0; max-width: 20rem; } /* a long title ellipsizes (its link's title says it whole) rather than pushing the table past a 1280px window */
.cell-title .thumb { flex: none; width: 26px; height: 39px; border-radius: 3px; object-fit: cover; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); }
.cell-title .thumb-fallback { flex: none; width: 26px; height: 39px; border-radius: 3px; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); display: flex; align-items: center; justify-content: center; font-size: 13px; }
.cell-title .t { min-width: 0; }
.cell-title .t a { color: var(--ink); font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cell-title .t a:hover { color: var(--accent); }
.cell-title .t small { color: var(--ink-2); display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── pills & chips ────────────────────────────────────────────────────────── */

.pill {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 0.18rem 0.5rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  color: var(--ink-2);
  background: transparent;
  display: inline-block;
  white-space: nowrap;
}
.pill.progress { border-color: var(--brass); color: color-mix(in srgb, var(--brass) 80%, var(--ink)); } /* brass as small text needs a touch of ink to read (4.7:1 on light paper) */
.pill.done { border-color: var(--accent); color: var(--accent); background: var(--accent-tint); }
.pill.dropped { color: var(--ink-3); text-decoration: line-through; }
.pill.lent { border-color: var(--stamp); color: var(--stamp); }
.pill.overdue { border-color: var(--stamp); background: var(--stamp); color: var(--surface); }
.pill.shared { border-color: var(--accent); color: var(--accent); }
.pill.ghost { border-style: dashed; color: var(--ink-3); } /* in the ledger, not on the shelf */
/* Configurable table columns. Every column is rendered server-side; hiding is
   purely presentational, driven by data-hide-cols on <html> (set before paint by
   an inline script in the layout, then maintained by app.js). With JS off the
   attribute is never set and the full table shows — the old behaviour. */
html[data-hide-cols~="type"] .col-type,
html[data-hide-cols~="shelf"] .col-shelf,
html[data-hide-cols~="year"] .col-year,
html[data-hide-cols~="completed"] .col-completed,
html[data-hide-cols~="rating"] .col-rating,
html[data-hide-cols~="status"] .col-status,
html[data-hide-cols~="holding"] .col-holding,
html[data-hide-cols~="tags"] .col-tags,
html[data-hide-cols~="acc"] .col-acc { display: none; }

.pill-btn { cursor: pointer; height: auto; line-height: inherit; } /* a <button> pill sizes like any other pill, not like a 30px button */
.pill-btn:hover { border-color: var(--accent); color: var(--accent); }
.pill.done.pill-btn:hover { border-color: var(--accent-strong); color: var(--accent-strong); }

.tag {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.12rem 0.55rem;
  margin: 0 0.3rem 0.3rem 0;
  text-decoration: none;
  white-space: nowrap; /* a two-word tag is one chip, never two lines inside one */
}
a.tag:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.tag-cloud { line-height: 2.1; }

.acc-no { font-family: var(--mono); font-size: 11px; color: var(--ink-3); letter-spacing: 0.04em; white-space: nowrap; }
.rating { color: var(--brass); font-size: 12.5px; letter-spacing: 0.08em; white-space: nowrap; }
/* on light paper the stars take a touch of ink, as the in-progress pill does (4.7:1); the lamp-lit gold reads as is */
.rating { color: color-mix(in srgb, var(--brass) 80%, var(--ink)); }
@media (prefers-color-scheme: dark) {
  .rating { color: var(--brass); }
}

/* ── covers & grid view ───────────────────────────────────────────────────── */

.item-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(136px, 1fr)); gap: 1.1rem 1rem; }
.item-card { display: flex; flex-direction: column; gap: 0.4rem; text-decoration: none; color: var(--ink); }
.item-card:hover { text-decoration: none; color: var(--ink); }
.item-cover { aspect-ratio: 2 / 3; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); transition: transform 120ms ease, box-shadow 120ms ease; }
.item-card:hover .item-cover { transform: translateY(-2px); box-shadow: var(--shadow), 0 0 0 1px var(--line-strong); }
.cover-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cover-fallback { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 2.2rem; }
.item-meta { display: flex; flex-direction: column; line-height: 1.3; }
.item-meta strong { font-size: 12.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.item-meta small { font-size: 11px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item-meta .mline { display: flex; flex-wrap: wrap; gap: 0.15rem 0.4rem; align-items: center; margin-top: 0.1rem; }
/* the accession number (or a share card's type) never shrinks to "№ 00…" — a pill wraps below it instead */
.item-meta .mline > small { flex: none; }

/* ── toolbar / filters / view toggle ──────────────────────────────────────── */

.toolbar { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; margin-bottom: 1rem; }
.toolbar select { width: auto; margin: 0; }

/* filter dropdowns: a <details> disguised as a select, holding any-of checkboxes.
   Opts out of the global details panel/manuscript-frame treatment — it's a
   toolbar control, not a framed panel. */
.toolbar .filter { position: relative; border: none; padding: 0; margin: 0; background: none; }
.toolbar .filter::after { content: none; }
.toolbar .filter summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: var(--control-h);
  line-height: normal; /* not the body's 1.55: the same line box as the select beside it */
  padding: 0 0.6rem;
  margin: 0; /* the global details[open] summary rule adds bottom margin */
  font-size: var(--control-text);
  font-weight: 400; /* the global summary rule bolds panel titles */
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
}
.toolbar .filter summary::-webkit-details-marker { display: none; }
/* the same chevron, gap and right padding as a <select> beside it; it turns over while the menu is open */
.toolbar .filter summary::after { content: ''; flex: none; width: 10px; height: 6px; background: var(--chevron) no-repeat center / 10px 6px; transition: transform 120ms ease; }
.toolbar .filter[open] summary::after { transform: rotate(180deg); }
.toolbar .filter[open] summary { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint); }
.filter-count {
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1;
  color: var(--accent);
  background: var(--accent-tint);
  border-radius: 999px;
  padding: 0.15rem 0.4rem;
}
/* A pill's words sit on the cap height's centre, not the line box's: SF Mono's line box puts capitals a pixel high
   in Chromium (not in Safari). Trimmed to cap height and baseline, and padded by what the trim took, a pill keeps its
   height and centres its capitals in every engine that trims; one that doesn't keeps the old box. */
@supports (text-box: trim-both cap alphabetic) {
  .pill { text-box: trim-both cap alphabetic; padding-block: calc(0.18rem + (1lh - 1cap) / 2); }
  .tag { text-box: trim-both cap alphabetic; padding-block: calc(0.12rem + (1lh - 1cap) / 2); }
  .filter-count { text-box: trim-both cap alphabetic; padding-block: calc(0.15rem + (1lh - 1cap) / 2); }
}
.filter-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 20;
  min-width: 170px;
  max-width: calc(100vw - 2rem);
  /* the last checkbox, too, keeps 24px clear of what's below the menu; and each keeps 24px clear of whatever the
     menu half covers beside it — a sliver of a select peeking out at its left edge is a touch target too */
  padding: 0.55rem 1.1rem 0.8rem;
  display: grid;
  gap: 0.8rem; /* each checkbox keeps 24px clear of the next: they are touch targets (WCAG 2.5.8) */
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.filter-menu label { display: flex; align-items: center; gap: 0.45rem; margin: 0; font-size: 12.5px; white-space: nowrap; cursor: pointer; }
.filter-menu input[type='checkbox'] { width: auto; height: auto; margin: 0; accent-color: var(--accent); }
.toolbar input[type='search'] { flex: 1 1 150px; width: auto; max-width: 190px; margin: 0; } /* gives way before the row wraps: a 13px Table/Covers toggle made a 1280 shelf's bar 16px too long for one line */
/* Columns and the view toggle: pushed to the row's end, and onto the next line together when the row wraps */
.toolbar-end { margin-left: auto; display: inline-flex; align-items: center; gap: 0.5rem; }

.view-toggle { display: inline-flex; height: var(--control-h); border: 1px solid var(--line-strong); border-radius: var(--radius-sm); overflow: hidden; }
.view-toggle a { display: inline-flex; align-items: center; line-height: normal; padding: 0 0.7rem; font-family: var(--mono); font-size: var(--control-text); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-2); background: var(--surface); text-decoration: none; }
/* the toggles clip to their rounded frame, so their focus ring is drawn inside */
.view-toggle a:focus-visible, .tab-bar .tab:focus-visible { outline-offset: -3px; }
.view-toggle a + a { border-left: 1px solid var(--line-strong); }
.view-toggle a.active { background: var(--accent-tint); color: var(--accent-strong); font-weight: 600; }
.view-toggle a:hover { text-decoration: none; color: var(--ink); }

.pagination { display: flex; justify-content: space-between; align-items: center; margin: 1rem 0; font-family: var(--mono); font-size: 11.5px; }

/* ── item detail ──────────────────────────────────────────────────────────── */

.item-detail { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 1.75rem; align-items: start; }
.item-detail-cover { position: sticky; top: 1.5rem; }
.item-detail-cover .cover-img, .item-detail-cover .cover-fallback { border-radius: var(--radius); box-shadow: var(--shadow), 0 0 0 1px var(--line); width: 100%; aspect-ratio: 2 / 3; font-size: 3.5rem; background: var(--surface-2); }
.item-detail-body hgroup { margin-bottom: 0.4rem; }
.item-detail-body hgroup h1 { font-size: 24px; }
.item-detail-body hgroup p { color: var(--ink-2); font-size: 14px; margin: 0.15rem 0 0; }

.props { display: grid; grid-template-columns: 130px minmax(0, 1fr); border-top: 1px solid var(--line); margin: 1rem 0; }
.props dt {
  font-family: var(--mono);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink-3);
  padding: 0.45rem 0.5rem 0.45rem 0;
  border-bottom: 1px solid var(--line);
  align-self: stretch;
  display: flex;
  align-items: center;
}
.props dd { margin: 0; padding: 0.45rem 0; border-bottom: 1px solid var(--line); font-size: 13px; display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem; }
.props dd.mono, .props dd .mono { font-family: var(--mono); font-size: 12px; }
/* rows grouped under one id, so a refresh can swap them (§16 #55, #60): the group adds no box, its rows stay in the grid */
.props-group { display: contents; }

.detail-section { margin-top: 1.4rem; }
.detail-section .eyebrow { margin-bottom: 0.35rem; }

/* Reading progress: indigo, because it is a working state — vermilion stays reserved
   for circulation and danger. The bar is a plain ruled trough, like a ledger column. */
.progress-track { height: 6px; margin: 0.4rem 0 0.6rem; background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: 1px; overflow: hidden; }
.progress-fill { height: 100%; background: var(--accent); }
.progress-log { list-style: none; padding: 0; margin: 0.7rem 0 0; display: grid; gap: 0.25rem; font-size: 13px; }
.progress-log li { display: flex; align-items: baseline; gap: 0.6rem; }
.progress-log li .mono:first-child { min-width: 4.5rem; }
/* In a props row (share pages) the bar takes its own line under the figures instead of shrinking to nothing. */
.props dd .progress-track { flex-basis: 100%; margin: 0.2rem 0 0; }
.feed-progress { margin-top: 0.3rem; font-size: 12px; }
.feed-progress li .mono:first-child { min-width: 2.8rem; }
.feed-progress li .mono:nth-child(2) { min-width: 4rem; } /* so the dates line up whatever the page number */

.circulation { border: 1px solid var(--line); border-left: 3px solid var(--stamp); border-radius: var(--radius); padding: 0.8rem 1rem; margin-top: 1.4rem; background: var(--surface); }
.circulation.free { border-left-color: var(--line-strong); }
/* "Lent before": the item's returned loans as a ruled ledger list — borrower, then the span and its length in
   mono. Each line wraps rather than scrolls, so a long household name never widens the page. */
.loan-history { list-style: none; padding: 0; margin: 0; font-size: 13px; border-top: 1px solid var(--line); }
.loan-history > li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.8rem; padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
.loan-history > li > strong { flex: 1 1 12rem; min-width: 0; overflow-wrap: anywhere; font-weight: 600; }
.loan-history > li > .mono { font-size: 12px; white-space: nowrap; }
.loan-history > li > .mono:last-child { min-width: 4.5rem; text-align: right; }
.loan-history-more { font-size: 12px; margin: 0.4rem 0 0; }

.actions { display: flex; gap: 0.5rem; align-items: center; margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--line); }

/* ── series (§16 #52) ─────────────────────────────────────────────────────────
   A series reads as a row of numbered slots, like a ledger's column of accession
   numbers: held volumes are ruled boxes, the one you're on is inked indigo, the
   ones you've finished carry the indigo rule, and missing numbers are dashed
   blanks — the same dashed hand as "Not owned". No vermilion: a gap is not danger. */
.series-fields { grid-template-columns: minmax(0, 1fr) 9rem; }
.series-title { margin: 0 0 0.5rem; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.5rem; }
.series-title a { font-weight: 600; }
.series-title .mono { font-size: 12px; }
.series-strip { list-style: none; padding: 0; margin: 0 0 0.6rem; display: flex; flex-wrap: wrap; gap: 0.3rem; }
.series-strip .vol {
  font-family: var(--mono);
  font-size: 11px;
  min-width: 1.9rem;
  height: 1.9rem;
  padding: 0 0.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink-2);
}
.series-strip a.vol:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.series-strip .vol.finished { border-color: var(--accent); color: var(--accent); background: var(--accent-tint); }
.series-strip .vol.current { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.series-strip .vol.missing { border-style: dashed; background: transparent; color: var(--ink-3); }
.next-up, .series-gaps { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; margin: 0 0 0.5rem; font-size: 13px; }
.next-up .eyebrow, .series-gaps .eyebrow { margin: 0; }
.next-up .mono, .series-gaps .mono { font-size: 12px; }
.next-skipped { flex-basis: 100%; }
.series-panel { margin-bottom: 1.25rem; }
.series-panel > :last-child { margin-bottom: 0; }
.series-edit { margin-top: 1.5rem; }
.series-list { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--line); }
.series-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid var(--line); }
.series-list .series-name { font-weight: 600; overflow-wrap: anywhere; }
.series-list .mono { font-size: 12px; }
.volume-ledger { list-style: none; padding: 0; margin: 0; border-top: 1px solid var(--line); }
.volume-ledger li { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr) auto; gap: 0.2rem 0.75rem; align-items: baseline; padding: 0.6rem 0; border-bottom: 1px solid var(--line); }
.volume-ledger .vol-no { font-size: 12px; color: var(--ink-2); }
.volume-ledger .vol-body { min-width: 0; display: flex; flex-direction: column; }
.volume-ledger .vol-title { font-weight: 600; overflow-wrap: anywhere; }
.volume-ledger .vol-state { display: flex; flex-wrap: wrap; gap: 0.3rem; justify-content: flex-end; }
.volume-ledger li.finished .vol-no { color: var(--accent); }
.volume-ledger li.gap { border-bottom-style: dashed; }
.volume-ledger li.gap .vol-no, .volume-ledger li.gap .vol-body { color: var(--ink-3); font-style: italic; }
.candidate-series { display: block; }
@media (max-width: 560px) {
  .series-fields { grid-template-columns: minmax(0, 1fr) 6.5rem; }
  .volume-ledger li { grid-template-columns: 3.5rem minmax(0, 1fr); }
  .volume-ledger .vol-state { grid-column: 2; justify-content: flex-start; }
}

/* ── add flow ─────────────────────────────────────────────────────────────── */

.tab-bar { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--radius-sm); overflow: hidden; margin-bottom: 1.25rem; }
.tab-bar .tab { border: none; border-radius: 0; background: var(--surface); color: var(--ink-2); height: 32px; font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; }
.tab-bar .tab + .tab { border-left: 1px solid var(--line-strong); }
.tab-bar .tab.active { background: var(--accent-tint); color: var(--accent-strong); }
.tab-panel[hidden] { display: none; }

#scanner-video { width: 100%; max-width: 560px; max-height: 45vh; border-radius: var(--radius); background: #000; display: none; box-shadow: var(--shadow); }
#scanner-video.live { display: block; margin-bottom: 0.75rem; }
#scanner-status { font-family: var(--mono); font-size: 11.5px; }

.candidate { display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 1rem; padding: 0.9rem; margin: 0.75rem 0; background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius); }
.candidate-cover img, .candidate-cover .cover-fallback { width: 84px; aspect-ratio: 2 / 3; object-fit: cover; border-radius: var(--radius-sm); box-shadow: 0 0 0 1px var(--line); display: flex; align-items: center; justify-content: center; font-size: 1.8rem; background: var(--surface-2); }
.candidate-body strong { font-size: 14px; display: block; } /* its own line, even when no creator line follows */
.candidate-body small { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.03em; }
/* a result the catalog already has: a pill on a line of its own, opening that item */
.candidate-body .in-catalog { display: block; width: fit-content; margin-top: 0.35rem; text-decoration: none; }
.candidate-body .in-catalog:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.candidate-save { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.65rem; }
#tab-search .inline-form input[type='search'] { flex: 1 1 16rem; }
/* the typed-barcode field: its label beside it, wrapping under it on a phone */
#tab-scan .inline-form label { gap: 0.5rem; flex-wrap: wrap; }
.candidate-save select { width: auto; margin: 0; }

/* scans held on the device while offline, waiting on the Add page (ARCH.md §16 #48) — candidate cards under a notice */
.scan-review { margin-bottom: 1.75rem; }
.scan-review-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem 1.25rem; margin: 0; }
.scan-review-head p { margin: 0.2rem 0 0; }
.scan-review-head select { width: auto; max-width: 100%; margin: 0; }
/* a long shelf name wraps inside the button rather than pushing it off a phone */
.scan-review-head button { white-space: normal; height: auto; min-height: 30px; padding-top: 0.3rem; padding-bottom: 0.3rem; line-height: 1.2; text-align: center; }
.review-scan { display: block; text-transform: uppercase; color: var(--ink-3); margin-bottom: 0.3rem; overflow-wrap: anywhere; }
.review-entry[data-added] { margin: 0.75rem 0; }
.review-trouble { margin: 0.65rem 0 0; }

/* ── auth (login / setup / account) ───────────────────────────────────────── */

.auth-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1.5rem; background: var(--paper); }
.auth-card { width: 100%; max-width: 380px; background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius); padding: 1.75rem 1.75rem 1.5rem; box-shadow: var(--shadow); }
.auth-card .brand-name { font-size: 15px; }
.auth-card .brand { margin-bottom: 1.25rem; padding: 0; }
.auth-card h1 { font-size: 18px; }

/* ── share (public) ───────────────────────────────────────────────────────── */

.share-panel { margin-top: 0.9rem; }
/* an h2 under the page's h1 (headings don't skip levels), set as the small heading it always looked like */
.share-panel-head { font-size: 12.5px; color: var(--ink-2); }
/* a publish form's link name: wide enough to read its whole placeholder, and the row's width on a phone */
.inline-form input.share-name { flex: 0 1 22rem; min-width: 0; }
.share-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.55rem 0;
  border-top: 1px solid var(--line);
}

.share-shell, .offline-shell { max-width: 1020px; margin: 0 auto; padding: 2rem 2rem 3rem; } /* the app's 2rem desktop gutter */
.offline-shell { max-width: 640px; } /* the installed app's stand-in for a page it can't load offline: public/offline.html */
.share-head, .offline-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; border-bottom: 1px solid var(--line); padding-bottom: 0.9rem; margin-bottom: 1.5rem; flex-wrap: wrap; }
.share-head h1, .offline-head h1 { margin: 0; }
.share-mark, .offline-mark { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.14em; color: var(--ink-3); }
.share-footer { margin-top: 3rem; border-top: 1px solid var(--line); padding-top: 1rem; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; color: var(--ink-3); display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem 1.5rem; }

/* "Powered by BGG" — BoardGameGeek's own mark, owed beside its data (ARCH.md §16 #44). Shown as BGG ships it,
   never recoloured: the <picture> swaps in BGG's reversed file for the lamp-lit theme. 32px tall keeps its
   lettering legible, which BGG's terms ask for; it is a credit, so it sits quietly below what it credits. */
.bgg-credit { display: inline-block; line-height: 0; border-radius: var(--radius-sm); flex: none; }
.bgg-credit img { display: block; height: 32px; width: auto; } /* 144px wide: fits any phone, never squeezed */
.bgg-credit:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.bgg-attribution { margin: 1.4rem 0 0; }

/* "Data provided by Discogs." — Discogs' terms want those words beside its data, linked to the release's page, and
   its notice (ARCH.md §16 #63). Text, no logo: small and quiet like the BGG credit, a working link in the accent,
   the notice in the muted ink below it. Long words wrap rather than push a phone's page sideways. */
.discogs-attribution { margin: 0.9rem 0 0; font-size: 11.5px; line-height: 1.45; color: var(--ink-3); overflow-wrap: anywhere; }
.discogs-attribution p { margin: 0; }
.discogs-attribution .discogs-notice { margin-top: 0.2rem; }
.discogs-credit { text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
.discogs-credit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.candidate-credit { display: block; }
.discogs-notice { font-size: 10.5px; color: var(--ink-3); overflow-wrap: anywhere; } /* also alone, once, below the Add page's Discogs results */

/* ── misc lists ───────────────────────────────────────────────────────────── */

.details-list { display: grid; grid-template-columns: max-content 1fr; gap: 0.1rem 1rem; font-size: 12.5px; }
.details-list dt { font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-3); padding-top: 0.15rem; }
.details-list dd { margin: 0; font-family: var(--mono); font-size: 12px; } /* catalogue data: ISBNs, ids, formats */

/* ── responsive ───────────────────────────────────────────────────────────── */

@media (max-width: 880px) {
  .app { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(78vw, var(--sidebar-w));
    z-index: 40;
    transform: translateX(-102%);
    /* closed, it's out of the tab order and the accessibility tree — hidden only once the slide has finished */
    visibility: hidden;
    transition: transform 160ms ease, visibility 0s linear 160ms;
    box-shadow: var(--shadow);
  }
  body.nav-open .sidebar { transform: translateX(0); visibility: visible; transition: transform 160ms ease, visibility 0s; }
  /* a section header is tapped with a thumb in the drawer: a taller target than the pointer needs */
  .nav-summary { min-height: 34px; }
  .mobile-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 1rem;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
    position: sticky;
    top: 0;
    z-index: 30;
  }
  .mobile-bar .brand-name { font-size: 14px; }
  /* A 36px target, pulled left by its own slack so the ☰ ink sits on the 16px gutter with the page title below
     (the glyph is ~12px wide: (36 − 12) / 2 = 12px of air on each side). */
  #nav-toggle { font-size: 18px; width: 36px; height: 36px; padding: 0; margin-left: -12px; color: var(--ink); }
  /* the open drawer dims the page behind it; a tap there closes it (app.js) */
  body.nav-open::after { content: ''; position: fixed; inset: 0; background: rgba(23, 19, 16, 0.35); z-index: 35; }
  .mobile-bar .mobile-unread { margin-left: auto; text-decoration: none; }
  .content { padding: 1.1rem 1rem 3rem; }
  .app-status { margin-inline: 1rem; } /* the phone's 16px gutter, as the content's */
  /* one phone gutter everywhere: public share pages and the login card sit on the same 16px line as the app */
  .share-shell, .offline-shell { padding: 1.25rem 1rem 3rem; }
  .auth-shell { padding: 1rem; }
  .item-detail { grid-template-columns: 1fr; }
  .item-detail-cover { position: static; max-width: 190px; }
  .data-table .hide-sm { display: none; }
  .stat-row { grid-template-columns: repeat(2, 1fr); }
  /* two-column reflow for any stat count (the not-owned stat is conditional) */
  .stat:nth-child(odd) { border-left: none; }
  .stat:nth-child(n + 3) { border-top: 1px solid var(--line); }
  /* an odd count leaves the last stat alone on its row: let it take the whole row */
  .stat:last-child:nth-child(odd) { grid-column: 1 / -1; }

  /* Row actions stay on screen: the buttons stack and wrap their words instead of pushing the table
     wider than the phone, and a long share URL keeps a readable column instead of one word per line. */
  .data-table th, .data-table td { padding-left: 0.45rem; padding-right: 0.45rem; }
  .data-table .actions-cell { white-space: normal; }
  .data-table .actions-cell .inline-form { flex-direction: column; align-items: flex-end; }
  .data-table .actions-cell button,
  .data-table .actions-cell .btn { white-space: normal; height: auto; min-height: 30px; padding: 0.3rem 0.55rem; line-height: 1.2; text-align: center; }
  .data-table td:has(> .break-anywhere) { min-width: 7rem; }
  .data-table .col-type { display: none; }
  /* the item table still scrolls on a phone (Columns trims it), but a long title no longer hides Status */
  .cell-title { max-width: 11rem; }
  .data-table .sub-settings select { max-width: 7.5rem; }
  /* Members: a display-name box narrow enough that Reset password and Remove stay on the phone's screen */
  .data-table .display-name-form input { width: 6.5rem; }
  .data-table .sub-settings input[type='number'] { width: 4.25rem; }
  /* columns already hidden on a phone have nothing to toggle */
  #columns-menu label:has(> [data-col='type'], > [data-col='shelf'], > [data-col='year'], > [data-col='completed'], > [data-col='tags'], > [data-col='acc']) { display: none; }
}

@media (min-width: 881px) {
  #nav-toggle { display: none; }
}

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

/* ── connections ──────────────────────────────────────────────────────────── */

/* a section's form, table and notes keep apart, in whatever order they come */
.fed-section > :not(.eyebrow) + * { margin-top: 0.75rem; }

/* ── members, account ─────────────────────────────────────────────────────── */

/* A household switch — Shared links' progress and names, Connections' progress, names and goals, Members' currency:
   the control and its label, then what it does, then Save, left-aligned under them, the same at every width. */
.switch-form { display: grid; justify-items: start; gap: 0.55rem; max-width: 46rem; }
.switch-form > label { display: flex; align-items: baseline; gap: 0.45rem; margin: 0; font-size: 12.5px; }
.switch-form > label input[type='checkbox'] { margin: 0; flex: none; transform: translateY(0.1rem); }
.switch-form > p { margin: 0; }
.switch-field { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.6rem; max-width: 100%; }
.switch-field label { margin: 0; }
.switch-field select { width: auto; max-width: 100%; margin: 0; }
/* one switch after another (or after a section's intro) keeps the same gap everywhere */
:is(section, .fed-section) > :not(.eyebrow) + .switch-form { margin-top: 1.4rem; }

/* Members' "Add a member" and "Household currency": clear of what's above, and a form's note clear of its button */
.settings-section { margin-top: 1.5rem; }
.settings-section > form + p { margin-top: 0.75rem; }
/* Account's password and display-name panels: two frames, not one frame touching the next */
.account-card + .account-card { margin-top: 1rem; }
.account-card form + .form-note { margin-top: 0.75rem; }

/* ── connections: feed ────────────────────────────────────────────────────── */

.feed { display: grid; gap: 0.75rem; max-width: 46rem; }
.feed-card { display: grid; grid-template-columns: 64px 1fr; gap: 0.9rem; padding: 0.8rem 0.9rem; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.feed-cover { width: 64px; aspect-ratio: 2 / 3; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); }
.feed-cover .cover-fallback { font-size: 1.6rem; }
.feed-body { min-width: 0; }
.feed-body .eyebrow { margin: 0 0 0.25rem; }
.feed-date { white-space: nowrap; } /* a date never breaks at its hyphens under a long household name */
.feed-line { margin: 0 0 0.2rem; line-height: 1.35; overflow-wrap: anywhere; }
.feed-line small { color: var(--ink-2); }
.feed-review { margin: 0.45rem 0 0; font-size: 13px; overflow-wrap: anywhere; }
.feed-burst { max-width: 46rem; margin: 0; border-color: var(--line); } /* framed like the cards around it */
.feed-burst::after { content: none; }
.feed-burst summary .mono { font-size: 11.5px; font-weight: 400; color: var(--ink-2); }
.sub-settings { display: flex; flex-wrap: wrap; gap: 0.35rem 0.6rem; align-items: center; }
.sub-settings label { display: inline-flex; gap: 0.35rem; align-items: center; margin: 0; font-size: 12.5px; }
.sub-settings select, .sub-settings input[type='number'] { width: auto; margin: 0; }
.sub-settings input[type='number'] { width: 5.5rem; }
.view-form select { width: auto; margin: 0; }
/* a failed pull, under its view's name: its own line, and wide enough not to wrap every two words */
.pull-error { display: block; margin-top: 0.15rem; line-height: 1.35; }
@media (min-width: 881px) {
  .pull-error { min-width: 10rem; }
}
.view-form input { flex: 1 1 14rem; }

/* ── connections: comments ────────────────────────────────────────────────── */

.thread { margin: 0.6rem 0 0; }
details.thread { padding: 0.45rem 0.7rem; }
details.thread::after { content: none; }
.thread-head { margin: 0.6rem 0 0.2rem; font-size: 12.5px; }
.comment { border-left: 2px solid var(--line-strong); padding: 0.05rem 0 0.05rem 0.7rem; margin: 0.45rem 0; }
.comment-meta { margin: 0; font-size: 11.5px; color: var(--ink-2); }
.comment-meta .inline { display: inline; margin-left: 0.45rem; }
.comment-meta .mono { white-space: nowrap; }
button.comment-delete, button.progress-delete { height: auto; line-height: 1; padding: 0; border: none; background: none; font-size: 11.5px; font-weight: 400; color: var(--ink-3); text-decoration: underline; text-underline-offset: 2px; }
button.comment-delete:hover, button.progress-delete:hover { background: none; color: var(--stamp); }
.comment .prewrap { margin: 0.15rem 0 0; font-size: 13px; overflow-wrap: anywhere; }
.comment-form { display: grid; gap: 0.4rem; margin: 0.5rem 0 0.3rem; }
.comment-form textarea { margin: 0; min-height: 3.2rem; }
.comment-form button { justify-self: start; }
.recent-comments { list-style: none; padding: 0; margin: 0 0 1.25rem; display: grid; gap: 0.3rem; font-size: 13px; }

/* ── connections: borrowing ───────────────────────────────────────────────── */

.household-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.35rem; }
.household-list small { margin-left: 0.4rem; }
.request-form { display: grid; gap: 0.45rem; max-width: 30rem; margin-top: 0.4rem; }
.request-form textarea { margin: 0; min-height: 3rem; }
.request-form button { justify-self: start; }
.request-actions { display: flex; gap: 0.5rem; align-items: center; justify-content: flex-end; }
.back-link { margin: 1.25rem 0 0; }
/* the Account page's release line: which version this instance runs, for whoever maintains it */
.version-line { margin: 1.25rem 0 0; font-size: 12.5px; }
/* on a phone the date, Lend and Decline stack, leaving the book and who asked room to read */
@media (max-width: 880px) {
  .request-actions, .data-table .request-actions .inline-form { flex-wrap: wrap; }
}

/* ── connections: recommendations (§16 #58) ─────────────────────────────────
   Sending is a quiet disclosure on the item's page, indigo like any working form —
   vermilion stays for circulation. The Recommended list reuses the feed's cards:
   what arrives from a connection reads the same wherever it shows. */
details.recommend { margin-top: 1.4rem; }
details.recommend > summary { font-size: 12.5px; }
.recommend-form { display: grid; gap: 0.35rem; max-width: 30rem; }
.recommend-form label { margin: 0.3rem 0 0; font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.recommend-form select, .recommend-form textarea { margin: 0; width: 100%; }
.recommend-form textarea { min-height: 3rem; }
.recommend-form #recommend-hint { margin: 0.1rem 0 0.3rem; font-size: 12px; }
.recommend-form button { justify-self: start; }
.recommend-sent { list-style: none; padding: 0; margin: 0.8rem 0 0; font-size: 12.5px; display: grid; gap: 0.2rem; overflow-wrap: anywhere; }
.recommend-sent .mono { font-size: 11.5px; }
.recommend-list { list-style: none; padding: 0; margin: 0 0 1.5rem; }
.recommend-from { margin: 0.1rem 0 0; font-size: 12.5px; color: var(--ink-2); overflow-wrap: anywhere; }
.recommend-from .mono { font-size: 11.5px; white-space: nowrap; }
.recommend-note { border-left: 2px solid var(--line-strong); padding-left: 0.6rem; }
.recommend-actions { display: flex; flex-wrap: wrap; gap: 0.5rem 0.8rem; align-items: center; margin-top: 0.6rem; font-size: 12.5px; }
.recommend-actions .inline { margin: 0; }
.recommend-sent-section { margin-top: 1.5rem; }

/* ── notifications ───────────────────────────────────────────────────────── */
.notifications { list-style: none; padding: 0; margin: 0 0 1.25rem; display: grid; gap: 0; border-top: 1px solid var(--line); }
.notifications li { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; padding: 0.6rem 0; border-bottom: 1px solid var(--line); font-size: 13.5px; }
.notifications li a { position: relative; padding-left: 1rem; color: var(--ink); text-decoration: none; } /* read and unread text start at one edge */
.notifications li a:hover { text-decoration: underline; }
.notifications li.unread a::before { content: ''; position: absolute; left: 0; top: 0.55em; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.notifications li .mono { flex-shrink: 0; font-size: 11.5px; }

/* ── reading again (ARCH.md §16 #41) ─────────────────────────────────────────
   A book's reads: the current one's controls, and every read so far. Existing tokens
   only — the re-reading marker is the indigo working accent, dashed because the book's
   own status (Completed) still stands beside it. */
.pill.rereading { border-color: var(--accent); border-style: dashed; color: var(--accent); }
.status-pills { display: inline-flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; }
.reading-summary { margin: 0 0 0.4rem; font-size: 13.5px; }
.read-actions { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; align-items: center; margin: 0.6rem 0 0; }
.read-history-head { margin-top: 1rem; }
.read-history { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.3rem; font-size: 13px; border-top: 1px solid var(--line); }
.read-history > li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 0.8rem; padding: 0.4rem 0 0; }
.read-history > li > .mono:first-child { min-width: 2.2rem; color: var(--ink-3); }
/* a read's Edit is a quiet link on its own line of the history, opening the form beneath it */
details.read-edit { border: none; background: none; padding: 0; margin: 0; }
details.read-edit::after { content: none; }
details.read-edit > summary { list-style: none; font-size: 11.5px; font-weight: 400; color: var(--ink-3); text-decoration: underline; text-underline-offset: 2px; }
details.read-edit > summary::-webkit-details-marker { display: none; }
details.read-edit > summary:hover { color: var(--accent); }
details.read-edit[open] { flex-basis: 100%; padding: 0.3rem 0 0.5rem; }
details.read-edit[open] > summary { margin-bottom: 0.45rem; }
/* each form an edit holds (a review's Save, then Delete, then Move) sits clear of the one above */
details.read-edit form + form { margin-top: 0.4rem; }
details.read-add { margin-top: 0.7rem; }
.form-note { margin: -0.25rem 0 0.75rem; font-size: 12.5px; }
.read-count { font-size: 11px; }

/* ── each member's reading and reviews (ARCH.md §16 #43) ─────────────────────
   A book's page names people once the household has more than one: each reader's
   reads under their name, and everyone's rating and review. Existing tokens only —
   a person is an eyebrow-weight mono name over a hairline, like the ledger's other
   headings, and the viewer's own block keeps the indigo working accent. */
.reader { margin-top: 1rem; padding-top: 0.6rem; border-top: 1px solid var(--line); }
.reader.reader-self { margin-top: 0.2rem; padding-top: 0; border-top: none; }
.reader + .reader { margin-top: 1.1rem; }
.reader-name { font-family: var(--mono); font-size: 11.5px; font-weight: 600; letter-spacing: 0.04em; color: var(--ink); margin: 0 0 0.35rem; overflow-wrap: anywhere; }
.reader-self > .reader-name { color: var(--accent); }
.reader .read-history { margin-top: 0.4rem; }
.member-reviews { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.8rem; }
.member-reviews > li { padding-top: 0.55rem; border-top: 1px solid var(--line); min-width: 0; }
.member-reviews > li:first-child { border-top: none; padding-top: 0; }
.review-by { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.7rem; margin: 0 0 0.3rem; font-size: 12.5px; }
.review-by .reviewer { font-family: var(--mono); font-size: 11.5px; font-weight: 600; letter-spacing: 0.04em; overflow-wrap: anywhere; }
.review-by .reviewer-self { color: var(--accent); }
.review-by .mono { font-size: 11px; }
.member-reviews .prewrap { margin: 0 0 0.35rem; overflow-wrap: anywhere; }
/* a review's own form: rating, text, Save — stacked, since the text wants the width */
.review-form { display: grid; gap: 0.45rem; max-width: 560px; }
.review-form select { width: auto; margin: 0; justify-self: start; }
.review-form textarea { margin: 0; }
.review-form button { justify-self: start; }
.rating-note { font-size: 12px; }
/* the search box keeps its width beside the Read by select, and wraps under it on a phone */
form.search-by-reader { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
form.search-by-reader input[type='search'] { flex: 1 1 17rem; width: auto; margin: 0; } /* past a phone's width with the select beside it, so the select wraps under it, never the button alone */
form.search-by-reader select { width: auto; margin: 0; max-width: 100%; }
.toolbar select[name='readBy'] { max-width: 100%; }

/* ── read next (ARCH.md §16 #46) ─────────────────────────────────────────────
   The Overview's one suggestion: a framed panel (the manuscript double rule), its
   cover beside the title, like an add-flow candidate. Existing tokens only — the
   accession number in mono, "Not owned" as the ghost pill it is everywhere. */
.read-next { display: grid; grid-template-columns: 84px minmax(0, 1fr); gap: 1rem; max-width: 46rem; padding: 0.9rem; }
.read-next-cover { display: block; width: 84px; aspect-ratio: 2 / 3; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); }
.read-next-cover .cover-fallback { font-size: 1.8rem; }
.read-next-body { min-width: 0; }
.read-next-title { display: block; color: var(--ink); font-size: 15px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.read-next-title:hover { color: var(--accent); }
.read-next-by { margin: 0.15rem 0 0; color: var(--ink-2); font-size: 12.5px; overflow-wrap: anywhere; }
.read-next-line { display: flex; flex-wrap: wrap; gap: 0.15rem 0.5rem; align-items: center; margin: 0.35rem 0 0; }
.read-next .read-actions { gap: 0.5rem; margin-top: 0.8rem; }
/* not owned: its own line, in ink rather than muted, with the pill drawn solid so it can't be missed */
.read-next-unowned { font-size: 12.5px; color: var(--ink); }
.read-next-unowned .pill.ghost { border-style: solid; border-color: var(--ink-2); color: var(--ink); }
.read-next-empty { margin: 0; font-size: 13px; }
@media (max-width: 880px) {
  .read-next { grid-template-columns: 64px minmax(0, 1fr); gap: 0.8rem; }
  .read-next-cover { width: 64px; }
}

/* ── what should we play tonight (ARCH.md §16 #60) ───────────────────────────
   Three labelled controls and two buttons, then a ledger of games: a small cover, the
   title, and its facts in mono — players, time, weight, last played. A fact the page
   doesn't know is set in the faint ink, italic, since the filters depend on it. The pick
   borrows Read next's framed panel. Existing tokens only. */
.play-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0 1rem; align-items: end; max-width: 46rem; }
.play-filters label { margin-bottom: 0.8rem; }
.play-buttons { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.8rem; }
.play-status { display: block; max-width: 46rem; margin: 0.2rem 0 1rem; font-family: var(--mono); font-size: 12px; color: var(--ink-2); }
.game-list { list-style: none; margin: 0; padding: 0; max-width: 46rem; border-top: 1px solid var(--line); }
.game-row { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 0.85rem; padding: 0.7rem 0; border-bottom: 1px solid var(--line); }
.game-thumb { display: block; width: 44px; aspect-ratio: 2 / 3; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); }
.game-thumb .cover-fallback { font-size: 1.2rem; }
.game-body { min-width: 0; }
.game-title { display: block; color: var(--ink); font-size: 14px; font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.game-title:hover { color: var(--accent); }
.game-by { display: block; margin-top: 0.1rem; color: var(--ink-2); font-size: 12px; overflow-wrap: anywhere; }
.game-facts { display: flex; flex-wrap: wrap; gap: 0.15rem 0.9rem; margin: 0.3rem 0 0; font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); }
.game-facts .missing { color: var(--ink-3); font-style: italic; }
.play-pick .eyebrow { margin-bottom: 0.2rem; }
.play-pick .game-facts { margin-top: 0.45rem; }
.play-pick .read-actions a { font-size: 13px; }
.play-unknown { margin-top: 2rem; max-width: 46rem; }
.play-unknown > .muted { font-size: 12.5px; margin: 0 0 0.6rem; }
.play-more, .play-empty { max-width: 46rem; margin: 0.8rem 0 0; font-size: 12.5px; }
.game-night { margin: 0; font-size: 13px; }

/* ── bulk edit (ARCH.md §16 #47) ─────────────────────────────────────────────
   A checkbox per item, and a bar that stays within reach at the foot of the screen
   while anything is selected. Existing tokens only: the bar is a framed surface with
   the indigo working accent down its edge and on its count — vermilion stays for the
   Delete it leads to. With :has, CSS alone shows the bar once a box is checked, and
   the tag field or shelf menu only for the actions that use them, JavaScript or not;
   a browser without :has shows all of it, all the time, and it still works. */
/* The checkbox column costs the table as little width as it can: no right padding, the Title cell beside it gives
   up some of its left, and on a wide screen the other cells a little of theirs — together about the column's own
   width, so a table that just fitted before still fits rather than scrolling inside its frame. A phone keeps its
   own tighter padding (the responsive rules below). */
@media (min-width: 881px) {
  .data-table:has(.col-pick) :is(th, td) { padding-left: 0.62rem; padding-right: 0.62rem; }
}
.data-table :is(th, td).col-pick { width: 1%; padding-left: 0.6rem; padding-right: 0; }
.data-table .col-pick + :is(th, td) { padding-left: 0.45rem; }
.col-pick input { margin: 0; width: 15px; height: 15px; vertical-align: middle; }
/* 60% of the tint, as a hovered row keeps half (muted text holds 4.5:1), and a firmer rule than hover's */
.data-table tbody tr:has(.bulk-pick:checked) { background: color-mix(in srgb, var(--accent-tint) 60%, transparent); }
.data-table tbody tr:has(.bulk-pick:checked) > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

.pick-cell { position: relative; min-width: 0; }
/* a 30px target over the cover's corner, so a thumb can hit it on a phone */
.pick { position: absolute; top: 6px; left: 6px; margin: 0; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); background: var(--surface); box-shadow: 0 0 0 1px var(--line-strong); cursor: pointer; }
.pick input { margin: 0; width: 16px; height: 16px; }
.pick-cell:has(.bulk-pick:checked) .item-cover { box-shadow: 0 0 0 2px var(--accent); }
.bulk-all { display: inline-flex; align-items: center; margin: 0 0 0.75rem; font-size: 12.5px; font-weight: 400; color: var(--ink-2); cursor: pointer; }

.bulk-bar {
  position: sticky;
  bottom: 0.75rem;
  z-index: 10;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin: 1rem 0;
  padding: 0.6rem 0.8rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.bulk-status { display: flex; flex-wrap: wrap; align-items: center; gap: 0.2rem 0.6rem; min-width: 0; }
.bulk-count { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent-strong); }
.bulk-status small { font-family: var(--mono); font-size: 10.5px; }
button.bulk-clear { height: 24px; padding: 0 0.45rem; font-weight: 400; }
.bulk-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; min-width: 0; }
.bulk-controls select, .bulk-controls input { width: auto; max-width: 100%; margin: 0; }
.bulk-controls .bulk-tag { width: 11rem; }
@supports selector(:has(a)) {
  body:not(:has(.bulk-pick:checked)) .bulk-bar { display: none; }
  .bulk-bar:not(:has(option[value^='tag-']:checked)) .bulk-tag { display: none; }
  .bulk-bar:not(:has(option[value='move']:checked)) .bulk-shelf { display: none; }
}

/* a delete's confirmation: how many, and which */
.bulk-confirm { max-width: 640px; }
.bulk-titles { margin: 0 0 0.75rem; padding-left: 0; list-style: none; display: grid; gap: 0.2rem; border-top: 1px solid var(--line); }
.bulk-titles li { display: flex; align-items: baseline; gap: 0.7rem; padding: 0.3rem 0; border-bottom: 1px solid var(--line); min-width: 0; }
.bulk-titles .t { min-width: 0; overflow-wrap: anywhere; }

@media (max-width: 880px) {
  .bulk-bar { bottom: 0.5rem; padding: 0.55rem 0.65rem; }
  /* the action and Apply share the first row; the tag or shelf it needs takes the whole row under them */
  .bulk-controls { width: 100%; }
  .bulk-controls select[name='action'] { flex: 1 1 9rem; min-width: 0; }
  .bulk-controls .bulk-tag, .bulk-controls .bulk-shelf { order: 1; flex: 1 1 100%; width: auto; min-width: 0; }
}

/* ── the play log (ARCH.md §16 #54) ──────────────────────────────────────────
   A board game's plays or a record's listens: the household's count, the Played
   button with the day it logs, and the recent dates as a ruled ledger column —
   mono, like every date. Indigo stays the working accent (the button); nothing here
   is circulation or danger, so no vermilion but Remove's hover, as on a page. */
.play-form { margin: 0.35rem 0 0; }
.play-form label { gap: 0.4rem; }
/* the lend form's due date says what it is, as Played's "on" does */
.lend-form label { gap: 0.4rem; }
.play-log { list-style: none; margin: 0.7rem 0 0; padding: 0.4rem 0 0; display: grid; gap: 0.25rem; font-size: 13px; border-top: 1px solid var(--line); }
.play-log li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.8rem; min-width: 0; }
.play-log li > time { min-width: 6.5rem; font-size: 12px; }
.play-by { font-size: 12px; overflow-wrap: anywhere; }
.play-more { margin: 0.5rem 0 0; font-size: 12.5px; }

/* ── a record's condition and pressing (ARCH.md §16 #55) ─────────────────────
   Grades are the copy's own, set in the ledger's mono like every other code; the
   pressing is catalogue data, listed as details are. The tracklist folds away —
   a double album runs to thirty lines — and on a phone keeps three columns:
   position and time stay narrow, the title takes the rest and wraps. */
.pill.grade { color: var(--ink); background: var(--surface-2); }
.details-list { grid-template-columns: max-content minmax(0, 1fr); }
.details-list dd { overflow-wrap: anywhere; }
details.tracklist { margin: 0.8rem 0 0; padding: 0.5rem 0.8rem; }
details.tracklist summary .mono { font-weight: 400; font-size: 11px; }
.tracks { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.tracks > li { display: grid; grid-template-columns: 2.6rem minmax(0, 1fr) auto; gap: 0 0.75rem; align-items: baseline; padding: 0.3rem 0; border-top: 1px solid var(--line); }
.tracks > li:first-child { border-top: none; }
.track-pos { font-size: 11px; color: var(--ink-3); overflow-wrap: anywhere; }
.track-title { overflow-wrap: anywhere; }
.track-artist { font-size: 12px; }
.track-time { font-size: 11.5px; color: var(--ink-2); text-align: right; }
.tracks > li.track-heading { display: block; font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink-3); padding-top: 0.7rem; }
.discogs-refresh, .discogs-note, .bgg-refresh, .bgg-note { margin: 0.8rem 0 0; }
.discogs-refresh small, .discogs-note, .bgg-refresh small, .bgg-note { font-size: 12px; }
/* a refresh's result, in a live region that is on the page from the start (so what htmx puts in it is announced):
   empty, it takes no room; "Asking Discogs…" while the request is out, then the answer's sentence */
.refresh-status { margin: 0.8rem 0 0; }
.refresh-status:empty { padding: 0; border: 0; margin: 0; }

/* ── reading goals (ARCH.md §16 #49) ──────────────────────────────────────────
   A goal is a count against a year: the figures in the registrar's mono, the bar the
   same ruled trough as reading progress, and a thin ink tick where a year-long pace
   (from 1 January) stands today, said in words in the small note under the bar.
   Indigo when on pace, ahead or reached; "behind" is a dashed ruled pill —
   falling behind on a book count is no emergency, so no vermilion. */
.goal { max-width: 40rem; }
.goal-meter { min-width: 0; }
.goal-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.55rem; margin: 0; }
.goal-count { font-family: var(--mono); font-size: 18px; font-weight: 600; letter-spacing: -0.02em; }
.goal-track { position: relative; display: flow-root; } /* flow-root: the bar's top margin stays inside, so the pace tick straddles the bar */
.goal-track .progress-track { height: 8px; }
.goal-pace { position: absolute; top: calc(0.4rem - 3px); width: 2px; height: 14px; margin-left: -1px; background: var(--ink-2); border-radius: 1px; pointer-events: none; }
.pill.reached { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); }
.pill.behind { border-style: dashed; }
.goal-note { margin: -0.3rem 0 0.5rem; font-size: 11.5px; color: var(--ink-3); }
.goal-edit { font-size: 12.5px; }
.goal-member { margin-bottom: 1rem; }
.goal-member select { width: auto; margin: 0 0 0 0.35rem; max-width: 100%; }
.goal-form { display: flex; flex-wrap: wrap; gap: 0.5rem 0.9rem; align-items: flex-end; margin-bottom: 0.9rem; }
#goal-form { margin-bottom: 1.5rem; }
.goal-form label { margin: 0; }
.goal-form select, .goal-form input[type='number'] { display: block; width: 7rem; margin: 0.25rem 0 0; }
.goal-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.2rem; max-width: 40rem; }
.goal-list > li { display: grid; grid-template-columns: 3.5rem minmax(0, 1fr) auto; gap: 0.3rem 0.9rem; align-items: center; padding: 0.55rem 0; border-top: 1px solid var(--line); }
.goal-list > li:first-child { border-top: none; }
.goal-year { font-size: 13px; font-weight: 600; color: var(--ink-2); }
@media (max-width: 480px) {
  .goal-list > li { grid-template-columns: minmax(0, 1fr) auto; }
  .goal-list .goal-meter { grid-column: 1 / -1; grid-row: 2; }
}
/* a connected household's goal on the Feed: no book, so no cover column — the line, the count and the bar */
.feed-card.feed-goal { grid-template-columns: minmax(0, 1fr); }
.feed-goal .goal-count { font-size: 14px; }
.feed-goal .progress-track { margin-bottom: 0.2rem; }
/* the goals switch waits on the names one: its checkbox and Save shown, but greyed and inert until names go to
   connections; the note saying why stays at full strength */
.switch-off > label, .switch-off > button { opacity: 0.5; }
/* ── want lists and purchase links (ARCH.md §16 #53) ─────────────────────────
   Each member's want list, and the household's pasted "where to buy" links. Existing
   tokens only: the want toggle is the indigo working accent — a wish is work in hand,
   not circulation, so no vermilion — and a purchase link is a quiet outbound row with
   its host in mono, the one part of an address a person checks before clicking. */
.want-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.75rem; margin: 0.2rem 0 0.4rem; }
/* the + or ✓ takes the button's own ink: in indigo it vanished into the filled button */
.want-toggle > span { font-family: var(--mono); color: inherit; }
button.want-toggle.on { background: var(--accent-tint); border-color: var(--accent); color: var(--accent-strong); }
button.want-toggle.on:hover { background: var(--accent-tint); border-color: var(--accent-strong); color: var(--accent-strong); }
.want-note { font-size: 12px; overflow-wrap: anywhere; }
a.want-note { text-decoration: underline; text-underline-offset: 2px; }

.buy-links { list-style: none; padding: 0; margin: 0.35rem 0 0; display: grid; gap: 0.3rem; }
.buy-links > li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.8rem; min-width: 0; }
.buy-link { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.15rem 0.5rem; min-width: 0; font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }
.buy-link small { font-size: 11px; font-weight: 400; color: var(--ink-3); }
.buy-link:hover small { color: var(--accent); }
.buy-links.editable > li { padding: 0.3rem 0; border-bottom: 1px solid var(--line); }
.buy-links.editable > li:first-child { border-top: 1px solid var(--line); }
.buy-form input[name='label'] { flex: 0 1 11rem; min-width: 0; }
.buy-form input[name='url'] { flex: 1 1 16rem; min-width: 0; }

/* a want list — the member's page inside the app, and a published gift list: a card a row, cover beside it */
.want-list { list-style: none; padding: 0; margin: 0 0 1rem; display: grid; gap: 0.6rem; max-width: 46rem; }
.want-card { display: grid; grid-template-columns: 56px minmax(0, 1fr); gap: 0.9rem; padding: 0.75rem 0.9rem; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); }
.want-cover { display: block; width: 56px; aspect-ratio: 2 / 3; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface-2); box-shadow: 0 0 0 1px var(--line); }
.want-cover .cover-fallback { font-size: 1.5rem; }
.want-body { min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.want-title { color: var(--ink); font-weight: 600; font-size: 14px; line-height: 1.3; overflow-wrap: anywhere; }
.want-title:hover { color: var(--accent); }
.want-creators { color: var(--ink-2); font-size: 12px; overflow-wrap: anywhere; }
.want-body .mline { display: flex; flex-wrap: wrap; gap: 0.15rem 0.5rem; align-items: center; margin-top: 0.15rem; }
.want-body .buy-links { margin-top: 0.4rem; }
.want-body form.inline { margin-top: 0.35rem; }
.gift-lists { margin-top: 2rem; max-width: 46rem; }
.gift-lists .data-table { margin-bottom: 0.75rem; }
.gift-lists > form { margin-bottom: 0.75rem; }
/* someone in the household wants it, and it isn't owned (§16 #53): the working accent, dotted — it pairs with the
   dashed ghost "Not owned" beside it, and is never a name */
.pill.wanted { border-color: var(--accent); border-style: dotted; color: var(--accent); }

/* ── year in review (ARCH.md §16 #59) ────────────────────────────────────────
   The member's year beside the household's, each column its own figures. Counts in the
   registrar's mono; the month bars in the indigo working accent, ratings in turmeric.
   The bars are for the eye only (aria-hidden): a table only screen readers see (.sr-only) carries them. */
.yr-picker label { margin: 0 0.15rem 0 0; font-size: 12.5px; }
.yr-intro { max-width: 46rem; font-size: 12.5px; }
.yr-empty { max-width: 40rem; margin-bottom: 2rem; }
.yr-empty p { margin: 0; }
.yr-section h3 { font-family: var(--mono); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; color: var(--ink-2); margin-bottom: 0.6rem; }
.yr-section h4 { margin: 1rem 0 0.35rem; }
.yr-section h3 + h4 { margin-top: 0; }
.yr-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem 2rem; }
.yr-pair.yr-solo { grid-template-columns: minmax(0, 1fr); max-width: 34rem; }
.yr-col { min-width: 0; }
.yr-col > .stat-row { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
.yr-list { list-style: none; counter-reset: yr; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.yr-list > li {
  counter-increment: yr;
  display: grid;
  grid-template-columns: 1.4rem minmax(0, 1fr) auto;
  gap: 0.6rem;
  align-items: baseline;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
}
.yr-list > li::before { content: counter(yr); font-family: var(--mono); font-size: 11px; color: var(--ink-3); }
.yr-name { min-width: 0; overflow-wrap: anywhere; }
.yr-list .tag.yr-name { justify-self: start; margin: 0; white-space: normal; } /* a long tag wraps inside its chip rather than widen a phone */
.yr-count { font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); white-space: nowrap; text-align: right; }
.yr-rating { white-space: nowrap; }
.yr-rating .mono { font-size: 11.5px; color: var(--ink-2); }
.yr-average { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.6rem; margin: 0; }
.yr-average .rating { font-size: 15px; }
.yr-average .yr-rating .mono { font-size: 15px; font-weight: 600; color: var(--ink); }
.yr-props { grid-template-columns: 110px minmax(0, 1fr); }
.yr-props dd a { overflow-wrap: anywhere; }
.yr-undated { font-size: 12.5px; }

/* the month chart: twelve columns, each a count over its bar and the month under it */
.yr-chart { position: relative; margin: 0.9rem 0 0; } /* holds its hidden table, so the table never sits past the page's edge */
.yr-chart-cap { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink-3); margin-bottom: 0.3rem; }
.yr-bars { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); }
.yr-month { display: flex; flex-direction: column; min-width: 0; }
.yr-n { font-family: var(--mono); font-size: 10px; line-height: 1.5; min-height: 1.5em; text-align: center; color: var(--ink-2); }
.yr-bar { display: flex; align-items: flex-end; height: 84px; padding: 0 2px; border-bottom: 1px solid var(--line-strong); }
.yr-fill { display: block; width: 100%; background: var(--accent); border-radius: 2px 2px 0 0; }
.yr-m { font-family: var(--mono); font-size: 9.5px; text-align: center; color: var(--ink-3); padding-top: 0.25rem; overflow: hidden; }

@media (max-width: 720px) {
  .yr-pair { grid-template-columns: minmax(0, 1fr); gap: 1.75rem; }
  .yr-props { grid-template-columns: 96px minmax(0, 1fr); }
}

/* ── money (ARCH.md §16 #61) ──────────────────────────────────────────────────
   What was paid is data, so it is set in the registrar's mono like every other figure —
   in ink, never gold (that's ratings) or vermilion (that's circulation). The currency code
   sits in a ruled cell at the head of the input, as a ledger's column heading would. */
.money, .money-cell { font-family: var(--mono); font-size: 12.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.money-field { margin-bottom: 0.9rem; }
.money-field > label { margin-bottom: 0; }
.money-input { display: flex; align-items: stretch; max-width: 18rem; margin-top: 0.3rem; }
.money-input input, .money-input select { margin: 0; }
.money-input input { font-family: var(--mono); min-width: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; }
.money-code, .money-input select {
  flex: none;
  display: inline-flex;
  align-items: center;
  width: auto;
  padding: 0 0.6rem;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.06em;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-right: none;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}
/* the shared rule's background shorthand clears the chevron: put it back */
.money-input select { padding-right: calc(1rem + 10px); background: var(--chevron) no-repeat right 0.6rem center / 10px 6px, var(--surface-2); }
.money-input input[aria-invalid='true'] { border-color: var(--stamp); }
.money-field .form-note { margin: 0.35rem 0 0; }
.field-error { color: var(--stamp); font-size: 12.5px; margin: 0.35rem 0 0; }
.paid-totals { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.35rem; margin: -0.4rem 0 1rem; font-size: 12.5px; }
.paid-totals .eyebrow { margin: 0 0.25rem 0 0; }
#currency select { width: auto; max-width: 100%; }

/* "More results" under a page of Add-page search results; the next page takes its place */
.results-more { margin: 0.25rem 0 1rem; }
.results-page:focus { outline: none; }

/* creators and publishers (ARCH.md §16 #72): the index's groups and its narrowing box */
.names-filter { margin: 0 0 1rem; }
.names-filter input[type='search'] { max-width: 20rem; }
.names-group + .names-group { margin-top: 1.5rem; }
/* a cover from the camera (ARCH.md §16 #73): the small form under an item's cover */
.cover-photo { margin-top: 0.75rem; display: grid; gap: 0.5rem; }
.cover-photo label { font-size: 12.5px; margin: 0; }
.cover-photo input[type='file'] { font-size: 12.5px; max-width: 100%; }
.cover-photo-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }

/* formats and editions (ARCH.md §16 #75) */
.pill.format { background: var(--surface-2); }
.format-pills { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; margin-left: 0.4rem; vertical-align: middle; }
/* each checkbox keeps 24px clear of its neighbours, wrapped onto a phone's width too (WCAG 2.2 target size) */
.formats-group { display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 0.5rem 0.75rem 0.65rem; margin: 0 0 0.75rem; }
.formats-group legend { font-size: 12.5px; padding: 0 0.3rem; }
.formats-group .inline-check { display: inline-flex; align-items: center; gap: 0.4rem; min-height: 24px; margin: 0; font-size: 13px; }
.editions-fields { margin: 0.5rem 0 1rem; }
.editions-rows { display: grid; gap: 0.5rem; }
.edition-row { display: grid; grid-template-columns: 9rem 1fr 1fr 5rem; gap: 0.5rem; align-items: end; }
.edition-row label { margin: 0; font-size: 12.5px; }
.edition-row input, .edition-row select { margin: 0; width: 100%; }
@media (max-width: 640px) { .edition-row { grid-template-columns: 1fr 1fr; } }
/* language (ARCH.md §16 #76) */
.pill.language { background: var(--surface-2); margin-left: 0.4rem; }
.original-title { margin: -0.25rem 0 0.35rem; font-style: italic; }
/* new from your authors (ARCH.md §16 #78) */
.authors-finished li form { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; width: 100%; }
.authors-finished li form .btn { margin-left: auto; }
/* a series' gaps from Open Library (ARCH.md §16 #79) */
.series-find { margin-top: 0.5rem; }
/* quotes and highlights (ARCH.md §16 #77) */
.quotes { list-style: none; padding: 0; margin: 0; }
.quotes .quote { padding: 0.65rem 0; border-top: 1px solid var(--line); }
.quotes .quote.shared { border-left: 3px solid var(--accent); padding-left: 0.6rem; }
.quote-text { margin: 0 0 0.3rem; padding: 0 0 0 0.75rem; border-left: 2px solid var(--line); font-style: italic; }
.quote-by { display: flex; flex-wrap: wrap; gap: 0.3rem 0.75rem; align-items: baseline; margin: 0 0 0.25rem; font-size: 12.5px; }
.quote-note { margin: 0.2rem 0 0.4rem; font-size: 13px; }
.quote-form { display: grid; gap: 0.4rem; max-width: 40rem; }
.quote-form .inline-check { display: inline-flex; align-items: center; gap: 0.4rem; min-height: 24px; font-size: 13px; }
.quote-add { margin-top: 0.5rem; }
.quote-book { display: flex; align-items: center; gap: 0.6rem; text-decoration: none; margin-bottom: 0.3rem; }
.quote-cover { width: 32px; flex: none; display: block; }
.quote-cover .cover-img, .quote-cover .cover-fallback { width: 32px; aspect-ratio: 2 / 3; object-fit: cover; border-radius: 2px; font-size: 1rem; display: flex; align-items: center; justify-content: center; background: var(--surface-2); }

/* saved views (ARCH.md §16 #81): the row of views above a shelf's filter bar, and under a shelf's name on the Overview */
.views { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.5rem; margin: 0 0 0.75rem; }
.views .eyebrow { margin: 0 0.25rem 0 0; }
.views .pill, .shelf-views .pill { display: inline-flex; align-items: center; min-height: 24px; text-decoration: none; }
.views .pill.active { border-color: var(--accent); color: var(--accent); }
.views .inline-form { margin-left: auto; }
.views .inline-form input { flex: 0 1 14rem; min-width: 0; }
.shelf-views { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.3rem; }

/* borrowed from someone not on Nalanda (ARCH.md §16 #82): circulation, so the stamp colour, like Lent */
.pill.borrowed { border-color: var(--stamp); color: var(--stamp); }
.borrowed-from { display: grid; gap: 0.5rem; }
.borrow-history { margin: 0; padding-left: 1.1rem; font-size: 13px; }
/* QR codes on Shared links (ARCH.md §16 #85): drawn at 512px, shown small and crisp; the download keeps it whole */
.share-qr { display: flex; align-items: flex-end; gap: 0.6rem; margin-top: 0.45rem; }
.share-qr img { width: 104px; height: 104px; image-rendering: pixelated; border: 1px solid var(--line); border-radius: 4px; background: var(--paper); }

/* item history (ARCH.md §16 #84): the admin's table under Circulation */
.item-history { margin-top: 1rem; }
.item-history .data-table { margin-top: 0.5rem; }
.history-value { max-width: 18rem; overflow-wrap: anywhere; font-size: 12.5px; }

/* API tokens (ARCH.md §16 #88): the list on the Account page, and the secret shown once */
.token-list { list-style: none; margin: 0 0 0.75rem; padding: 0; display: grid; gap: 0.5rem; }
.token-list li { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.token-secret { display: inline-block; padding: 0.35rem 0.5rem; font-size: 13px; user-select: all; }
