/* ── Reset & base ──────────────────────────────────────────── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/*
 * ── Design tokens ─────────────────────────────────────────────
 *
 * Canonical tokens (colors, font, type scale, spacing, radii) come from the CDN:
 *   https://cdn.neorgon.org/v1.0.0/styles/base.css   (linked in index.html)
 * Do NOT redeclare them here: a local copy is how the fleet drifted. The block
 * below holds only what base.css does not define, plus the per-site accent.
 *
 * Motion: base.css still ships --ease-snap (bounce) for legacy sites, but it is
 * deprecated for new work (impeccable law: ease-out exponential, no bounce).
 * Use --ease-out below.
 */

:root {
  /* Per-site identity: set to this site's hub card accent (PROJECTS.md section 4) */
  --accent: #b45309;
  --accent-bright: #d97706;

  /* Site extras base.css does not define */
  --surface-toast: #1a1730;
  --danger: #e11d48;
  --danger-hover: #f43f5e;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --control-height: 44px;
  --control-height-sm: 36px;
  --shadow-card: 0 12px 28px rgba(15, 23, 42, 0.5);
  --shadow-modal: 0 24px 48px rgba(2, 6, 23, 0.58);
  --z-sticky-header: 200;
  --z-modal: 400;
  --z-toast: 500;
}

/* Author display values (e.g. .section/.stack flex) would otherwise defeat the
   hidden attribute, so restore its authority once, globally. */
[hidden] { display: none !important; }

html { height: 100%; }
body {
  font-family: var(--font);
  background: var(--bg);
  min-height: 100%;
  color: var(--text-primary);
  display: flex; flex-direction: column;
}

/* The room. A warm wash from above where the lamps are, darkness pooling at
   the floor, and a vignette so the page has edges. Fixed, so it reads as a
   room the shelves stand in rather than a texture that scrolls with them.
   `--bg` itself is untouched: this is site styling over the fleet token. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(130% 62% at 50% -12%,
      color-mix(in srgb, var(--accent) 13%, transparent) 0%, transparent 58%),
    radial-gradient(100% 55% at 50% 108%, rgba(0, 0, 0, .62) 0%, transparent 66%),
    radial-gradient(140% 100% at 50% 50%, transparent 42%, rgba(0, 0, 0, .5) 100%);
}

body.modal-open {
  overflow: hidden;
}

main#main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  width: 100%;
}

/* ── Layout ────────────────────────────────────────────────── */
/*
 * GOTCHA: body { display:flex; flex-direction:column } + margin: 0 auto
 * on a child collapses its width, so always pair with width: 100%.
 *
 * GOTCHA: External images (YouTube ggpht, Gravatar, etc.) may block loading
 * when the Referer header points to localhost or an unknown origin. Fix:
 * 1. Add <meta name="referrer" content="no-referrer"> in <head> (global)
 * 2. Or add referrerpolicy="no-referrer" on each <img> tag
 */
.container {
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
  padding: var(--space-6);
  flex: 1;
}

/* ── Side-rail shell (opt-in) ──────────────────────────────────
 * A desktop window is far wider than a comfortable line of text, so a single
 * centred column leaves two large empty gutters and still runs the prose to
 * 100+ characters. This puts the section nav in the left gutter and narrows
 * the reading column, without changing .container for sites that do not use it.
 *
 *   <div class="shell">
 *     <nav class="shell__rail" aria-label="Section"> … </nav>
 *     <div class="shell__main"> … </div>
 *   </div>
 *
 * Below 940px it collapses to one column and the rail becomes a scrollable
 * row, so the same markup works on a phone with no JavaScript involved. From
 * 1240px up, the spare width goes to the right margin only, so the reading
 * column walks onto the page's centre line instead of sitting right of it.
 */
.shell {
  width: 100%;
  max-width: 1240px;
  margin: 0 auto;
  padding: var(--space-6) var(--space-6) 64px;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  align-content: start;
  flex: 1;
}
/* A 1fr track is minmax(auto, 1fr): a wide child (a board, a table, a long
   code line) sets min-content and stretches the column past the viewport. */
.shell > * { min-width: 0; }

.shell__rail {
  display: flex;
  gap: var(--space-1);
  padding: 4px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow-x: auto;
  scrollbar-width: none;
}
.shell__rail::-webkit-scrollbar { display: none; }

@media (min-width: 940px) {
  .shell {
    grid-template-columns: 208px minmax(0, 1fr);
    gap: var(--space-8);
    padding: var(--space-8) var(--space-8) 80px;
  }
  .shell__rail {
    position: sticky;
    top: 76px;          /* clears the app-mode header */
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: var(--space-2);
    overflow: visible;
    align-self: start;
  }
}

/* The rail sits in the left gutter, but with only two tracks it does not stay
   there: the rail plus its gap add to main's left side and nothing balances
   them on the right, so main sits half that distance right of the page's centre
   line (120px at a 208px rail). A column that far off centre reads as a
   misalignment rather than as a nav beside it.
   The correction is to stop splitting the spare width between both margins and
   spend all of it on the right. Main then walks back onto the centre line as the
   window grows, reaching it at 1240 + 208 + 32 = 1480px, and it costs main no
   width at any point on the way: the shell widens, main does not move relative
   to the rail. Below 1240px the shell already fills the viewport, so there is no
   spare width to spend and any correction would come straight out of the
   reading column. That trade is not worth making. */
@media (min-width: 1240px) {
  .shell {
    --shell-nudge: calc(208px + var(--space-8));
    max-width: calc(1240px + var(--shell-nudge));
    padding-right: clamp(
      var(--space-8),
      calc(var(--space-8) + 100vw - 1240px),
      calc(var(--space-8) + var(--shell-nudge))
    );
  }
}

/* Vertical rhythm: use inside .container or nested regions */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}
.stack--tight { gap: var(--space-4); }
/* Major sections need a real break. At 32px a heading sits almost on top of
   the block above it and the page reads as one undifferentiated column. */
.stack--loose { gap: 56px; }

/* Page section: title row + body (replaces “card everything” for grouping) */
.section {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}
.section__header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
}
.section__titles {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}
.section__title {
  font-size: var(--text-xl);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-primary);
}
.section__lead {
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.6;
  max-width: 64ch;   /* 65-75 characters is the readable measure */
}
.section__lead code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
  font-size: 0.92em;
  color: var(--text-secondary);
}
.stack > .card { margin-bottom: 0; }
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* ── Header ────────────────────────────────────────────────────
 * Owned by the Neorgon Header Kit (css/neorgon-header.css, canonical
 * source: packages/neorgon-ui/header/). Do NOT style .header-bar,
 * .header-logo*, .header-title-link, .header-subtitle, .header-right,
 * .header-actions, or .header-home here. Auth styles (.auth-*) stay
 * site-owned below.
 */
nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* ── Buttons (canonical) ─────────────────────────────────────
 * Use .btn + a variant. Aliases: .nav-link = toolbar ghost; .auth-* map below.
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-height);
  padding: 0 var(--space-4);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-primary);
  transition:
    background-color var(--dur),
    color var(--dur),
    border-color var(--dur),
    box-shadow var(--dur),
    transform var(--dur-fast);
  -webkit-tap-highlight-color: transparent;
}
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
.btn--sm {
  min-height: var(--control-height-sm);
  padding: 0 var(--space-3);
  font-size: var(--text-xs);
}
.btn--block { width: 100%; }
.btn--icon {
  min-width: var(--control-height);
  padding: 0;
}
.btn--primary {
  background: var(--accent);
  color: #fff;
  border-color: transparent;
}
.btn--primary:hover { background: var(--accent-bright); }
.btn--secondary {
  background: var(--surface-2);
  color: var(--text-primary);
  border-color: var(--border-strong);
}
.btn--secondary:hover {
  background: rgba(255, 255, 255, 0.1);
}
.btn--ghost {
  background: transparent;
  color: rgba(255,255,255,0.92);
  border-color: rgba(255,255,255,0.18);
}
.btn--ghost:hover {
  background: rgba(255,255,255,0.1);
  color: #fff;
}
.btn--danger {
  background: var(--danger);
  color: #fff;
  border-color: transparent;
}
.btn--danger:hover { background: var(--danger-hover); }

/* Toolbar ghost (header nav): same as .btn.btn--ghost.btn--sm */
.nav-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: var(--radius-sm);
  background: transparent;
  color: rgba(255,255,255,0.92);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  transition: background-color var(--dur), color var(--dur), border-color var(--dur);
}
.nav-link:hover {
  background: rgba(255,255,255,0.1);
  color: #fff;
}

.auth-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--control-height);
  min-height: var(--control-height);
  padding: 0;
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: var(--radius-sm);
  background: transparent;
  color: rgba(255,255,255,0.92);
  cursor: pointer;
  font-family: inherit;
  transition: background-color var(--dur), color var(--dur), border-color var(--dur);
}
.auth-toggle:hover {
  background: rgba(255,255,255,0.1);
  color: #fff;
}
.auth-toggle svg {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  display: block;
}
.auth-toggle.logged-in { color: var(--accent); }
.auth-toggle.logged-in svg circle:nth-of-type(2) {
  fill: currentColor;
  stroke: none;
}

/* ── Sheet (inline panel under header), not a modal ──────────
 * Use for auth, filters, compact forms. No backdrop; page scrolls.
 * For blocking overlays use .modal below.
 */
.auth-panel {
  background: rgba(255,255,255,0.02);
  border-bottom: 1px solid var(--border-subtle);
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease-out;
}
.auth-panel.open { max-height: 300px; }
.auth-panel-inner {
  padding: var(--space-6);
  max-width: 400px;
  margin: 0 auto;
}
.auth-tabs {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.auth-tab {
  flex: 1;
  min-height: var(--control-height-sm);
  padding: 0 var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  transition: background-color var(--dur), color var(--dur), border-color var(--dur);
}
.auth-tab.active {
  background: var(--surface-2);
  color: var(--text-primary);
  border-color: var(--border-strong);
}
.auth-form input {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-family: inherit;
}
.auth-submit {
  width: 100%;
  min-height: var(--control-height);
  padding: 0 var(--space-4);
  border: none;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: #fff;
  font-family: inherit;
  font-weight: 600;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background-color var(--dur);
}
.auth-submit:hover { background: var(--accent-bright); }
#authUser {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.auth-username {
  color: var(--text-primary);
  font-weight: 600;
}
.auth-logout {
  min-height: var(--control-height-sm);
  padding: 0 var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  transition: background-color var(--dur), color var(--dur), border-color var(--dur);
}
.auth-logout:hover {
  background: var(--surface-2);
  color: var(--text-primary);
}
.auth-divider {
  height: 1px;
  background: var(--border-subtle);
}

/* ── Modal (blocking overlay) ──────────────────────────────── */
.modal[hidden] {
  display: none !important;
}
.modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}
.modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(2, 4, 12, 0.85);
  -webkit-backdrop-filter: blur(10px); /* Safari still needs the prefix */
  backdrop-filter: blur(10px);
}
.modal__dialog {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: min(100%, 520px);
  max-height: min(90vh, 680px);
  overflow: hidden;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
}
/* For modals holding option grids or editors: narrow dialogs cramp them */
.modal__dialog--wide {
  width: min(100%, 760px);
}
.modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--border-subtle);
}
.modal__header h2 {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
}
.modal__body {
  padding: var(--space-6);
  overflow: auto;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.55;
}
.modal__body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
  font-size: 0.9em;
  color: var(--text-primary);
}
.modal__footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--border-subtle);
}

/* ── Card ──────────────────────────────────────────────────── */
/*
 * GOTCHA: backdrop-filter creates a stacking context, so dropdowns inside
 * a card can be clipped by a later card's stacking context. Fix by adding
 * position: relative; z-index: 10 to the card that owns the open dropdown.
 *
 * Default: static surface (no hover lift, avoids generic “dashboard demo”).
 * Use .card--interactive for raised hover + entrance motion.
 *
 * GOTCHA: a <button> or <a> used AS a card does not inherit body text color,
 * buttons default to canvas text (near-black), rendering invisible on the dark
 * bg. When a card is an interactive element, set color: var(--text-primary)
 * and font-family: var(--font) explicitly on it.
 */
.card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  margin-bottom: var(--space-6);
  backdrop-filter: blur(10px);
  transition: border-color var(--dur), box-shadow var(--dur);
}
.card:hover {
  border-color: var(--border-strong);
  box-shadow: 0 8px 20px rgba(15, 23, 42, 0.45);
}
.card--interactive {
  transition:
    transform var(--dur) ease-out,
    box-shadow var(--dur) ease-out,
    border-color var(--dur);
  animation: fadeIn 0.35s ease-out both;
}
.card--interactive:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-card);
  border-color: var(--border-strong);
}
.card--enter {
  animation: fadeIn 0.35s ease-out both;
}
.card--flat:hover {
  box-shadow: none;
}
/* Card with an action row, in an equal-height grid: pin the toolbar to the
   bottom so buttons align across cards whose text wraps differently. */
.card--actions {
  display: flex;
  flex-direction: column;
}
.card--actions > .toolbar:last-child {
  margin-top: auto;
}

/* ── Button press feedback ─────────────────────────────────── */
button:active,
.btn:active,
.nav-link:active,
.auth-toggle:active,
.auth-tab:active,
.auth-submit:active,
.auth-logout:active {
  transform: scale(0.98);
  transition-duration: var(--dur-fast);
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Skeleton loading ──────────────────────────────────────── */
@keyframes shimmer { to { background-position: -200% 0; } }
.skeleton {
  background: linear-gradient(90deg, var(--surface-1) 25%, var(--surface-2) 50%, var(--surface-1) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s ease-in-out infinite;
  border-radius: var(--radius);
}

/* ── Toast ─────────────────────────────────────────────────── */
.toast {
  position: fixed;
  bottom: var(--space-6);
  right: var(--space-6);
  z-index: var(--z-toast);
  max-width: min(360px, calc(100vw - var(--space-8)));
  background: var(--surface-toast);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  color: var(--text-primary);
  box-shadow: var(--shadow-card);
  pointer-events: none;
  transform: translateY(8px);
  opacity: 0;
  transition: opacity 0.2s ease-out, transform 0.2s ease-out;
}
.toast.visible {
  opacity: 1;
  transform: translateY(0);
}

/* ── Footer ────────────────────────────────────────────────────
   Owned by the Neorgon Footer Kit (css/neorgon-footer.css).
   Don't add .neo-footer rules here. Edit packages/neorgon-ui/footer/
   and re-run sync-footer.sh. */

/* ── Focus styles ──────────────────────────────────────────── */
:focus-visible {
  outline: 2px solid var(--accent-bright);
  outline-offset: 2px;
}

/* ── Skip link ─────────────────────────────────────────────── */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--space-4);
  z-index: 1000;
  padding: var(--space-2) var(--space-4);
  background: var(--accent-bright);
  color: #000;
  font-weight: 600;
  font-size: var(--text-sm);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  text-decoration: none;
  transition: top var(--dur);
}
.skip-link:focus { top: 0; }

/* ── Reduced motion ────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .card--interactive:hover { transform: none; }
}

/* ── Responsive ────────────────────────────────────────────── */
@media (max-width: 600px) {
  .container { padding: var(--space-4); }
  .toast {
    left: var(--space-4);
    right: var(--space-4);
    max-width: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Vitrina
   A lit case with books standing in it. The spines are the only saturated
   thing on the page; every other surface stays out of their way.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --case-wood: #2a1a10;
  --case-wood-lit: #4a2f1c;
  --case-board: 12px;
  --case-gap: 1px;          /* books touch, but stay separate objects */
  --drawer-w: 460px;
}

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

main#main { padding-bottom: var(--space-8); }

/* ── Controls ─────────────────────────────────────────────────────────────── */

.controls {
  position: sticky;
  top: var(--header-h-cur, 56px);
  z-index: 40;
  transition: top .26s var(--header-ease, ease);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border-subtle);
}
:root:has(.header-bar.header-hidden) .controls { top: 0; }

.controls__inner {
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-3) var(--space-6);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.viewswitch {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  background: var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
}
.viewswitch__btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--text-sm);
  padding: 6px 14px;
  border-radius: 7px;
  cursor: pointer;
  transition: color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.viewswitch__btn:hover { color: var(--text-primary); }
.viewswitch__btn.is-on {
  background: var(--surface-2);
  color: var(--text-primary);
  box-shadow: inset 0 0 0 1px var(--border);
}

.field {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-muted);
}
.field__label { white-space: nowrap; }
.field select,
.field input[type="search"] {
  appearance: none;
  background: var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--text-sm);
  padding: 7px 10px;
  height: var(--control-height-sm);
}
.field select { padding-right: 26px; cursor: pointer; }
.field--search { position: relative; flex: 1 1 220px; max-width: 340px; }
.field--search input { width: 100%; padding-left: 32px; }
.field__icon {
  position: absolute;
  left: 10px;
  color: var(--text-muted);
  pointer-events: none;
}

.check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}
.check input { accent-color: var(--accent); width: 15px; height: 15px; cursor: pointer; }
.check:hover { color: var(--text-secondary); }

.controls__count {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.stage {
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-8) var(--space-6) 0;
  /* `main` is a flex column, so this is a flex item, and `margin: 0 auto`
     disables cross-axis stretch. Without a definite width the browser then
     sizes it to max-content and clamps to max-width, so the 13,900px row of
     Nova spines in the whole-collection view pushed the stage to a flat 1400px
     and scrolled the whole page sideways on any narrower viewport. The row
     scrolls inside its own case; the stage must never grow to fit it. */
  width: 100%;
  min-width: 0;
}

/* ── A shelf row ──────────────────────────────────────────────────────────── */

.shelfrow { margin-bottom: 52px;
  /* The whole-collection view puts 564 spines across five rows into the
     document, and Nova alone is 365 of them in a 13,900px strip. Without this
     the browser lays out and paints every one of them at first load, including
     the ~90% that are outside their row's own horizontal scroll. Gating on
     visibility lets it skip the rows nobody is looking at; the intrinsic size
     keeps the page's scroll height honest so the scrollbar does not jump as
     rows come into view. */
  content-visibility: auto;
  contain-intrinsic-size: auto 420px;
}

.shelfrow__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.shelfrow__label {
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--text-secondary);
}
.shelfrow__count {
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  padding: 2px 8px;
  border: 1px solid var(--border-subtle);
  border-radius: 999px;
}

/* The case: a lit back panel, a board below, books standing between.
   The light and the panel sit on .shelfrow, which does not scroll, so a
   365-volume row moves under a lamp that stays where it is. */
.shelfrow__case {
  position: relative;
  padding-top: 26px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .16) transparent;
}
.shelfrow__case::-webkit-scrollbar { height: 8px; }
.shelfrow__case::-webkit-scrollbar-track { background: transparent; }
.shelfrow__case::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, .14);
  border-radius: 99px;
}
.shelfrow__case:hover::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .26); }

.shelfrow__case-wrap {
  position: relative;
  border-radius: var(--radius-sm) var(--radius-sm) 2px 2px;
}
/* The inside of the case: a recessed back panel the books stand against. */
.shelfrow__case-wrap::before {
  content: '';
  position: absolute;
  inset: 0 0 calc(var(--case-board) + 8px) 0;
  border-radius: inherit;
  background:
    radial-gradient(120% 90% at 50% 0%,
      color-mix(in srgb, var(--accent) 11%, transparent) 0%, transparent 60%),
    linear-gradient(180deg, rgba(0, 0, 0, .55) 0%, rgba(0, 0, 0, .22) 30%, rgba(0, 0, 0, .48) 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
  pointer-events: none;
  z-index: 0;
}

.shelfrow__books {
  display: flex;
  align-items: flex-end;
  gap: var(--case-gap);
  min-height: 120px;
  padding: 0 2px;
  width: max-content;
  min-width: 100%;
}

.shelfrow__board {
  height: var(--case-board);
  border-radius: 2px;
  background:
    linear-gradient(180deg,
      var(--case-wood-lit) 0%,
      var(--case-wood) 42%,
      #150c07 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, .07) inset,
    0 10px 22px -8px rgba(0, 0, 0, .8);
  min-width: 100%;
  width: max-content;
}

/* ── A spine ──────────────────────────────────────────────────────────────── */

.spine {
  appearance: none;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  position: relative;
  flex: 0 0 auto;
  height: var(--h, 285px);
  width: var(--w, var(--est, 24px));
  border-radius: 1px 1px 0 0;
  transform: translateY(0);
  transition:
    transform 260ms var(--ease-out),
    filter 260ms var(--ease-out),
    box-shadow 260ms var(--ease-out);
  filter: brightness(.92) saturate(.96);
  box-shadow:
    1px 0 2px rgba(0, 0, 0, .55),
    0 6px 10px -6px rgba(0, 0, 0, .9);
}
.spine__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: fill;
  border-radius: inherit;
}
.spine:hover,
.spine:focus-visible {
  transform: translateY(-12px);
  filter: brightness(1.12) saturate(1.05);
  z-index: 3;
  box-shadow:
    1px 0 2px rgba(0, 0, 0, .55),
    0 16px 18px -10px rgba(0, 0, 0, .95);
}
.spine.is-open {
  transform: translateY(-24px);
  filter: brightness(1.18) saturate(1.05);
  z-index: 4;
  box-shadow:
    0 0 0 2px var(--accent),
    0 14px 26px -10px rgba(0, 0, 0, .9);
}

/* A volume in the collection that is not on the shelf. Dimmed rather than
   hidden: the point of the whole-collection view is seeing the holes, and the
   art still has to be legible enough to recognise in a second-hand shop. */
.spine--ghost {
  filter: grayscale(.85) brightness(.42) contrast(.9);
  opacity: .55;
}
.spine--ghost::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px dashed rgba(255, 255, 255, .18);
  border-bottom: 0;
  pointer-events: none;
}
.spine--ghost:hover,
.spine--ghost:focus-visible {
  filter: grayscale(0) brightness(1.05);
  opacity: 1;
  transform: translateY(-12px);
}
.spine--ghost:hover::after,
.spine--ghost:focus-visible::after { border-color: var(--accent); border-style: solid; }

.shelfrow__case[data-scroll="start"] {
  mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 56px), transparent 100%);
}
.shelfrow__case[data-scroll="middle"] {
  mask-image: linear-gradient(to right, transparent 0, #000 56px, #000 calc(100% - 56px), transparent 100%);
}
.shelfrow__case[data-scroll="end"] {
  mask-image: linear-gradient(to right, transparent 0, #000 56px, #000 100%);
}

.shelfrow__hint {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-left: auto;
}
.shelfrow--run .shelfrow__head { gap: var(--space-3); }

/* A book the catalogue has no spine scan for. Deliberately flat and lettered,
   so it never passes for the real thing. */
.spine__drawn { display: none; }
.spine--drawn {
  width: var(--est, 26px);
  background:
    linear-gradient(90deg, rgba(0,0,0,.45) 0%, rgba(255,255,255,.05) 22%, rgba(0,0,0,.35) 100%),
    linear-gradient(180deg,
      hsl(var(--hue, 30) 26% 26%) 0%,
      hsl(var(--hue, 30) 24% 18%) 100%);
  box-shadow: 1px 0 2px rgba(0,0,0,.55), inset 0 0 0 1px rgba(255,255,255,.06);
}
.spine--drawn .spine__drawn {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  height: 100%;
  width: 100%;
  padding: 8px 0;
}
.spine__drawn-title {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-size: 10px;
  line-height: 1;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .92);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: calc(100% - 22px);
}
.spine__drawn-num {
  font-size: 8px;
  color: rgba(255, 255, 255, .82);
  font-variant-numeric: tabular-nums;
}

/* ── Covers view ──────────────────────────────────────────────────────────── */

.coverset { margin-bottom: 48px; }

.covergrid,
.browsegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: var(--space-6) var(--space-4);
}

.cover {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 6px;
  color: inherit;
}
.cover__frame {
  position: relative;
  display: block;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-1);
  box-shadow: 0 8px 18px -10px rgba(0, 0, 0, .85);
  transition: transform 240ms var(--ease-out), box-shadow 240ms var(--ease-out);
}
.cover__frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cover:hover .cover__frame,
.cover:focus-visible .cover__frame {
  transform: translateY(-4px);
  box-shadow: 0 16px 28px -12px rgba(0, 0, 0, .9);
}
.cover.is-open .cover__frame { box-shadow: 0 0 0 2px var(--accent); }
.cover__none,
.detail__cover--none {
  display: grid;
  place-items: center;
  height: 100%;
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.cover__flag {
  position: absolute;
  left: 6px;
  bottom: 6px;
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .72);
  color: rgba(255, 255, 255, .7);
}
.cover__title {
  font-size: var(--text-sm);
  line-height: 1.3;
  color: var(--text-primary);
}
.cover__meta { font-size: var(--text-xs); color: var(--text-muted); }

/* ── Browse view ──────────────────────────────────────────────────────────── */

.bcard {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.bcard__art {
  position: relative;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-1);
}
.bcard__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bcard.is-own .bcard__art { outline: 2px solid var(--accent); outline-offset: -2px; }
.bcard.is-own img { opacity: .45; }
.bcard__own {
  position: absolute;
  inset: auto 0 0 0;
  background: var(--accent);
  color: #fff;
  font-size: 10px;
  text-align: center;
  padding: 3px;
  letter-spacing: .03em;
}
.bcard__title { font-size: var(--text-sm); font-weight: 500; line-height: 1.3; }
.bcard__by,
.bcard__meta { font-size: var(--text-xs); color: var(--text-muted); }
.bcard__add { margin-top: 4px; align-self: flex-start; }

.more { display: grid; place-items: center; padding: var(--space-8) 0; }

.gaps {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  background: var(--surface-1);
  padding: var(--space-4) var(--space-6);
  margin-bottom: var(--space-8);
}
.gaps__title { font-size: var(--text-base); margin-bottom: 2px; }
.gaps__lead { font-size: var(--text-xs); color: var(--text-muted); margin-bottom: var(--space-3); }
.gaps__list { list-style: none; display: grid; gap: var(--space-3); }
.gaps__list li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--text-sm); }
.gaps__have { font-size: var(--text-xs); color: var(--text-muted); }
.gaps__missing { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  appearance: none;
  font: inherit;
  font-size: var(--text-xs);
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px dashed var(--border-strong);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.chip:hover { border-color: var(--accent); border-style: solid; color: var(--text-primary); }

/* ── Empty state and provenance ───────────────────────────────────────────── */

.empty { text-align: center; padding: 80px var(--space-6); }
.empty__lead { font-size: var(--text-lg); color: var(--text-secondary); margin-bottom: var(--space-2); }
.empty__hint { font-size: var(--text-sm); color: var(--text-muted); }

.provenance {
  max-width: 1400px;
  width: 100%;
  margin: var(--space-8) auto 0;
  padding: 0 var(--space-6);
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.6;
}
.provenance a { color: var(--text-secondary); }

/* ── Detail drawer ────────────────────────────────────────────────────────── */

.drawer[hidden] { display: none; }
.drawer { position: fixed; inset: 0; z-index: 300; }
.drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(2, 4, 10, .62);
  opacity: 0;
  transition: opacity 220ms var(--ease-out);
}
.drawer.is-open .drawer__scrim { opacity: 1; }
.drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(var(--drawer-w), 100vw);
  background: #0a0d1a;
  border-left: 1px solid var(--border);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateX(100%);
  transition: transform 260ms var(--ease-out);
  box-shadow: -24px 0 48px -24px rgba(0, 0, 0, .9);
}
.drawer.is-open .drawer__panel { transform: translateX(0); }
.drawer__close { position: absolute; top: var(--space-3); right: var(--space-3); z-index: 2; }

.detail { padding: var(--space-8) var(--space-6) 56px; }
.detail__art {
  display: flex;
  align-items: flex-end;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.detail__cover {
  width: 168px;
  border-radius: var(--radius-sm);
  box-shadow: 0 18px 34px -16px rgba(0, 0, 0, .95);
  background: var(--surface-1);
}
.detail__cover--none { width: 168px; aspect-ratio: 2/3; }
.detail__spine { height: 232px; width: auto; border-radius: 1px; box-shadow: 0 10px 22px -12px rgba(0,0,0,.9); }
.detail__title { font-size: var(--text-2xl); line-height: 1.2; }
.detail__by { color: var(--text-secondary); font-size: var(--text-sm); margin-top: 4px; }
.detail__note {
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-left: 2px solid var(--accent);
  background: var(--surface-1);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.detail__warn { margin-top: var(--space-4); font-size: var(--text-xs); color: var(--text-muted); }
.detail__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-6); }

.facts { margin-top: var(--space-6); display: grid; gap: 7px; }
.fact { display: grid; grid-template-columns: 116px 1fr; gap: var(--space-3); font-size: var(--text-sm); }
.fact dt { color: var(--text-muted); font-size: var(--text-xs); padding-top: 2px; }
.fact dd { color: var(--text-secondary); }

.contents { margin-top: var(--space-4); font-size: var(--text-sm); }
.contents summary { cursor: pointer; color: var(--text-muted); font-size: var(--text-xs); padding: 4px 0; }
.contents summary:hover { color: var(--text-secondary); }
.contents__list { margin: var(--space-3) 0 0 0; padding-left: var(--space-4); display: grid; gap: 5px; color: var(--text-secondary); }
.contents__pp { color: var(--text-muted); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }

/* ── Dialog internals ─────────────────────────────────────────────────────── */

.dialog__lead { font-size: var(--text-sm); color: var(--text-muted); margin-bottom: var(--space-4); line-height: 1.6; }
.form { display: grid; gap: var(--space-3); }
.form__pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.form__row { display: grid; gap: 4px; font-size: var(--text-xs); color: var(--text-muted); }
.form__row input,
.form__area {
  background: var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--text-sm);
  padding: 9px 11px;
  width: 100%;
}
.form__area { resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--text-xs); margin-top: var(--space-3); }
.form__file { font-size: var(--text-sm); color: var(--text-muted); }

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--space-4); }
.stat dt { font-size: var(--text-xs); color: var(--text-muted); }
.stat dd { font-size: var(--text-xl); color: var(--text-primary); margin-top: 2px; }
.stats__cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-6); margin-top: var(--space-8); }
.stats__cols h3 { font-size: var(--text-sm); color: var(--text-secondary); margin-bottom: var(--space-2); }
.stats__list { list-style: none; display: grid; gap: 4px; font-size: var(--text-sm); }
.stats__list li { display: flex; justify-content: space-between; gap: var(--space-3); color: var(--text-secondary); }
.stats__list b { color: var(--text-muted); font-variant-numeric: tabular-nums; font-weight: 500; }

.check--block {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  margin-top: var(--space-8);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
  line-height: 1.5;
}
.check--block input { margin-top: 2px; flex: 0 0 auto; }
.check--block kbd {
  font: inherit;
  font-size: var(--text-xs);
  padding: 1px 5px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface-1);
}

.toast--bad { border-color: var(--danger); }

/* ── Responsive ───────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
  .controls__inner { padding: var(--space-3) var(--space-4); gap: var(--space-2); }
  .stage { padding: var(--space-6) var(--space-4) 0; }
  .provenance { padding: 0 var(--space-4); }
  .controls__count { order: -1; margin-left: 0; width: 100%; }
  .field--search { max-width: none; }
  .detail { padding: var(--space-6) var(--space-4) 48px; }
  .form__pair { grid-template-columns: 1fr; }
  .fact { grid-template-columns: 96px 1fr; }
}

@media (max-width: 600px) {
  .covergrid, .browsegrid { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); }
  .detail__art { gap: var(--space-3); }
  .detail__cover, .detail__cover--none { width: 132px; }
  .detail__spine { height: 184px; }
  .drawer__panel { top: auto; bottom: 0; height: 88vh; width: 100%; border-left: 0; border-top: 1px solid var(--border); border-radius: var(--radius-lg) var(--radius-lg) 0 0; transform: translateY(100%); }
  .drawer.is-open .drawer__panel { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .spine:hover, .spine:focus-visible, .spine.is-open { transform: none; }
  .cover:hover .cover__frame, .cover:focus-visible .cover__frame { transform: none; }
}
