/* ==========================================================================
   The Humble Library — Design system
   Literary, paper-forward. Source Serif 4 (display) + Inter (body) +
   JetBrains Mono (metadata). Day/night with auto.
   ========================================================================== */

/* -- Tokens (day = default) ------------------------------------------------ */
:root,
[data-theme="day"] {
  --ob-bg:           #f0e6d2;
  --ob-surface:      #faf3e3;
  --ob-surface-2:    #ece2cf;
  --ob-overlay:      rgba(14, 14, 12, 0.55);

  --ob-ink:          #0e0e0c;
  --ob-ink-muted:    rgba(14, 14, 12, 0.55);
  --ob-ink-subtle:   rgba(14, 14, 12, 0.35);
  --ob-ink-inverse:  #f0e6d2;

  --ob-rule:         rgba(14, 14, 12, 0.12);
  --ob-rule-strong:  rgba(14, 14, 12, 0.22);

  --ob-accent:       #c9a961;
  --ob-accent-ink:   #0e0e0c;

  --ob-success:      #3d5a3d;
  --ob-warning:      #c9a961;
  --ob-danger:       #8b3a2e;
  --ob-info:         #2a2a55;

  --ob-btn-bg:       #0e0e0c;
  --ob-btn-fg:       #f0e6d2;

  --ob-dot-shelf:    #5a7855;
  --ob-dot-reading:  #c9a961;
  --ob-dot-lent:     #8b3a2e;

  --ob-shadow-sm:    0 1px 0 rgba(0,0,0,0.06), 0 4px 10px -4px rgba(0,0,0,0.18);
  --ob-shadow-md:    0 1px 0 rgba(0,0,0,0.08), 0 8px 18px -10px rgba(0,0,0,0.35);
  --ob-shadow-lg:    0 1px 0 rgba(0,0,0,0.10), 0 30px 60px -20px rgba(0,0,0,0.45);

  --ob-font-display: "Source Serif 4", "Iowan Old Style", Georgia, serif;
  --ob-font-body:    Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --ob-font-mono:    "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --ob-radius-sm: 6px;
  --ob-radius-md: 10px;
  --ob-radius-lg: 14px;
  --ob-radius-xl: 22px;

  color-scheme: light;
}

[data-theme="night"] {
  --ob-bg:           #0e0e0c;
  --ob-surface:      #1a1a17;
  --ob-surface-2:    #221d16;
  --ob-overlay:      rgba(0, 0, 0, 0.65);

  --ob-ink:          #f0e6d2;
  --ob-ink-muted:    rgba(244, 237, 227, 0.55);
  --ob-ink-subtle:   rgba(244, 237, 227, 0.35);
  --ob-ink-inverse:  #0e0e0c;

  --ob-rule:         rgba(244, 237, 227, 0.12);
  --ob-rule-strong:  rgba(244, 237, 227, 0.22);

  --ob-accent:       #c9a961;
  --ob-accent-ink:   #0e0e0c;

  --ob-success:      #5a7855;
  --ob-warning:      #c9a961;
  --ob-danger:       #b8513a;
  --ob-info:         #6a6ab0;

  --ob-btn-bg:       #f0e6d2;
  --ob-btn-fg:       #0e0e0c;

  --ob-dot-shelf:    #5a7855;
  --ob-dot-reading:  #c9a961;
  --ob-dot-lent:     #b8513a;

  --ob-shadow-sm:    0 1px 0 rgba(255,255,255,0.04) inset, 0 4px 10px -4px rgba(0,0,0,0.5);
  --ob-shadow-md:    0 1px 0 rgba(255,255,255,0.04) inset, 0 8px 18px -10px rgba(0,0,0,0.6);
  --ob-shadow-lg:    0 1px 0 rgba(255,255,255,0.05) inset, 0 30px 60px -20px rgba(0,0,0,0.7);

  color-scheme: dark;
}

/* -- Base -------------------------------------------------------------------*/
* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

/* Top-of-page navigation progress bar. Injected + driven by the script in
   base.html — fires on every same-origin link click and form submit so the
   user gets instant visual confirmation that their click was registered,
   even when the server is slow. The bar grows from 0 to ~88% over 14
   seconds (slowing curve), and is destroyed when the new page paints —
   so completion is implicit. base.html also inlines a fallback copy of
   these rules so the bar works even before this stylesheet has loaded. */
#nav-progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: linear-gradient(90deg,
                              var(--ob-accent),
                              color-mix(in srgb, var(--ob-accent) 40%, transparent));
  /* color-mix fallback: older Safari/Firefox just see the solid accent at
     the trailing edge, which still reads as a progress bar. */
  transform: scaleX(0);
  transform-origin: left;
  z-index: 99999;
  pointer-events: none;
  opacity: 0;
  transition: transform 800ms cubic-bezier(0.1, 0.9, 0.2, 1),
              opacity 200ms;
}
#nav-progress.is-active {
  opacity: 1;
  transform: scaleX(0.88);
  transition: transform 14s cubic-bezier(0.05, 0.7, 0.3, 0.99),
              opacity 120ms;
}
@media (prefers-reduced-motion: reduce) {
  /* Skip the slow grow animation — just show a static partial bar so the
     visual cue still works without movement. */
  #nav-progress {
    transition: opacity 120ms;
  }
  #nav-progress.is-active {
    transform: scaleX(0.6);
    transition: opacity 120ms;
  }
}
body {
  font-family: var(--ob-font-body);
  background: var(--ob-bg);
  color: var(--ob-ink);
  font-size: 14px;
  line-height: 1.5;
  min-height: 100vh;
  padding-bottom: 88px;
  position: relative;
}

a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--ob-rule); }
a:hover { border-bottom-color: var(--ob-ink-muted); }
a.bare, .bare a { border-bottom: 0; }

img { max-width: 100%; display: block; }
button { font-family: inherit; cursor: pointer; }
input, textarea, select { font-family: inherit; font-size: 14px; }

body::before {
  content: '';
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: 0.5;
  mix-blend-mode: multiply;
  background-image: radial-gradient(rgba(14,14,12,0.06) 1px, transparent 1px);
  background-size: 4px 4px;
}
[data-theme="night"] body::before {
  background-image: radial-gradient(rgba(244,237,227,0.05) 1px, transparent 1px);
  mix-blend-mode: screen;
}

.topbar, .container, .footer, .bottombar, .platform-announcement,
.impersonation-banner, .banner-warn, .flash-stack,
.tour-overlay, main { position: relative; z-index: 1; }

.skip-link {
  position: absolute; top: -40px; left: 4px;
  background: var(--ob-ink); color: var(--ob-bg);
  padding: 8px 12px; border-radius: var(--ob-radius-sm);
  z-index: 1000; font-family: var(--ob-font-mono); font-size: 11px;
  letter-spacing: 0.08em; text-transform: uppercase; border-bottom: 0;
}
.skip-link:focus { top: 4px; color: var(--ob-bg); }

*:focus-visible { outline: 1.5px solid var(--ob-accent); outline-offset: 2px; }

/* -- Typography ------------------------------------------------------------*/
h1, h2, h3, h4, h5 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.05;
}
h1 { font-size: 38px; }
h2 { font-size: 26px; }
h3 { font-size: 20px; line-height: 1.1; letter-spacing: -0.015em; }
.display { font-size: 48px; line-height: 1.0; letter-spacing: -0.025em; font-family: var(--ob-font-display); font-weight: 600; }
.muted { color: var(--ob-ink-muted); }
.muted.small, .small { font-size: 12px; }

@media (max-width: 600px) {
  h1 { font-size: 30px; }
  h2 { font-size: 22px; }
  h3 { font-size: 18px; }
  .display { font-size: 38px; }
}

.meta {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
}
.meta-lg { font-size: 11px; letter-spacing: 0.08em; }

.quote, blockquote, .italic, .friend-note {
  font-family: var(--ob-font-display);
  font-style: italic;
  font-size: 17px;
  line-height: 1.5;
  text-wrap: pretty;
}

.brand-dot {
  color: var(--ob-accent);
  /* Pull the period tight against the preceding letter — italic bold glyphs
     in serif fonts have left sidebearing that visually detaches the dot. */
  margin-left: -0.08em;
}
.tabular { font-variant-numeric: tabular-nums; }

/* -- Top bar ---------------------------------------------------------------*/
.topbar {
  background: var(--ob-bg);
  color: var(--ob-ink);
  padding: 18px 28px 14px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid var(--ob-rule);
}
.brand {
  display: inline-flex; align-items: baseline;
  color: var(--ob-ink);
  font-family: var(--ob-font-display); font-weight: 600; font-size: 22px;
  letter-spacing: -0.02em;
  border-bottom: 0;
}
.brand:hover { color: var(--ob-ink); }
.brand-svg { display: none; }

.topnav { display: flex; gap: 14px; align-items: center; }
.navlink {
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-mono);
  font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  padding: 6px 8px; border-bottom: 0;
}
.navlink:hover { color: var(--ob-ink); border-bottom: 0; }
.navlink.active, .navlink.primary { color: var(--ob-ink); }
.navlink.primary {
  background: var(--ob-btn-bg); color: var(--ob-btn-fg);
  padding: 8px 14px; border-radius: var(--ob-radius-sm);
}
.navlink-admin {
  border: 0.5px solid var(--ob-rule-strong); padding: 6px 10px;
  border-radius: var(--ob-radius-sm); display: inline-flex; align-items: center; gap: 4px;
}
.navlink .badge {
  background: var(--ob-danger); color: var(--ob-ink-inverse);
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 999px;
  margin-left: 4px; vertical-align: top;
}
.avatar-link img,
.avatar-link .avatar-placeholder {
  width: 28px; height: 28px; border-radius: 50%; object-fit: cover;
  border: 0.5px solid var(--ob-rule);
}
.avatar-placeholder {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ob-surface-2); color: var(--ob-ink);
  font-family: var(--ob-font-mono); font-weight: 600; font-size: 11px;
  letter-spacing: 0.04em;
}

/* -- Bottom tab bar (mobile) -----------------------------------------------*/
.bottombar {
  position: fixed;
  bottom: 14px; left: 14px; right: 14px;
  background: rgba(250, 243, 227, 0.85);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-xl);
  display: flex;
  z-index: 40;
  box-shadow: var(--ob-shadow-md);
}
[data-theme="night"] .bottombar { background: rgba(26, 26, 23, 0.85); }
.bottombar a {
  flex: 1; text-align: center; padding: 14px 6px;
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-mono);
  font-size: 10px; letter-spacing: 0.10em; text-transform: uppercase;
  border-bottom: 0; position: relative;
}
.bottombar a:hover { color: var(--ob-ink); }
.bottombar a.active { color: var(--ob-accent); }
.bottombar a.active::after {
  content: ''; position: absolute;
  bottom: 6px; left: 50%; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: var(--ob-accent);
}
.bottombar .dot {
  display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: var(--ob-danger); margin-left: 3px; vertical-align: top;
}
@media (min-width: 768px) {
  .bottombar { display: none; }
  body { padding-bottom: 0; }
}

/* -- Container -------------------------------------------------------------*/
.container { max-width: 760px; margin: 0 auto; padding: 24px 20px 32px; }
@media (min-width: 768px) { .container { padding: 32px 28px 56px; } }

/* -- Buttons ---------------------------------------------------------------*/
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 10px 18px;
  border-radius: var(--ob-radius-sm);
  font-family: var(--ob-font-mono);
  font-size: 12px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase;
  border: 0; cursor: pointer; border-bottom: 0;
  background: var(--ob-surface);
  color: var(--ob-ink);
  transition: all .15s ease;
}
.btn:hover { color: var(--ob-ink); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn-primary {
  background: var(--ob-btn-bg); color: var(--ob-btn-fg);
  box-shadow: var(--ob-shadow-sm);
}
.btn-primary:hover { color: var(--ob-btn-fg); }

.btn-secondary, .btn-ghost {
  background: transparent;
  color: var(--ob-ink);
  border: 0.5px solid var(--ob-rule-strong);
}
.btn-ghost { border: 0; color: var(--ob-ink-muted); }
.btn-ghost:hover { color: var(--ob-ink); }

.btn-danger { background: var(--ob-danger); color: var(--ob-ink-inverse); }
.btn-danger:hover { color: var(--ob-ink-inverse); }
.btn-accent { background: var(--ob-accent); color: var(--ob-accent-ink); }

.btn-sm { padding: 6px 12px; font-size: 11px; }
.btn-lg { padding: 13px 24px; font-size: 12px; }
.btn-wide { width: 100%; }

/* Compact square icon-button for tightly packed tables (e.g. admin
   user list per-row Suspend / Delete). The full action stays in the
   `title` attr for hover; aria-label keeps screen-readers happy.
   Inherits .btn-* colour variants (.btn-danger, .btn-primary, etc.) */
.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  font-size: 14px;
  line-height: 1;
  border-radius: var(--ob-radius-sm);
  border: 0.5px solid var(--ob-rule);
  background: var(--ob-surface);
  color: var(--ob-ink);
  text-decoration: none;
  cursor: pointer;
  font-family: inherit;
  flex-shrink: 0;
}
.btn-icon:hover { border-color: var(--ob-rule-strong); color: var(--ob-ink); }
.btn-icon[disabled] { opacity: 0.4; cursor: not-allowed; }

/* -- Status pills ----------------------------------------------------------*/
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px;
  border: 0.5px solid var(--ob-rule);
  border-radius: 999px;
  font-family: var(--ob-font-mono);
  font-size: 10px; font-weight: 500;
  letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--ob-ink);
  background: transparent;
}
.pill::before {
  content: ''; display: inline-block;
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}
.pill-shelf, .pill-on-shelf { color: var(--ob-success); }
.pill-shelf::before, .pill-on-shelf::before { background: var(--ob-dot-shelf); }
.pill-reading { color: var(--ob-warning); }
.pill-reading::before { background: var(--ob-dot-reading); }
.pill-lent { color: var(--ob-danger); }
.pill-lent::before { background: var(--ob-dot-lent); }
.pill-friends { color: var(--ob-success); }
.pill-friends::before { background: var(--ob-dot-shelf); }
.pill-danger { color: var(--ob-danger); }
.pill-danger::before { background: var(--ob-dot-lent); }
.pill-vis-public { color: var(--ob-success); }
.pill-vis-public::before { background: var(--ob-dot-shelf); }
.pill-vis-friends { color: var(--ob-warning); }
.pill-vis-friends::before { background: var(--ob-dot-reading); }
.pill-vis-private { color: var(--ob-info); }
.pill-vis-private::before { background: var(--ob-info); }
.pill-internal { color: var(--ob-success); }
.pill-internal::before { background: var(--ob-dot-shelf); }
.pill-street { color: var(--ob-warning); }
.pill-street::before { background: var(--ob-dot-reading); }
.pill-private { color: var(--ob-info); }
.pill-private::before { background: var(--ob-info); }
.pill-status-pending { color: var(--ob-warning); }
.pill-status-pending::before { background: var(--ob-warning); }
.pill-status-approved { color: var(--ob-success); }
.pill-status-approved::before { background: var(--ob-success); }
.pill-status-active { color: var(--ob-info); }
.pill-status-active::before { background: var(--ob-info); }
.pill-status-returned { color: var(--ob-ink-muted); }
.pill-status-returned::before { background: var(--ob-ink-muted); }
.pill-status-denied, .pill-status-cancelled { color: var(--ob-danger); }
.pill-status-denied::before, .pill-status-cancelled::before { background: var(--ob-danger); }

/* -- Cards -----------------------------------------------------------------*/
.card, .lib-card, .stat-card, .admin-panel, .auth-card, .tour-card, .scan-card {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  padding: 18px;
  color: var(--ob-ink);
}
.lib-card { display: block; border-bottom: 0.5px solid var(--ob-rule); }
.lib-card:hover { box-shadow: var(--ob-shadow-md); }
.lib-card-meta { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; align-items: center; }
.lib-card-count { color: var(--ob-ink-muted); font-size: 12px; }
.lib-card h3 { margin-top: 4px; margin-bottom: 4px; }

/* -- Stats row -------------------------------------------------------------*/
.stat-row { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 16px 0 24px; }
@media (min-width: 600px) { .stat-row { grid-template-columns: repeat(4, 1fr); } }
.stat-card { padding: 16px; text-align: center; color: var(--ob-ink); border-bottom: 0.5px solid var(--ob-rule); }
.stat-card:hover { color: var(--ob-ink); }
.stat-num {
  display: block; font-family: var(--ob-font-display); font-size: 28px;
  font-weight: 600; letter-spacing: -0.02em; color: var(--ob-ink);
}
.stat-label {
  display: block; font-family: var(--ob-font-mono); font-size: 10px;
  letter-spacing: 0.10em; text-transform: uppercase; color: var(--ob-ink-muted);
  margin-top: 4px;
}
.stat-card-warn .stat-num { color: var(--ob-danger); }

/* -- Card grid -------------------------------------------------------------*/
.card-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 600px) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .card-grid { grid-template-columns: repeat(3, 1fr); } }

/* ── Explore feed (Search/Explore page) ──────────────────────────────────
   30-tile grid below the search bar — random visible books weighted
   toward the viewer's friends. Tile is cover-led: the cover is the
   biggest visual element, with title/author/library text beneath in
   compact size. Hover lifts the tile slightly.

   Layout: container-driven via `auto-fill, minmax()`. The grid figures
   out the column count from the ACTUAL content-area width (which on
   desktop is `viewport − sidebar(240) − rightrail(360) − padding(112)`,
   so ~700–1200px depending on viewport), not from the viewport itself.
   Each tile is 130–~190px wide, regardless of how wide the screen gets;
   wider screens just fit more tiles per row instead of inflating each
   one. This stops the 2:3 cover from scaling up past ~290px tall. */
.explore-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 12px;
  margin-top: 4px;
}

.explore-tile {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  border: 0.5px solid var(--ob-rule);
  border-radius: 10px;
  background: var(--ob-surface);
  text-decoration: none;
  color: var(--ob-ink);
  /* Belt-and-braces against a single huge tile breaking the row — even
     if a parent container reports an unexpectedly wide column to the
     grid, the tile itself stays a sensible thumbnail size. */
  max-width: 100%;
  min-width: 0;
  transition: transform 160ms ease,
              box-shadow 160ms ease,
              border-color 160ms ease;
}
.explore-tile:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--ob-accent) 40%, var(--ob-rule));
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
}
.explore-tile:focus-visible {
  outline: 2px solid var(--ob-accent);
  outline-offset: 2px;
}

.explore-tile-cover {
  position: relative;
  /* Book-cover-standard 2:3 ratio. With a 130–190px wide tile this works
     out to 195–285px tall — reasonable thumbnail proportions. */
  aspect-ratio: 2 / 3;
  width: 100%;
  background: var(--ob-surface-2);
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.explore-tile-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.explore-tile-cover-fallback {
  font-family: var(--ob-font-display);
  font-size: 32px;
  font-weight: 600;
  color: var(--ob-ink-muted);
}
.explore-tile-friend-pin {
  position: absolute;
  top: 5px; right: 5px;
  width: 20px; height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ob-accent);
  color: #fff;
  border-radius: 50%;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
  pointer-events: none;
}

.explore-tile-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0; /* allow ellipsis to engage */
  padding: 2px 2px 0;
}
.explore-tile-title {
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.3;
  /* Two-line clamp keeps the grid tidy. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.explore-tile-author {
  font-size: 11.5px;
  color: var(--ob-ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.explore-tile-library {
  font-size: 9.5px;
  color: var(--ob-ink-muted);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.explore-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 22px;
  flex-wrap: wrap;
}
.explore-count {
  font-size: 11px;
  color: var(--ob-ink-muted);
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
}
.explore-done-msg {
  text-align: center;
  margin-top: 10px;
}

/* -- Forms -----------------------------------------------------------------*/
.form { display: flex; flex-direction: column; gap: 14px; max-width: 560px; }
.form label {
  display: flex; flex-direction: column; gap: 4px;
  font-family: var(--ob-font-mono);
  font-size: 10.5px; letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--ob-ink-muted);
}
.form label.checkbox-label, .form label.radio-label {
  flex-direction: row; align-items: flex-start; gap: 10px;
  font-family: var(--ob-font-body);
  font-size: 13px; letter-spacing: 0; text-transform: none; color: var(--ob-ink);
}
.form label.checkbox-label input,
.form label.radio-label input { width: auto; flex-shrink: 0; margin-top: 2px; }

.form input[type=text], .form input[type=email], .form input[type=password],
.form input[type=number], .form input[type=file], .form input[type=date],
.form input[type=url], .form textarea, .form select {
  width: 100%;
  padding: 11px 14px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule-strong);
  border-radius: var(--ob-radius-md);
  color: var(--ob-ink);
  font-family: var(--ob-font-body); font-size: 14px;
}
.form input::placeholder, .form textarea::placeholder {
  color: var(--ob-ink-subtle); font-family: var(--ob-font-mono);
  font-size: 12px; letter-spacing: 0.04em;
}
.form input:focus, .form textarea:focus, .form select:focus {
  border-color: var(--ob-accent);
  outline: 1.5px solid var(--ob-accent); outline-offset: 0;
}
.form small {
  font-family: var(--ob-font-mono); font-size: 10.5px; letter-spacing: 0.06em;
  color: var(--ob-ink-subtle); text-transform: none;
}
.form-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 480px) { .form-grid-2 { grid-template-columns: 1fr; } }

input[type=search], .form input[type=search] { border-radius: 999px; padding: 10px 16px; }
input[type=checkbox], input[type=radio] { accent-color: var(--ob-ink); }

.radio-label {
  padding: 12px 14px; background: var(--ob-surface); border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md); cursor: pointer;
}
.radio-label > span:not(:first-child) { display: flex; flex-direction: column; gap: 2px; flex: 1; }
.radio-label:has(input:checked) { border-color: var(--ob-accent); }

.filter-input { padding: 9px 14px; background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule-strong); border-radius: var(--ob-radius-md);
  color: var(--ob-ink); font-family: var(--ob-font-body); font-size: 13px; }
.filter-select { padding: 9px 14px; background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule-strong); border-radius: var(--ob-radius-md);
  color: var(--ob-ink); font-family: var(--ob-font-body); font-size: 13px; }

/* -- Auth card -------------------------------------------------------------*/
.auth-card {
  max-width: 420px; margin: 48px auto;
  background: var(--ob-surface);
  padding: 32px;
  border-radius: var(--ob-radius-lg);
  box-shadow: var(--ob-shadow-md);
}
.auth-card h1 { margin-bottom: 6px; }
.auth-alt {
  margin-top: 18px; text-align: center; font-family: var(--ob-font-mono);
  font-size: 11px; letter-spacing: 0.08em; color: var(--ob-ink-muted);
  text-transform: uppercase;
}
.auth-alt a { border-bottom: 0; color: var(--ob-ink); }

.oauth-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.oauth-divider {
  text-align: center; color: var(--ob-ink-subtle);
  font-family: var(--ob-font-mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; position: relative; margin: 12px 0;
}
.oauth-divider::before, .oauth-divider::after {
  content: ''; position: absolute; top: 50%; width: 40%;
  border-top: 0.5px solid var(--ob-rule);
}
.oauth-divider::before { left: 0; } .oauth-divider::after { right: 0; }
.oauth-divider span { background: var(--ob-surface); padding: 0 12px; position: relative; }

/* -- Hero --------------------------------------------------------------- */
.hero { text-align: center; padding: 56px 16px 32px; }
.hero h1 { font-size: 48px; margin-bottom: 12px; }
@media (max-width: 600px) { .hero h1 { font-size: 36px; } }
.hero-tagline { color: var(--ob-ink-muted); font-size: 16px; margin-bottom: 24px; }
.hero-blurb { max-width: 540px; margin: 0 auto 28px; color: var(--ob-ink-muted); line-height: 1.55; }
.hero-cta { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

/* -- Page heads --------------------------------------------------------- */
.section { margin: 28px 0; }
.section h2 { font-size: 20px; margin-bottom: 14px; }
.section-head, .page-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 12px; margin-bottom: 18px; flex-wrap: wrap;
}
.page-head h1 { margin: 0; }
.page-head-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* -- Flash / banners --------------------------------------------------- */
.flash-stack {
  position: fixed; top: 84px; left: 50%; transform: translateX(-50%);
  z-index: 100; display: flex; flex-direction: column; gap: 8px;
  max-width: 90vw;
}
.flash {
  padding: 11px 18px; border-radius: var(--ob-radius-md);
  background: var(--ob-surface); border: 0.5px solid var(--ob-rule);
  box-shadow: var(--ob-shadow-md);
  font-size: 13px; cursor: pointer; min-width: 280px;
}
.flash-success { border-left: 3px solid var(--ob-success); }
.flash-error { border-left: 3px solid var(--ob-danger); }
.flash-info { border-left: 3px solid var(--ob-accent); }

.alert {
  background: var(--ob-surface-2); padding: 12px 16px;
  border-radius: var(--ob-radius-md); margin: 12px 0;
  border-left: 3px solid var(--ob-accent);
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px; flex-wrap: wrap; font-size: 13px;
}
.alert.flash-error { border-left-color: var(--ob-danger); }
.alert.flash-info { border-left-color: var(--ob-info); }
.alert.flash-success { border-left-color: var(--ob-success); }

.banner-warn {
  background: var(--ob-surface-2); padding: 8px 16px; text-align: center;
  font-size: 12px; border-bottom: 0.5px solid var(--ob-rule);
  color: var(--ob-ink-muted); font-family: var(--ob-font-mono); letter-spacing: 0.04em;
}
.banner-warn form { display: inline; }

.impersonation-banner {
  background: var(--ob-danger); color: var(--ob-ink-inverse);
  padding: 8px 16px; text-align: center; font-size: 12px;
  font-family: var(--ob-font-mono); letter-spacing: 0.06em;
}
.impersonation-banner form { display: inline; margin-left: 8px; }

.platform-announcement {
  padding: 10px 16px; text-align: center; font-size: 13px;
  border-bottom: 0.5px solid var(--ob-rule); color: var(--ob-ink);
  font-family: var(--ob-font-display); font-style: italic;
}
.platform-info { background: var(--ob-surface-2); }
.platform-warn { background: var(--ob-surface-2); border-left: 3px solid var(--ob-accent); }
.platform-urgent { background: var(--ob-danger); color: var(--ob-ink-inverse); font-weight: 600; }

/* -- Profile ----------------------------------------------------------- */
.profile-head { display: flex; gap: 18px; align-items: center; margin-bottom: 16px; }
.profile-banner {
  width: 100%; height: 160px; background: var(--ob-surface-2);
  border-radius: var(--ob-radius-lg); margin-bottom: 16px;
  background-size: cover; background-position: center;
  border: 0.5px solid var(--ob-rule);
}
.avatar-lg {
  width: 88px; height: 88px; border-radius: 50%;
  border: 0.5px solid var(--ob-rule); object-fit: cover;
  background: var(--ob-surface-2); color: var(--ob-ink);
  font-family: var(--ob-font-mono); font-size: 28px; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center;
}
.avatar-sm {
  width: 36px; height: 36px; border-radius: 50%; border: 0.5px solid var(--ob-rule);
  object-fit: cover; display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--ob-font-mono); font-size: 12px; font-weight: 600;
  background: var(--ob-surface-2); color: var(--ob-ink);
}
.avatar-preview {
  margin-top: 8px; width: 80px; height: 80px;
  border-radius: 50%; object-fit: cover; border: 0.5px solid var(--ob-rule);
}
.bio { margin-top: 8px; font-style: italic; font-family: var(--ob-font-display); }
.profile-actions { display: flex; gap: 8px; margin: 12px 0; flex-wrap: wrap; align-items: center; }
.meta-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }

/* -- Lists ------------------------------------------------------------- */
.user-list, .book-list, .borrow-list, .feed-list, .notif-list,
.message-list, .session-list {
  list-style: none; display: flex; flex-direction: column;
}
.user-list li, .borrow-list li, .feed-list li, .notif-list li,
.session-item {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  padding: 12px 14px;
  display: flex; align-items: center; gap: 12px; justify-content: space-between;
  margin-bottom: 8px;
}
.user-row, .feed-actor {
  display: flex; align-items: center; gap: 12px; flex: 1;
  color: var(--ob-ink); border-bottom: 0;
}
.user-row:hover { color: var(--ob-ink); }
.row-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.row-action-form { display: inline; margin: 0; }
.admin-users-table .row-actions .btn-sm { white-space: nowrap; }

.book-list li {
  background: var(--ob-surface); border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md); padding: 0; margin-bottom: 8px;
}
.book-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; color: var(--ob-ink); border-bottom: 0; width: 100%;
}
.book-row:hover { color: var(--ob-ink); background: var(--ob-surface-2); }
.book-cover-sm {
  width: 40px; height: 56px;
  background: var(--ob-surface-2); color: var(--ob-ink);
  border-radius: var(--ob-radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--ob-font-display); font-weight: 600; font-size: 16px;
  flex-shrink: 0; overflow: hidden;
  border: 0.5px solid var(--ob-rule);
  position: relative;
}
.book-cover-sm img { width: 100%; height: 100%; object-fit: cover; }
.book-cover-sm::after {
  content: ''; position: absolute; left: 2px; top: 2px; bottom: 2px; width: 1.5px;
  background: rgba(0,0,0,0.2); border-radius: 1px;
}
.book-row-info { flex: 1; min-width: 0; }
.book-row-info strong {
  display: block; font-family: var(--ob-font-display); font-size: 15px; font-weight: 600;
  letter-spacing: -0.01em;
}
.book-row-info small { font-size: 12px; color: var(--ob-ink-muted); }

/* -- Filter chips ------------------------------------------------------ */
.filter-bar { display: flex; gap: 8px; margin: 12px 0; flex-wrap: wrap; }
.filter-bar input, .filter-bar select {
  flex: 1; min-width: 150px;
  padding: 9px 14px; background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule-strong); border-radius: var(--ob-radius-md);
  color: var(--ob-ink); font-family: var(--ob-font-body); font-size: 13px;
}

.filter-chip, .chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-radius: 999px;
  font-family: var(--ob-font-mono); font-size: 11px;
  letter-spacing: 0.10em; text-transform: uppercase;
  border: 0.5px solid var(--ob-rule); cursor: pointer; border-bottom: 0;
  background: transparent; color: var(--ob-ink-muted);
}
.filter-chip.on, .chip.on, .filter-chip:hover, .chip:hover {
  background: var(--ob-ink); color: var(--ob-bg); border-color: transparent;
}
.filter-pill { display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px;
  font-family: var(--ob-font-mono); font-size: 11px;
  letter-spacing: 0.10em; text-transform: uppercase;
  border: 0.5px solid var(--ob-rule); cursor: pointer; border-bottom: 0;
  background: transparent; color: var(--ob-ink-muted);
}
.filter-pill.on { background: var(--ob-ink); color: var(--ob-bg); border-color: transparent; }
.filter-pill-count { color: var(--ob-ink-subtle); font-size: 10px; }
.filter-form { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* -- Borrow ------------------------------------------------------------ */
.borrow-item { flex-direction: column !important; align-items: stretch !important; gap: 8px; }
.borrow-meta { display: flex; flex-direction: column; gap: 2px; }
.borrow-meta strong { font-family: var(--ob-font-display); font-size: 16px; }
.borrow-msg {
  background: var(--ob-surface-2);
  padding: 8px 14px; border-radius: var(--ob-radius-md);
  font-family: var(--ob-font-display); font-style: italic; font-size: 14px;
}
.borrow-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.status-overdue { border-left: 3px solid var(--ob-danger); }
.overdue-text { color: var(--ob-danger); font-weight: 600; }

/* -- Feed -------------------------------------------------------------- */
.feed-item { padding: 16px; gap: 14px; }
.feed-body { flex: 1; }
.feed-body p { margin-bottom: 4px; }

.feed-card {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 18px;
  padding: 22px 0;
  border-bottom: 0.5px solid var(--ob-rule);
  background: transparent;
}
@media (max-width: 600px) {
  .feed-card { grid-template-columns: 88px 1fr; gap: 14px; padding: 16px 0; }
}
.feed-card-cover {
  width: 110px; aspect-ratio: 2/3; border-radius: var(--ob-radius-sm);
  background: var(--ob-surface-2); border: 0.5px solid var(--ob-rule);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--ob-font-display); font-size: 32px; color: var(--ob-ink-muted);
  overflow: hidden; box-shadow: var(--ob-shadow-sm);
}
@media (max-width: 600px) { .feed-card-cover { width: 88px; } }
.feed-card-cover img { width: 100%; height: 100%; object-fit: cover; }
.feed-card-actor { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.feed-card-actor strong { font-family: var(--ob-font-body); font-weight: 600; font-size: 13px; }
.feed-card-verb { color: var(--ob-ink-muted); font-size: 12px; }
.feed-card-title {
  font-family: var(--ob-font-display); font-size: 19px; font-weight: 600;
  letter-spacing: -0.01em; margin-bottom: 2px; line-height: 1.2;
}
.feed-card-author { font-family: var(--ob-font-mono); font-size: 11px; letter-spacing: 0.06em; color: var(--ob-ink-muted); text-transform: uppercase; }
.feed-card-note {
  margin-top: 10px; font-family: var(--ob-font-display); font-style: italic;
  font-size: 15px; line-height: 1.5; color: var(--ob-ink); text-wrap: pretty;
}
.feed-card-actions {
  display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; align-items: center;
}
.feed-card-actions .pill { font-size: 9.5px; }
.feed-action-btn {
  font-family: var(--ob-font-mono); font-size: 10.5px; letter-spacing: 0.10em;
  text-transform: uppercase; color: var(--ob-ink-muted); background: transparent;
  border: 0; cursor: pointer; padding: 4px 6px; border-bottom: 0;
}
.feed-action-btn:hover { color: var(--ob-ink); }
.feed-action-btn.on { color: var(--ob-accent); }
.feed-card-body-link { display: contents; color: var(--ob-ink); border-bottom: 0; }

.reaction-row { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.reaction-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: 0.5px solid var(--ob-rule);
  border-radius: 999px; padding: 3px 10px; font-size: 12px; cursor: pointer;
  color: var(--ob-ink-muted); border-bottom: 0;
}
.reaction-btn:hover, .reaction-btn.on { color: var(--ob-ink); border-color: var(--ob-rule-strong); }
.reaction-btn .count { font-family: var(--ob-font-mono); font-size: 10px; }

/* -- Notifications (legacy item — kept for any incidental uses) -------- */
.notif-item { position: relative; }
.notif-item.unread { background: var(--ob-surface-2); }
.notif-body { flex: 1; }
.notif-time { font-size: 10px; color: var(--ob-ink-subtle); font-family: var(--ob-font-mono); letter-spacing: 0.06em; }

/* ── Notifications inbox (Direction A — Editorial inbox) ─────────────────
   Layout per row:
     [icon] [avatar] [body (verb + time)] [actions]
   Unread rows get a 2px coloured accent border on the left, where the
   colour comes from the .kind-* class. Read rows lose the accent and
   slide into a flat surface. Day buckets (Today / Yesterday / etc.)
   are tiny mono-caps headers above the list. */
.ob-notif-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 18px;
}

.ob-notif-inbox { max-width: 760px; }

.ob-notif-day-head {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin: 24px 0 10px;
  padding-bottom: 6px;
  border-bottom: 0.5px solid var(--ob-rule);
  font-weight: 600;
}
.ob-notif-day-head:first-child { margin-top: 0; }

.ob-notif-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ob-notif-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px 12px 18px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  transition: opacity 200ms ease, transform 200ms ease, max-height 200ms ease, padding 200ms ease, margin 200ms ease;
  overflow: hidden;
  max-height: 200px;
}
.ob-notif-row.removing {
  opacity: 0;
  transform: translateX(8px);
  max-height: 0;
  margin: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-color: transparent;
}

/* Unread accent — 3px left ribbon coloured by notification kind. The
   ribbon is achieved with a left border (not a pseudo) so it stays
   crisp against the row's rounded corner. */
.ob-notif-row.unread {
  border-left-width: 3px;
  background: var(--ob-surface-2);
}
.ob-notif-row.unread.kind-social   { border-left-color: var(--ob-accent); }
.ob-notif-row.unread.kind-borrow   { border-left-color: var(--ob-success); }
.ob-notif-row.unread.kind-library  { border-left-color: #5a7e9a; }   /* muted blue */
.ob-notif-row.unread.kind-system   { border-left-color: var(--ob-warning); }

.ob-notif-link {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;            /* let the text truncate cleanly */
  color: var(--ob-ink);
  border-bottom: 0;
  text-decoration: none;
}
.ob-notif-link:hover { color: var(--ob-ink); }
.ob-notif-link:hover .ob-notif-text strong { color: var(--ob-accent); }

.ob-notif-icon {
  flex: 0 0 28px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  font-size: 16px;
  color: var(--ob-ink-muted);
  border-radius: 50%;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
}
.ob-notif-row.unread .ob-notif-icon { background: var(--ob-surface); }

.ob-notif-avatar {
  flex: 0 0 36px;
}
.ob-notif-avatar-empty {
  display: inline-block;
  width: 36px;
  height: 36px;
  /* visual placeholder for system notifications with no actor */
}

.ob-notif-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ob-notif-text {
  color: var(--ob-ink);
  font-size: 14px;
  line-height: 1.4;
  word-break: break-word;
}
.ob-notif-text strong { font-weight: 600; }
.ob-notif-text em {
  font-style: italic;
  font-family: var(--ob-font-display);
  color: var(--ob-ink);
}

.ob-notif-time {
  font-size: 10px;
  color: var(--ob-ink-subtle);
  letter-spacing: 0.06em;
}

.ob-notif-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
  align-items: center;
}
.ob-notif-actions form { margin: 0; }

/* ── Post-action status pill ──────────────────────────────────────
   Replaces the Accept/Decline button cluster after the user clicks
   one. Stays in the row so the user sees what they just did, instead
   of the row vanishing. Colour-coded: accept = success green,
   decline = neutral muted. */
.ob-notif-status {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 11px;
  border-radius: 999px;
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  white-space: nowrap;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  color: var(--ob-ink-muted);
}
.ob-notif-status-accept {
  color: var(--ob-success);
  border-color: color-mix(in srgb, var(--ob-success) 50%, var(--ob-rule));
  background: color-mix(in srgb, var(--ob-success) 8%, var(--ob-surface));
}
.ob-notif-status-decline {
  color: var(--ob-ink-muted);
}

.ob-notif-empty {
  margin: 32px 0;
  padding: 24px;
  background: var(--ob-surface);
  border: 0.5px dashed var(--ob-rule);
  border-radius: var(--ob-radius-md);
  text-align: center;
}

@media (max-width: 600px) {
  .ob-notif-row {
    flex-wrap: wrap;
    padding: 10px 12px 10px 14px;
  }
  .ob-notif-actions {
    flex-basis: 100%;
    justify-content: flex-end;
    margin-top: 4px;
  }
  .ob-notif-icon { display: none; }   /* save horizontal real-estate on phones */
}

/* -- Book detail ------------------------------------------------------- */
.book-detail { display: flex; gap: 28px; flex-direction: column; margin-top: 12px; }
@media (min-width: 600px) { .book-detail { flex-direction: row; align-items: flex-start; } }
.book-cover-lg {
  width: 200px; aspect-ratio: 2/3;
  background: var(--ob-surface-2); color: var(--ob-ink);
  border-radius: var(--ob-radius-md);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--ob-font-display); font-size: 64px; font-weight: 600;
  overflow: hidden; flex-shrink: 0;
  border: 0.5px solid var(--ob-rule);
  box-shadow: var(--ob-shadow-md);
}
.book-cover-lg img { width: 100%; height: 100%; object-fit: cover; }
.book-detail-body { flex: 1; }
.book-detail-body h1 { margin-bottom: 6px; }
.book-desc {
  margin: 16px 0; line-height: 1.6;
  font-family: var(--ob-font-display); font-style: italic; font-size: 15px;
  color: var(--ob-ink); text-wrap: pretty;
}

.cover-preview {
  margin-top: 8px;
  max-height: 220px;
  max-width: 160px;
  width: auto;        /* don't stretch — preserve native aspect ratio */
  height: auto;
  object-fit: contain;
  border-radius: var(--ob-radius-sm);
  display: block;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
}

/* ── Cover chooser (Edit-Book only) ───────────────────────────────────────
   Three radio tiles: current cover, ISBN-source cover, upload-new. The
   tiles are <label>s wrapping a hidden radio + thumbnail + caption; the
   selected tile gets a 2px accent ring so the user can see which one
   they've picked at a glance. The file input lives inside the upload
   tile and is visually hidden — clicking the tile triggers the picker. */
.cover-chooser {
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  padding: 12px 14px 14px;
  margin: 0;
  background: var(--ob-surface);
}
.cover-chooser legend {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  padding: 0 6px;
}
.cover-options {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 6px 0 8px;
}
.cover-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  background: var(--ob-surface-2);
  cursor: pointer;
  width: 116px;
  position: relative;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.cover-option:hover { border-color: var(--ob-rule-strong); }
.cover-option input[type="radio"] {
  /* hide the native dot but keep it focusable for keyboard users */
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
/* :has() lets us style the parent label when its child radio is
   checked — every modern browser supports this now (2023+). The
   inset 2px ring gives a clean "selected" affordance without
   shifting layout. */
.cover-option:has(input[type="radio"]:checked) {
  border-color: var(--ob-accent);
  box-shadow: 0 0 0 1.5px var(--ob-accent) inset;
}
.cover-option:has(input[type="radio"]:focus-visible) {
  outline: 2px solid var(--ob-accent);
  outline-offset: 2px;
}
.cover-option .cover-thumb {
  width: 96px;
  height: 144px;
  object-fit: cover;
  border-radius: 3px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  display: block;
}
/* Upload-tile placeholder — '+' sign on a tinted square so the
   tile reads as an action even when no preview is loaded. */
.cover-option .cover-thumb-upload {
  display: grid;
  place-items: center;
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 36px;
  color: var(--ob-ink-muted);
  background: var(--ob-surface);
}
.cover-option .mono-meta {
  font-size: 10px;
  letter-spacing: 0.10em;
  color: var(--ob-ink-muted);
  text-align: center;
}
/* The file input inside the upload tile is visually hidden but still
   reachable via the tile's click handler (label propagates to the
   input). */
.cover-option-file {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* Delete-uploaded affordance under the chooser. Sits its own row,
   right-aligned so it doesn't compete with the chooser's tiles. */
.cover-delete-form {
  margin: 6px 0 0;
  display: flex;
  justify-content: flex-end;
}

/* ── Structured-dimensions fieldset (Add/Edit Book) ─────────────────────────
   Four small numeric inputs in a row — Length / Width / Height / Weight —
   so the user can fill machine-friendly dimensions distinct from the
   free-text `dimensions` field. Each label stacks its caption above its
   input the same way the rest of the form does. */
.dim-fieldset {
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  padding: 10px 12px 12px;
  margin: 0;
}
.dim-fieldset legend {
  font-family: var(--ob-font-mono);
  font-size: 10px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  padding: 0 6px;
}
.dim-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-top: 4px;
}
.dim-grid label {
  display: flex;
  flex-direction: column;
  font-size: 11px;
  color: var(--ob-ink-muted);
}
.dim-grid label input {
  margin-top: 2px;
  font-size: 14px;
}
@media (max-width: 480px) {
  .dim-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ── Currency-prefix input wrapper (MSRP) ───────────────────────────────────
   A '$' glyph sits flush-left inside the same border as the input. The
   input itself loses its own border + padding-left so the two read as a
   single field. */
.input-with-prefix {
  display: inline-flex;
  align-items: stretch;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  background: var(--ob-surface);
  overflow: hidden;
}
.input-with-prefix .input-prefix {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  background: var(--ob-surface-2);
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-mono);
  font-size: 13px;
  border-right: 0.5px solid var(--ob-rule);
}
.input-with-prefix input {
  border: 0;
  padding: 6px 10px;
  background: transparent;
  font-size: 14px;
  flex: 1;
  min-width: 0;
}
.input-with-prefix input:focus {
  outline: none;
}
.input-with-prefix:focus-within {
  border-color: var(--ob-accent);
  box-shadow: 0 0 0 1px var(--ob-accent);
}
.cover-preview.cover-preview-loading {
  /* subtle pulse so the user knows something is happening */
  background: linear-gradient(90deg,
    var(--ob-surface-2) 25%,
    var(--ob-surface) 50%,
    var(--ob-surface-2) 75%);
  background-size: 200% 100%;
  animation: cover-shimmer 1.2s ease-in-out infinite;
}
@keyframes cover-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.cover-preview.cover-preview-loaded {
  /* once loaded, drop the placeholder background */
  background: transparent;
  animation: none;
}

/* ── ISBN auto-lookup feedback ─────────────────────────────────────────── */
.isbn-label-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.isbn-status {
  font-family: var(--ob-font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--ob-surface-2);
  color: var(--ob-ink-muted);
  border: 0.5px solid var(--ob-rule);
  animation: ob-fade 0.2s ease-out;
}
.isbn-field-wrap.is-loading .isbn-status { background: color-mix(in srgb, var(--ob-accent) 18%, var(--ob-surface-2)); color: var(--ob-ink); border-color: var(--ob-accent); }
.isbn-field-wrap.is-found    .isbn-status { background: color-mix(in srgb, var(--ob-success) 16%, var(--ob-surface-2)); color: var(--ob-ink); border-color: var(--ob-success); }
.isbn-field-wrap.is-notfound .isbn-status { background: color-mix(in srgb, var(--ob-warning) 16%, var(--ob-surface-2)); color: var(--ob-ink); border-color: var(--ob-warning); }
.isbn-field-wrap.is-error    .isbn-status { background: color-mix(in srgb, var(--ob-danger) 14%, var(--ob-surface-2)); color: var(--ob-ink); border-color: var(--ob-danger); }

.isbn-field-wrap.is-loading input[type="text"] {
  border-color: var(--ob-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ob-accent) 20%, transparent);
  background-image: linear-gradient(90deg,
    transparent 0%,
    color-mix(in srgb, var(--ob-accent) 20%, transparent) 50%,
    transparent 100%);
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: ob-isbn-shimmer 1.1s linear infinite;
}
.isbn-field-wrap.is-found input[type="text"] {
  border-color: var(--ob-success);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ob-success) 18%, transparent);
}
.isbn-field-wrap.is-notfound input[type="text"] {
  border-color: var(--ob-warning);
}

.isbn-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  margin-bottom: 14px;
  border-radius: var(--ob-radius-md);
  border: 0.5px solid var(--ob-rule);
  font-size: 14px;
  animation: ob-fade 0.2s ease-out;
}
.isbn-banner-loading  { background: color-mix(in srgb, var(--ob-accent) 12%, var(--ob-surface)); border-color: var(--ob-accent); color: var(--ob-ink); }
.isbn-banner-found    { background: color-mix(in srgb, var(--ob-success) 14%, var(--ob-surface)); border-color: var(--ob-success); color: var(--ob-ink); }
.isbn-banner-notfound { background: color-mix(in srgb, var(--ob-warning) 14%, var(--ob-surface)); border-color: var(--ob-warning); color: var(--ob-ink); }
.isbn-banner-error    { background: color-mix(in srgb, var(--ob-danger) 12%, var(--ob-surface)); border-color: var(--ob-danger); color: var(--ob-ink); }

.isbn-spinner {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--ob-accent) 30%, transparent);
  border-top-color: var(--ob-accent);
  animation: ob-spin 0.7s linear infinite;
}

@keyframes ob-spin    { to { transform: rotate(360deg); } }
@keyframes ob-isbn-shimmer { 0% { background-position: -100% 0; } 100% { background-position: 200% 0; } }
@keyframes ob-fade    { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .isbn-spinner { animation: none; border-top-color: var(--ob-accent); }
  .isbn-field-wrap.is-loading input[type="text"] { animation: none; background-image: none; }
}

/* -- Tables ------------------------------------------------------------ */
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; }
.data-table th, .data-table td {
  padding: 9px 10px; text-align: left;
  border-bottom: 0.5px solid var(--ob-rule);
}
.data-table th {
  font-family: var(--ob-font-mono); font-size: 10px;
  letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--ob-ink-muted); font-weight: 500;
  border-bottom: 0.5px solid var(--ob-rule-strong);
}
.data-table tr:hover td { background: rgba(0,0,0,0.025); }
[data-theme="night"] .data-table tr:hover td { background: rgba(255,255,255,0.04); }

/* -- Pagination -------------------------------------------------------- */
.pagination { display: flex; gap: 12px; align-items: center; justify-content: center; margin: 24px 0; }
.pagination-info { font-family: var(--ob-font-mono); font-size: 11px; color: var(--ob-ink-muted); letter-spacing: 0.08em; }

/* -- Quick links / footer --------------------------------------------- */
.quick-links { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 24px; }
.footer {
  text-align: center; padding: 28px 16px;
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-mono); font-size: 10px;
  letter-spacing: 0.10em; text-transform: uppercase;
  margin-top: 48px;
  border-top: 0.5px solid var(--ob-rule);
}
.footer a { border-bottom: 0; color: var(--ob-ink-muted); }
.footer a:hover { color: var(--ob-ink); }
.dot-sep {
  display: inline-block; width: 3px; height: 3px; border-radius: 50%;
  background: var(--ob-ink-subtle); margin: 0 8px; vertical-align: middle;
}

/* Cookie banner CSS removed alongside the banner itself (see base.html
   rationale). The .cookie-banner residuals in the small grouped
   selectors below (animation reset + print suppression) are also
   pruned. */

/* -- Modal / Tour ----------------------------------------------------- */
.tour-overlay {
  position: fixed; inset: 0; background: var(--ob-overlay);
  display: grid; place-items: center; z-index: 200; padding: 16px;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.tour-overlay.tour-closing { opacity: 0; transition: opacity .15s; }
.tour-card {
  background: var(--ob-surface); padding: 28px;
  border-radius: var(--ob-radius-lg);
  max-width: 460px; width: 100%; box-shadow: var(--ob-shadow-lg);
}
.tour-card h2 { font-size: 24px; margin-bottom: 4px; }
.tour-sub { color: var(--ob-ink-muted); margin-bottom: 18px; }
.tour-steps { list-style: none; display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.tour-steps li {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 14px; background: var(--ob-surface-2);
  border-radius: var(--ob-radius-md);
}
.tour-steps li > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.tour-icon { font-size: 22px; line-height: 1.1; flex-shrink: 0; width: 32px; text-align: center; }
.tour-foot { margin-top: 12px; text-align: center; }
.tour-dontshow {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--ob-ink-muted);
  margin-bottom: 12px; cursor: pointer; user-select: none;
}
.tour-dontshow input[type="checkbox"] { margin: 0; cursor: pointer; }

/* -- Scanner ----------------------------------------------------------- */
.scan-wrap { background: var(--ob-surface); border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-md); padding: 16px; margin: 12px 0; }
#scan-video { width: 100%; max-width: 480px; border-radius: var(--ob-radius-sm); background: black; }
.scan-controls { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.scan-result { margin: 12px 0; }
.scan-card { background: var(--ob-surface-2); padding: 16px; border-radius: var(--ob-radius-md); border-left: 3px solid var(--ob-accent); }
.scan-card-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.scan-meta { margin-top: 8px; }

/* -- 2FA --------------------------------------------------------------- */
.totp-qr { width: 200px; height: 200px; margin: 12px 0; border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-md); padding: 6px; background: white; }
.setup-steps { margin-left: 22px; margin-bottom: 14px; }
.setup-steps li { margin-bottom: 6px; }
.backup-codes { list-style: none; display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; margin: 12px 0; }
.backup-codes li { background: var(--ob-surface-2); padding: 8px 12px; border-radius: var(--ob-radius-sm); text-align: center; font-family: var(--ob-font-mono); font-size: 13px; }

/* -- DMs --------------------------------------------------------------- */
.message-list {
  display: flex; flex-direction: column; gap: 4px;
  max-height: 60vh; overflow-y: auto;
  padding: 12px; background: var(--ob-surface-2);
  border-radius: var(--ob-radius-md); margin-bottom: 12px;
}
.message-list li { display: flex; flex-direction: column; }
.message-mine { align-items: flex-end; }
.message-theirs { align-items: flex-start; }
.message-bubble {
  max-width: 75%; padding: 10px 14px; border-radius: var(--ob-radius-lg);
  font-size: 14px; word-wrap: break-word;
  font-family: var(--ob-font-body); line-height: 1.4;
}
.message-mine .message-bubble {
  background: var(--ob-ink); color: var(--ob-bg); border-bottom-right-radius: 4px;
}
.message-theirs .message-bubble {
  background: var(--ob-surface); border: 0.5px solid var(--ob-rule);
  border-bottom-left-radius: 4px;
}

/* -- Sessions ---------------------------------------------------------- */
.session-current { border-color: var(--ob-accent) !important; }

/* -- Quotas ------------------------------------------------------------ */
.quota-bar { width: 100px; height: 6px; background: var(--ob-surface-2); border-radius: 999px; overflow: hidden; display: inline-block; vertical-align: middle; }
.quota-fill { height: 100%; background: var(--ob-success); transition: width .3s; }
.quota-fill-warn { background: var(--ob-accent); }
.quota-fill-danger { background: var(--ob-danger); }

/* -- Bar chart --------------------------------------------------------- */
.bar-chart { list-style: none; display: flex; flex-direction: column; gap: 6px; }
.bar-chart li { display: grid; grid-template-columns: 60px 1fr 40px; gap: 8px; align-items: center; font-size: 12px; }
.bar-label { color: var(--ob-ink-muted); font-family: var(--ob-font-mono); font-size: 11px; }
.bar { background: var(--ob-surface-2); height: 14px; border-radius: 4px; overflow: hidden; }
.bar-fill { display: block; height: 100%; background: var(--ob-accent); }
.bar-count { text-align: right; color: var(--ob-ink-muted); font-family: var(--ob-font-mono); font-size: 11px; }

/* -- Donor list / tags ------------------------------------------------ */
.donor-list { list-style: none; counter-reset: donor; display: flex; flex-direction: column; gap: 6px; }
.donor-list li {
  background: var(--ob-surface); border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md); padding: 12px 16px;
  display: grid; grid-template-columns: 50px 1fr auto; gap: 12px; align-items: center;
}
.donor-rank { font-family: var(--ob-font-display); font-weight: 600; color: var(--ob-accent); font-variant-numeric: tabular-nums; }
.donor-count { color: var(--ob-ink-muted); font-size: 12px; font-family: var(--ob-font-mono); }

.tag-cloud { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 16px; }
.tag-cloud li { background: var(--ob-surface-2); padding: 5px 12px; border-radius: 999px; font-size: 12px; display: inline-flex; align-items: center; gap: 6px; }
.tag-name { font-weight: 600; }
.tag-count { color: var(--ob-ink-muted); font-family: var(--ob-font-mono); font-size: 10px; }

/* -- QR / spine ------------------------------------------------------- */
.qr-details summary { cursor: pointer; padding: 6px 0; }
.qr-img { width: 200px; height: 200px; margin: 8px 0; }

/* "Book added — what next?" confirmation page */
.book-added-summary {
  display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap;
  padding: 18px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  margin-top: 20px;
}
.book-added-cover {
  width: 80px; max-height: 120px; height: auto;
  object-fit: contain;
  border-radius: var(--ob-radius-sm);
  background: var(--ob-bg);
  border: 0.5px solid var(--ob-rule);
}
.book-added-meta { flex: 1; min-width: 200px; }

.book-added-actions {
  display: grid;
  gap: 14px;
  grid-template-columns: 1fr;
  margin-top: 14px;
}
@media (min-width: 720px) {
  .book-added-actions { grid-template-columns: repeat(3, 1fr); }
}
.book-added-action {
  display: flex; flex-direction: column; gap: 6px;
  padding: 22px 20px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  color: var(--ob-ink);
  text-decoration: none;
  min-height: 130px;
  transition: all .15s ease;
}
.book-added-action:hover,
.book-added-action:focus-visible {
  border-color: var(--ob-accent);
  box-shadow: var(--ob-shadow-md);
  transform: translateY(-1px);
}
.book-added-action-primary {
  border-color: var(--ob-accent);
  background: var(--ob-surface-2);
}
.book-added-action-icon {
  font-size: 24px;
  line-height: 1;
  margin-bottom: 4px;
}
.book-added-action strong { font-size: 15px; }
.book-added-action small { font-size: 12px; line-height: 1.4; }

/* Library invitation buttons — sized for mobile touch targets (≥44px). */
.invite-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 14px;
  position: relative;  /* defends against parent stacking-context issues */
  z-index: 1;
}
.invite-action-form { margin: 0; flex: 1 1 130px; }
.invite-action-form-solo { flex: 0 0 auto; }
.invite-btn-accept,
.invite-btn-decline {
  width: 100%;
  min-height: 44px;
  padding: 12px 18px;
  font-size: 13px;
  /* prevent iOS double-tap-zoom from delaying the click */
  touch-action: manipulation;
}
.invite-pending-note {
  margin-top: 10px;
  padding: 10px 12px;
  background: var(--ob-surface-2);
  border-radius: var(--ob-radius-sm);
  border-left: 3px solid var(--ob-accent);
}
@media (min-width: 600px) {
  .invite-action-form { flex: 0 0 auto; }
  .invite-btn-accept,
  .invite-btn-decline { width: auto; min-width: 130px; }
}

.library-qr-panel {
  margin: 16px 0;
  padding: 14px 18px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  background: var(--ob-surface-2);
}
.library-qr-panel[open] { padding-bottom: 18px; }
.library-qr-panel summary { cursor: pointer; padding: 4px 0; }
.library-qr-body {
  display: flex; gap: 18px; flex-wrap: wrap; align-items: center;
  margin-top: 12px;
}
.library-qr-img {
  width: 200px; height: 200px;
  background: white;
  padding: 8px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  flex: 0 0 auto;
}
.library-qr-info { flex: 1; min-width: 200px; }
.library-qr-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0; }

.spine-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  align-items: stretch;
}
@media print { .spine-grid { grid-template-columns: repeat(3, 1fr); gap: 6mm; } }
.spine-label {
  background: white;                  /* don't inherit dark mode — labels are for print */
  color: black;
  border: 1px dashed #999;
  padding: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  page-break-inside: avoid;
  break-inside: avoid;
  border-radius: var(--ob-radius-sm);
  min-height: 76px;
  overflow: hidden;
  box-sizing: border-box;
}
.spine-qr { width: 60px; height: 60px; flex-shrink: 0; display: block; }
.spine-text { flex: 1; min-width: 0; font-size: 11px; line-height: 1.3; }
.spine-text strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--ob-font-display); color: black; }
.spine-text small { color: #555; font-family: var(--ob-font-mono); font-size: 10px; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* -- Audit log diff -------------------------------------------------- */
.audit-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
.audit-list li { padding: 8px 12px; background: var(--ob-surface); border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-sm); font-size: 12px; }
.audit-diff { font-family: var(--ob-font-mono); font-size: 11px; background: var(--ob-surface-2); padding: 8px 10px; border-radius: var(--ob-radius-sm); max-height: 200px; overflow: auto; white-space: pre-wrap; word-break: break-all; }
.log-tail { font-family: var(--ob-font-mono); font-size: 11px; background: var(--ob-surface-2); padding: 14px; border-radius: var(--ob-radius-md); max-height: 400px; overflow: auto; white-space: pre-wrap; }

/* -- Admin sub-nav --------------------------------------------------- */
.admin-nav {
  display: flex; flex-wrap: wrap; gap: 4px;
  background: var(--ob-surface-2); padding: 10px;
  border-radius: var(--ob-radius-md); margin-bottom: 18px;
}
.admin-nav a {
  padding: 5px 11px; border-radius: var(--ob-radius-sm);
  color: var(--ob-ink-muted); border-bottom: 0;
  font-family: var(--ob-font-mono); font-size: 10.5px; letter-spacing: 0.08em;
  text-transform: uppercase;
}
.admin-nav a:hover { background: var(--ob-surface); color: var(--ob-ink); }
.admin-nav a.on { background: var(--ob-ink); color: var(--ob-bg); }
.admin-nav a.on:hover { background: var(--ob-ink); color: var(--ob-bg); }
.admin-grid { display: grid; grid-template-columns: 1fr; gap: 14px; margin-bottom: 18px; }
@media (min-width: 700px) { .admin-grid { grid-template-columns: 1fr 1fr; } }
.admin-panel { padding: 18px; }
.admin-panel h2 { margin-bottom: 14px; }
.admin-chart { background: var(--ob-surface); border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-md); padding: 16px; }
.admin-chart canvas { width: 100%; height: auto; }
.admin-panel canvas { width: 100%; height: auto; display: block; }
.insights-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.insights-list li { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; background: var(--ob-surface-2); border-radius: var(--ob-radius-sm); border-left: 2px solid var(--ob-rule); }
.insights-list li.insight-alert { border-left-color: var(--ob-danger); background: color-mix(in srgb, var(--ob-danger) 8%, var(--ob-surface-2)); }
.insight-label { font-family: var(--ob-font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ob-ink-muted); }
.insight-value { font-size: 14px; color: var(--ob-ink); }
.db-tables { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 16px; }
.db-table-link { padding: 5px 11px; border-radius: var(--ob-radius-sm); background: var(--ob-surface-2); font-family: var(--ob-font-mono); font-size: 10.5px; color: var(--ob-ink-muted); border-bottom: 0; }
.db-table-link.on { background: var(--ob-ink); color: var(--ob-bg); }
.db-table-scroll { overflow-x: auto; }
.db-table-scroll table { font-size: 11px; min-width: 600px; }
.bulk-actions { display: flex; gap: 8px; margin-top: 14px; align-items: center; }
.admin-template-card { background: var(--ob-surface); border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-md); padding: 14px 18px; margin-bottom: 8px; }
.admin-template-card[open] summary { margin-bottom: 8px; }
.admin-template-card summary { cursor: pointer; font-size: 14px; }
.form-preview { max-height: 120px; margin-top: 8px; border-radius: var(--ob-radius-sm); }

/* -- Print ------------------------------------------------------------ */
@media print {
  .topbar, .bottombar, .footer, .flash-stack, .no-print { display: none !important; }
  body { padding: 0; background: white; color: black; }
  body::before { display: none; }
  .container { max-width: none; padding: 0; }
}
.print-page { background: white; color: black; padding: 16px; border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-md); }
.print-head h1 { font-size: 22px; margin-bottom: 4px; }
.print-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.print-table th { background: #f4f1ec; text-align: left; padding: 6px 8px; border-bottom: 1px solid #ccc; font-size: 10px; text-transform: uppercase; letter-spacing: .04em; }
.print-table td { padding: 6px 8px; border-bottom: 1px solid #eee; }

/* -- Error page ------------------------------------------------------- */
.error-page { text-align: center; padding: 80px 16px; }
.error-page h1 { font-size: 96px; margin-bottom: 8px; color: var(--ob-accent); font-style: italic; }

/* -- Legal ------------------------------------------------------------ */
.legal { max-width: 720px; line-height: 1.7; font-family: var(--ob-font-body); }
.legal h2 { margin-top: 28px; margin-bottom: 8px; font-size: 18px; }
.legal ul { margin-left: 22px; margin-bottom: 8px; }
.legal li { margin-bottom: 4px; }

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

/* -- Library shelves -------------------------------------------------- */
.shelf-rail { display: flex; gap: 12px; overflow-x: auto; padding: 8px 4px 16px; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
.shelf-rail > * { scroll-snap-align: start; flex-shrink: 0; }

/* -- Achievement badges ----------------------------------------------- */
.badge-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px; }
.badge-card { background: var(--ob-surface); border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-md); padding: 14px 10px; text-align: center; }
.badge-icon { width: 48px; height: 48px; border-radius: 50%; background: var(--ob-accent); color: var(--ob-accent-ink); display: inline-flex; align-items: center; justify-content: center; font-family: var(--ob-font-display); font-size: 22px; font-weight: 600; margin-bottom: 8px; }
.badge-name { font-family: var(--ob-font-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ob-ink); }
.badge-date { font-family: var(--ob-font-mono); font-size: 10px; color: var(--ob-ink-subtle); margin-top: 4px; }
.badge-locked { opacity: 0.4; }

/* -- Reading progress ------------------------------------------------- */
.progress-bar { height: 4px; width: 100%; background: var(--ob-surface-2); border-radius: 2px; overflow: hidden; }
.progress-fill { height: 100%; background: var(--ob-accent); transition: width .3s; }

/* -- Feed banner ------------------------------------------------------ */
.feed-new-banner {
  position: sticky; top: 60px; z-index: 5;
  background: var(--ob-accent); color: var(--ob-accent-ink);
  padding: 8px 14px; border-radius: var(--ob-radius-md);
  text-align: center; font-family: var(--ob-font-mono); font-size: 11px;
  letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer;
  margin-bottom: 16px; box-shadow: var(--ob-shadow-sm);
}

/* -- Comments --------------------------------------------------------- */
.comments-list { list-style: none; margin-top: 12px; display: flex; flex-direction: column; gap: 10px; }
.comment-item { display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; background: var(--ob-surface-2); border-radius: var(--ob-radius-md); }
.comment-body { flex: 1; min-width: 0; }
.comment-author { font-family: var(--ob-font-body); font-weight: 600; font-size: 13px; }
.comment-time { font-family: var(--ob-font-mono); font-size: 10px; color: var(--ob-ink-subtle); margin-left: 8px; letter-spacing: 0.06em; }
.comment-text { margin-top: 2px; font-size: 13px; line-height: 1.5; word-wrap: break-word; }
.comment-form { display: flex; gap: 8px; margin-top: 12px; }
.comment-form input { flex: 1; }

/* -- Mention / hashtag ------------------------------------------------ */
.mention { color: var(--ob-accent); border-bottom: 0; font-weight: 600; }
.hashtag { color: var(--ob-info); border-bottom: 0; }

/* -- Empty states ----------------------------------------------------- */
.empty-state { text-align: center; padding: 48px 16px; color: var(--ob-ink-muted); font-family: var(--ob-font-display); font-style: italic; font-size: 16px; }
.empty-state-meta { display: block; font-family: var(--ob-font-mono); font-style: normal; font-size: 10px; letter-spacing: 0.10em; text-transform: uppercase; color: var(--ob-ink-subtle); margin-bottom: 8px; }

.divider { border: 0; border-top: 0.5px solid var(--ob-rule); margin: 24px 0; }
.section-divider { height: 28px; }

/* -- Theme picker swatch --------------------------------------------- */
.theme-swatch { display: inline-block; width: 18px; height: 12px; border-radius: 2px; vertical-align: middle; margin-right: 6px; border: 0.5px solid var(--ob-rule); }
.theme-swatch-day { background: linear-gradient(135deg, #f0e6d2 50%, #c9a961 50%); }
.theme-swatch-night { background: linear-gradient(135deg, #0e0e0c 50%, #c9a961 50%); }
.theme-swatch-auto { background: linear-gradient(135deg, #f0e6d2 50%, #0e0e0c 50%); }

/* -- Recommendations ------------------------------------------------- */
.rec-card { display: flex; gap: 12px; padding: 14px; background: var(--ob-surface); border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-md); margin-bottom: 8px; }
.rec-cover { width: 56px; height: 80px; background: var(--ob-surface-2); border-radius: var(--ob-radius-sm); flex-shrink: 0; border: 0.5px solid var(--ob-rule); }
.rec-body { flex: 1; }
.rec-from { font-family: var(--ob-font-mono); font-size: 10px; letter-spacing: 0.10em; color: var(--ob-ink-muted); text-transform: uppercase; margin-top: 4px; }

/* -- Misc -------------------------------------------------------------- */
.danger-section { background: rgba(139,58,46,0.08); border-radius: var(--ob-radius-md); padding: 18px; margin-top: 28px; border: 0.5px solid rgba(139,58,46,0.25); }
.danger-form { margin-top: 16px; }
.form-actions { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.featured-section { margin: 28px 0; }
.section-bar { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 14px; }
.section-label { font-family: var(--ob-font-mono); font-size: 11px; letter-spacing: 0.10em; color: var(--ob-ink-muted); text-transform: uppercase; }
.section-link { font-family: var(--ob-font-mono); font-size: 10.5px; color: var(--ob-ink-muted); border-bottom: 0; }

.featured-row { display: flex; gap: 12px; overflow-x: auto; padding: 4px 0 16px; scroll-snap-type: x mandatory; }
.book-card-sm { flex-shrink: 0; width: 130px; scroll-snap-align: start; border-bottom: 0; color: var(--ob-ink); }
.book-card-sm:hover { color: var(--ob-ink); }
.book-card-sm .book-cover {
  width: 130px; aspect-ratio: 2/3;
  background: var(--ob-surface-2); border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm); display: flex; align-items: center; justify-content: center;
  font-family: var(--ob-font-display); font-size: 36px; color: var(--ob-ink-muted);
  position: relative; overflow: hidden; box-shadow: var(--ob-shadow-sm);
}
.book-card-sm .book-cover img { width: 100%; height: 100%; object-fit: cover; }
.book-card-sm .book-title {
  font-family: var(--ob-font-display); font-size: 14px; font-weight: 600;
  margin-top: 8px; line-height: 1.2; letter-spacing: -0.01em;
}
.book-card-sm .book-author { font-family: var(--ob-font-mono); font-size: 10px; color: var(--ob-ink-muted); letter-spacing: 0.06em; text-transform: uppercase; margin-top: 2px; }
.book-location-badge {
  position: absolute; top: 6px; right: 6px;
  background: var(--ob-bg); color: var(--ob-ink);
  font-family: var(--ob-font-mono); font-size: 8px; letter-spacing: 0.10em; text-transform: uppercase;
  padding: 2px 6px; border-radius: 999px; border: 0.5px solid var(--ob-rule);
}
.cover-letter { font-family: var(--ob-font-display); font-weight: 600; }

/* Search palette */
.search-overlay { position: fixed; inset: 0; background: var(--ob-overlay); z-index: 200; display: grid; place-items: start center; padding-top: 80px; }
.search-palette { width: min(560px, calc(100vw - 32px)); background: var(--ob-surface); border-radius: var(--ob-radius-lg); box-shadow: var(--ob-shadow-lg); padding: 16px; }

/* Loc dot */
.loc-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; }
.dot-street { background: var(--ob-warning); }
.dot-library { background: var(--ob-success); }

.book-row-end { display: inline-flex; gap: 8px; align-items: center; padding-right: 12px; }
.book-row-arrow { color: var(--ob-ink-subtle); font-size: 18px; }
.book-row-cover { width: 36px; height: 50px; background: var(--ob-surface-2); border: 0.5px solid var(--ob-rule); border-radius: var(--ob-radius-sm); display: inline-flex; align-items: center; justify-content: center; font-family: var(--ob-font-display); font-weight: 600; color: var(--ob-ink-muted); flex-shrink: 0; overflow: hidden; }
.book-row-cover img { width: 100%; height: 100%; object-fit: cover; }
.book-genre-tag { font-family: var(--ob-font-mono); font-size: 9.5px; letter-spacing: 0.08em; text-transform: uppercase; padding: 3px 9px; border-radius: 999px; background: var(--ob-surface-2); color: var(--ob-ink-muted); border: 0.5px solid var(--ob-rule); }

/* ════════════════════════════════════════════════════════════════════════════
   Only Books — Layout Migration (PRs 1–17)
   Spec: ONLY_BOOKS_LAYOUT.md
   Tokens come from the design system block above.
   ════════════════════════════════════════════════════════════════════════════ */

/* Hide legacy chrome we replaced — old topbar/bottombar still rendered by */
/* a couple of templates we haven't migrated yet, so they don't clash. */
[data-shell] .topbar, [data-shell] .bottombar { display: none; }

/* Hide the legacy footer everywhere except marketing */
[data-shell="app"] > body > .footer,
[data-shell="admin"] > body > .footer,
body.shell-app > .footer,
body.shell-admin > .footer,
body.shell-auth > .footer { display: none; }

/* ── Wordmark + tagline ─────────────────────────────────────────────────── */
.ob-wordmark {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 24px;
  letter-spacing: -0.01em;
  color: var(--ob-ink);
  text-decoration: none;
  display: inline-block;
}
.ob-wordmark.sm { font-size: 18px; }
.ob-wordmark .brand-dot,
.brand-dot { color: var(--ob-accent); font-weight: 700; }
.ob-wordmark .mono-meta {
  margin-left: 8px;
  align-self: center;
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--ob-surface-2);
  color: var(--ob-ink-muted);
}
.ob-tagline { margin: 4px 0 28px; font-size: 11px; }
.mono-meta {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
}

/* ════════ APP SHELL ═══════════════════════════════════════════════════════ */
.ob-app-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr) 360px;
  grid-template-areas: "sidenav center rightrail";
  background: var(--ob-bg);
  transition: grid-template-columns 0.18s ease;
}
/* Right rail collapsed — shrinks to just the toggle handle (36px wide). */
.ob-app-shell.rightrail-collapsed {
  grid-template-columns: 240px minmax(0, 1fr) 36px;
}
.ob-app-shell > .ob-sidenav    { grid-area: sidenav; }
.ob-app-shell > .ob-center     { grid-area: center; }
.ob-app-shell > .ob-rightrail  { grid-area: rightrail; }
.ob-mobile-top { display: none; }
.ob-tabbar     { display: none; }

/* Profile-page sign-out button is mobile-only — desktop has it in the
   sidebar. The form/button only appears on viewports where the bottom
   tabbar is active (i.e. where the sidebar is hidden). The display:flex
   inside the mobile media query (further down) re-enables it. */
.profile-signout-mobile { display: none; }

/* ── SideNav ── § 2.4 ── */
.ob-sidenav {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  padding: 24px 22px 22px;
  border-right: 0.5px solid var(--ob-rule);
  background: color-mix(in srgb, var(--ob-bg) 96%, var(--ob-ink));
  display: flex;
  flex-direction: column;
}
.ob-sidenav .ob-wordmark { margin-bottom: 2px; }
.ob-search-pill {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border-radius: 999px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-body);
  font-size: 13px;
  cursor: pointer;
  margin-bottom: 22px;
  text-align: left;
  transition: border-color .15s, background .15s;
}
.ob-search-pill:hover { border-color: var(--ob-rule-strong); }
.ob-search-pill .ob-search-placeholder { flex: 1; }
.ob-search-pill .ob-search-kbd {
  font-family: var(--ob-font-mono);
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--ob-bg);
  border: 0.5px solid var(--ob-rule);
}
.ob-primary-nav { display: flex; flex-direction: column; gap: 1px; }
.ob-nav-row {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 40px;
  padding: 0 12px;
  border-radius: 8px;
  color: var(--ob-ink);
  text-decoration: none;
  font-size: 14px;
  border-bottom: 0;
}
.ob-nav-row:hover { background: color-mix(in srgb, var(--ob-ink) 4%, transparent); }
.ob-nav-row.on { font-weight: 600; }
.ob-nav-row.on::before {
  content: "";
  position: absolute;
  left: -10px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 18px;
  border-radius: 2px;
  background: var(--ob-accent);
}
.ob-nav-row .meta {
  font-family: var(--ob-font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
}
.ob-nav-row .meta.dot-active {
  color: var(--ob-ink);
  background: color-mix(in srgb, var(--ob-accent) 22%, transparent);
  padding: 2px 8px;
  border-radius: 999px;
}
.ob-nav-admin { color: var(--ob-ink-muted); }
.ob-sidenav-spacer { flex: 1; min-height: 28px; }
.ob-section-head { margin: 18px 0 8px; }
.ob-sidenav-friends { margin-bottom: 12px; }
.ob-you-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  text-decoration: none;
  color: var(--ob-ink);
}
.ob-you-card img,
.ob-you-card .avatar-placeholder {
  width: 32px; height: 32px; border-radius: 50%;
  flex: 0 0 32px;
  display: inline-grid; place-items: center;
  background: var(--ob-bg); border: 0.5px solid var(--ob-rule);
  font-family: var(--ob-font-display); font-weight: 600;
  object-fit: cover;
  overflow: hidden;
}
.ob-you-card div { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.ob-you-card strong { font-size: 13px; }
.ob-you-card small { font-size: 10px; }

.ob-signout-form { margin: 6px 0 0; }
.ob-signout-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  border-radius: 8px;
  background: transparent;
  border: 0.5px solid transparent;
  color: var(--ob-muted);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  text-align: left;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.ob-signout-btn:hover,
.ob-signout-btn:focus-visible {
  background: var(--ob-surface-2);
  border-color: var(--ob-rule);
  color: var(--ob-ink);
  outline: none;
}
.ob-signout-btn svg { flex: 0 0 14px; opacity: 0.75; }
.ob-signout-btn:hover svg { opacity: 1; }

/* ── Center column ── */
.ob-center {
  padding: 30px 56px 80px;
  min-width: 0;
  max-width: 100%;
}
@media (min-width: 1440px) {
  .ob-center { padding: 30px 72px 80px; }
}

/* ── RightRail ── § 2.5 ── */
.ob-rightrail {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 24px 22px 24px 36px; /* extra left padding for the toggle */
  border-left: 0.5px solid var(--ob-rule);
  background: color-mix(in srgb, var(--ob-bg) 96%, var(--ob-ink));
  transition: padding 0.18s ease;
}
/* Collapse handle — sits on the rail's left edge always-visible. */
.ob-rightrail-toggle {
  position: absolute;
  left: 0;
  top: 16px;
  width: 28px;
  min-height: 80px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  border-left: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  border-top-left-radius: var(--ob-radius-sm);
  border-bottom-left-radius: var(--ob-radius-sm);
  cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px;
  padding: 8px 0;
  color: var(--ob-ink-muted);
  font-size: 14px;
  z-index: 2;
  transition: background 0.15s ease, color 0.15s ease;
}
.ob-rightrail-toggle:hover,
.ob-rightrail-toggle:focus-visible {
  background: var(--ob-surface);
  color: var(--ob-ink);
  outline: none;
}
.ob-rightrail-toggle-icon { font-size: 18px; line-height: 1; font-weight: 600; }
.ob-rightrail-toggle-label {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.ob-rightrail-content { transition: opacity 0.15s ease; }

/* Collapsed state — content hidden, only the toggle handle is visible. */
.rightrail-collapsed .ob-rightrail {
  padding: 24px 0 24px 0;
  overflow: hidden;
}
.rightrail-collapsed .ob-rightrail-content {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}
.rightrail-collapsed .ob-rightrail-toggle {
  /* Match the 36px collapsed-column width — handle now fills the cell. */
  width: 36px;
  border: 0;
  background: transparent;
}
.rightrail-collapsed .ob-rightrail-toggle:hover,
.rightrail-collapsed .ob-rightrail-toggle:focus-visible {
  background: var(--ob-surface-2);
}
.rr-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 14px; }
.rr-head .display { font-family: var(--ob-font-display); font-weight: 600; font-size: 22px; margin: 0; }
.rr-empty { padding: 24px 0; opacity: 0.7; }
.rr-section { margin-bottom: 22px; }
.rr-section-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.rr-section-head .name { font-family: var(--ob-font-display); font-weight: 600; font-size: 14px; }
.rr-section-head .meta { font-family: var(--ob-font-mono); font-size: 10px; color: var(--ob-ink-muted); letter-spacing: 0.08em; text-transform: uppercase; }

/* Binding chip — tiny abbreviated indicator on book-list rows
   (PB / HC / MM / 🎧 / EB / SB / LB). Shows the physical format at
   a glance so a borrower can avoid hauling a 600-page hardcover when
   they wanted the paperback. Empty chip when no binding data. */
.binding-chip {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  border-radius: 999px;
  font-family: var(--ob-font-mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  color: var(--ob-ink-muted);
  vertical-align: 1px;
}

/* ── Bookshelf — right-rail visual library ─────────────────────────────
   Cover-forward tiles arranged in shelf rows, each row terminated by a
   wood-tone "shelf board" element so the viewer reads it as a real
   bookshelf. Data is fetched async via /api/me/shelf; this CSS only
   describes the visual chrome.

   Layout: 4 covers per row at the rail's 360px (minus padding ≈ 320px
   usable). Each cover ≈ 72×108 (3:4-ish, slightly squashed for
   density). Two filter dropdowns sit above the shelves; a status line
   between shows "X of Y" or filter-empty messages.
*/
.ob-bookshelf {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.ob-bookshelf-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 4px;
}
.ob-bookshelf-head .display {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 22px;
  margin: 0;
}
.ob-bookshelf-filters {
  display: flex;
  gap: 8px;
}
.ob-bookshelf-filters select {
  flex: 1;
  min-width: 0;
  padding: 6px 8px;
  font-size: 12px;
  border: 0.5px solid var(--ob-rule);
  border-radius: 6px;
  background: var(--ob-surface);
  color: var(--ob-ink);
  font-family: var(--ob-font-mono);
}
.ob-bookshelf-filters select:focus-visible {
  border-color: var(--ob-accent);
  outline: none;
}
.ob-bookshelf-status {
  font-size: 10.5px;
  color: var(--ob-ink-muted);
  letter-spacing: 0.04em;
}
.ob-bookshelf-empty,
.ob-bookshelf-no-match {
  padding: 24px 4px;
  opacity: 0.7;
  font-size: 12px;
  line-height: 1.5;
}
.ob-bookshelf-empty a {
  color: var(--ob-accent);
}

/* A single shelf row + its wood board underneath. The negative margin
   on the board lifts it slightly INTO the row so the books appear to
   rest on it (the board's top edge sits beneath the bottom of each
   tile). Without that overlap the books float above an unrelated bar. */
.ob-bookshelf-rows {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.ob-shelf-row {
  display: grid;
  /* Container-driven column count: each tile is 52–66 px wide, the
     grid figures out how many fit. At a 360 px right-rail width
     (minus padding), this lands at 5 columns of ~58 px → cover height
     ~87 px (3:2 aspect). 4 rows ≈ 350 px of shelf. The previous
     `repeat(4, 1fr)` produced 75 × 113 px tiles which dominated the
     viewport. */
  grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));
  gap: 5px;
  padding: 0 2px 2px;
  align-items: end;
  min-height: 0;
}
/* The wood-grain shelf board. Multi-stop gradient gives the wood look
   without an image asset. Subtle drop-shadow underneath suggests the
   shelf casts a shadow on the wall behind it (depth cue). */
.ob-shelf-board {
  height: 8px;
  margin: -2px 0 14px;
  border-radius: 1px;
  background:
    /* Faint vertical grain — narrow dark stripes layered over the
       horizontal wood-tone gradient. */
    repeating-linear-gradient(90deg,
      rgba(0, 0, 0, 0.06) 0px,
      rgba(0, 0, 0, 0.06) 1px,
      transparent 1px,
      transparent 9px),
    /* Horizontal wood-tone fill. The slight asymmetry of the stops
       breaks the look of a flat colour and reads as "real". */
    linear-gradient(180deg,
      #8b6635 0%,
      #6f4f28 45%,
      #5a3f20 100%);
  /* Inset highlight at top suggests the shelf surface catching light;
     drop-shadow below fades for depth. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.2),
    inset 0 -1px 0 rgba(0, 0, 0, 0.35),
    0 4px 6px -3px rgba(0, 0, 0, 0.4),
    0 1px 2px rgba(0, 0, 0, 0.25);
}
[data-theme='night'] .ob-shelf-board {
  background:
    repeating-linear-gradient(90deg,
      rgba(0, 0, 0, 0.18) 0px,
      rgba(0, 0, 0, 0.18) 1px,
      transparent 1px,
      transparent 9px),
    linear-gradient(180deg, #5e4220 0%, #4a3318 45%, #3a280f 100%);
}

/* ── Cover tile ──────────────────────────────────────────────────────
   Each cover is an <a> wrapping an <img> (or a fallback span when the
   book has no cover photo). Aspect-ratio 3:4 keeps proportions sane
   even with arbitrary cover images. Box-shadow gives the book some
   depth — different on top vs bottom edge so it reads like a 3D
   object resting on the shelf. */
.ob-shelf-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 2 / 3;
  width: 100%;
  background: var(--ob-surface-2);
  border-radius: 2px;
  overflow: hidden;
  text-decoration: none;
  color: var(--ob-ink);
  position: relative;
  /* Two-axis shadow: top-right edge sharp, bottom-left edge softer.
     Gives the book a subtle 3D presence without being a literal 3D
     transform. The 2px y-offset means it casts onto the shelf. */
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.35),
    0 1px 0 rgba(0, 0, 0, 0.2),
    inset 1px 0 0 rgba(255, 255, 255, 0.05),
    inset -1px 0 0 rgba(0, 0, 0, 0.18);
  transition: transform 180ms cubic-bezier(0.2, 0.8, 0.25, 1),
              box-shadow 180ms ease;
}
.ob-shelf-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.ob-shelf-tile-fallback {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 22px;
  color: var(--ob-ink-muted);
}
/* Hover: book lifts off the shelf and rotates very slightly forward.
   The lift is small (3px) so it's an inviting cue rather than a
   dramatic motion. transform-origin at the bottom keeps the book's
   spine "rooted" to the shelf board even as the top tilts forward. */
.ob-shelf-tile:hover,
.ob-shelf-tile:focus-visible {
  transform: translateY(-3px) rotate(-0.5deg);
  transform-origin: bottom center;
  box-shadow:
    0 6px 12px rgba(0, 0, 0, 0.4),
    0 2px 0 rgba(0, 0, 0, 0.2),
    inset 1px 0 0 rgba(255, 255, 255, 0.05),
    inset -1px 0 0 rgba(0, 0, 0, 0.18);
  outline: none;
  z-index: 2;
}
/* Empty placeholders pad incomplete final rows so the wood board
   spans the full width — they take a tile's width but render
   nothing visible. Keep the height-by-aspect-ratio rule from the
   parent so the row's height stays consistent. */
.ob-shelf-tile-empty {
  background: transparent;
  box-shadow: none;
  pointer-events: none;
}
/* Subtle organic-touch: every 5th, 7th, 11th tile gets a microtilt
   so the row doesn't look like a perfectly-machined block. Tiny
   enough that it reads as "real books" not "broken layout".
   Disabled at hover so the hover transform takes precedence cleanly. */
.ob-shelf-tile:nth-child(5n+2):not(:hover) { transform: rotate(-0.3deg); }
.ob-shelf-tile:nth-child(7n+3):not(:hover) { transform: rotate(0.3deg); }
.ob-shelf-tile:nth-child(11n+5):not(:hover) { transform: rotate(-0.5deg); }

/* Hero face-out card (newest book) */
.rr-hero {
  display: flex;
  gap: 14px;
  padding: 12px;
  border: 0.5px solid var(--ob-rule);
  border-radius: 12px;
  background: var(--ob-surface);
  margin-bottom: 22px;
}
.rr-hero img,
.rr-hero .ob-cover-placeholder {
  width: 80px; height: 116px;
  flex: 0 0 80px;
  border-radius: 4px;
  object-fit: cover;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  display: grid; place-items: center;
  font-family: var(--ob-font-display); font-weight: 600;
  color: var(--ob-ink-muted);
}
.rr-hero .meta { font-family: var(--ob-font-mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ob-ink-muted); }
.rr-hero .title { font-family: var(--ob-font-display); font-weight: 600; font-size: 15px; margin: 4px 0 2px; line-height: 1.3; }
.rr-hero .author { font-size: 12px; color: var(--ob-ink-muted); margin-bottom: 8px; }

/* ── Shelf rail (desktop & mobile) — § 4.3 ── */
.ob-shelf {
  position: relative;
  margin-bottom: 22px;
}
.ob-shelf-spines {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  min-height: 132px;
  padding-top: 14px;
}
.ob-spine {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  flex: 0 0 auto;
  height: 132px;
  min-width: 28px;
  background: var(--ob-spine-bg, var(--ob-surface-2));
  color: var(--ob-spine-fg, var(--ob-ink));
  border: 0.5px solid var(--ob-rule);
  border-radius: 1px;
  cursor: pointer;
  text-decoration: none;
  overflow: hidden;
}
.ob-spine .ob-spine-title {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  padding: 12px 0;
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: 100%;
}
.ob-spine .ob-spine-author {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  padding: 8px 0;
  font-family: var(--ob-font-mono);
  font-size: 8px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  opacity: 0.8;
}
.ob-spine[data-lent="1"]::after {
  content: "LENT";
  position: absolute;
  top: 8px; right: 2px;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--ob-font-mono);
  font-size: 7px;
  letter-spacing: 0.20em;
  color: rgba(255,255,255,0.85);
  background: rgba(0,0,0,0.4);
  padding: 6px 2px;
  border-radius: 2px;
}
.ob-spine .ob-spine-owner {
  position: absolute;
  top: -8px; left: 50%;
  transform: translateX(-50%);
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--ob-bg);
  border: 1.5px solid var(--ob-bg);
  box-shadow: 0 0 0 0.5px var(--ob-rule);
  font-size: 8px;
  display: grid; place-items: center;
  color: var(--ob-ink);
  font-family: var(--ob-font-display);
  font-weight: 600;
}
.ob-shelf-board {
  height: 6px;
  background: linear-gradient(180deg,
    rgba(14,14,12,0.18) 0%,
    rgba(14,14,12,0.10) 50%,
    rgba(0,0,0,0.04) 100%);
  border-radius: 0 0 2px 2px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.10), 0 1px 0 rgba(0,0,0,0.10);
}
[data-theme="night"] .ob-shelf-board {
  background: linear-gradient(180deg, rgba(244,237,227,0.08) 0%, rgba(244,237,227,0.04) 100%);
}
.ob-shelf-rule { height: 1px; background: var(--ob-rule); margin-top: 0; }

/* Hover preview (desktop) */
.ob-shelf-preview {
  display: none;
  margin-top: 12px;
  padding: 10px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: 10px;
  align-items: center;
  gap: 12px;
}
.ob-shelf-preview.on { display: flex; }
.ob-shelf-preview img,
.ob-shelf-preview .ph {
  width: 48px; height: 70px;
  background: var(--ob-surface-2);
  border-radius: 3px;
  border: 0.5px solid var(--ob-rule);
}

/* Mobile inline-expand panel — § 4.2 */
.ob-shelf-expand {
  margin-top: 10px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: 12px;
  padding: 14px;
  display: none;
  animation: ob-slide-down 200ms ease-out;
}
.ob-shelf-expand.on { display: flex; gap: 14px; }
@keyframes ob-slide-down {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

/* Epigraph card */
.ob-epigraph {
  border: 1px dashed var(--ob-rule-strong);
  border-radius: 10px;
  padding: 16px;
  font-family: var(--ob-font-display);
  font-style: italic;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ob-ink-muted);
  margin-top: 18px;
}

/* ── Mobile shell — § 2.3 ──
   These rules need higher selector specificity than .ob-app-shell.rightrail-collapsed
   (which forces a 3-column grid for desktop right-rail collapse). Without the
   doubled selector, the collapsed-grid columns leak through to mobile and
   reserve empty space on either side of the main content. */
@media (max-width: 1023px) {
  .ob-app-shell,
  .ob-app-shell.rightrail-collapsed {
    grid-template-columns: 240px minmax(0, 1fr);
    grid-template-areas: "sidenav center";
  }
  .ob-rightrail { display: none; }
  .ob-center { padding: 28px 28px 80px; }
}
@media (max-width: 767px) {
  .ob-app-shell,
  .ob-app-shell.rightrail-collapsed {
    grid-template-columns: 1fr;
    grid-template-areas: "center";
  }
  .ob-sidenav { display: none; }
  .ob-mobile-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 22px;
    height: 60px;
    border-bottom: 0.5px solid var(--ob-rule);
    background: var(--ob-bg);
    position: sticky; top: 0; z-index: 20;
  }
  .ob-mobile-actions { display: flex; gap: 14px; align-items: center; }
  .ob-mobile-actions a { color: var(--ob-ink); text-decoration: none; position: relative; padding: 6px; }
  .ob-bell-dot {
    position: absolute;
    top: 4px; right: 4px;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--ob-accent);
  }
  .ob-bell-count {
    position: absolute;
    top: -2px; right: -2px;
    min-width: 16px; height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--ob-accent);
    color: var(--ob-accent-ink);
    font-family: var(--ob-font-mono);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0;
    line-height: 16px;
    text-align: center;
    box-shadow: 0 0 0 2px var(--ob-bg);
  }
  .ob-center { padding: 18px 22px 120px; }
  /* Mobile shell: re-enable the profile-page sign-out button (hidden on
     desktop where the sidebar already exposes one). */
  .profile-signout-mobile { display: inline-block; }

  .ob-tabbar {
    display: flex;
    position: fixed;
    left: 14px; right: 14px; bottom: 14px;
    padding: 8px 6px;
    background: color-mix(in srgb, var(--ob-surface) 88%, transparent);
    backdrop-filter: blur(18px) saturate(160%);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
    border: 0.5px solid var(--ob-rule);
    border-radius: 22px;
    box-shadow: var(--ob-shadow-md);
    z-index: 30;
    padding-bottom: max(8px, env(safe-area-inset-bottom));
  }
  .ob-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 6px 4px 4px;
    color: var(--ob-ink-muted);
    text-decoration: none;
    font-size: 10px;
    border-bottom: 0;
    position: relative;
  }
  .ob-tab svg { stroke: currentColor; }
  .ob-tab.on { color: var(--ob-ink); }
  .ob-tab.on svg { color: var(--ob-accent); }
  .ob-tab.on::after {
    content: "";
    position: absolute;
    bottom: -2px; left: 50%;
    transform: translateX(-50%);
    width: 4px; height: 4px;
    border-radius: 50%;
    background: var(--ob-accent);
  }
}

/* ════════ ADMIN SHELL ═════════════════════════════════════════════════════ */
.ob-admin-shell {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  background: var(--ob-bg);
  min-height: 100vh;
}
.ob-sidenav-admin {
  border-right: 0.5px solid var(--ob-rule);
  background: color-mix(in srgb, var(--ob-bg) 94%, var(--ob-ink));
}
.ob-admin-main {
  padding: 24px 40px 60px;
  min-width: 0;
}
.ob-admin-main h1 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 28px;
  letter-spacing: -0.01em;
  margin: 0 0 4px;
}
@media (max-width: 767px) {
  .ob-admin-shell { grid-template-columns: 1fr; }
  .ob-admin-shell .ob-sidenav-admin { display: none; }
  .ob-admin-main { padding: 18px 22px 60px; }
}

/* Admin tables — § 11.2 */
.ob-admin-main .data-table,
.ob-admin-main table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  margin-top: 8px;
}
.ob-admin-main thead th {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  text-align: left;
  height: 32px;
  padding: 0 12px;
  border-bottom: 0.5px solid var(--ob-rule);
  font-weight: 500;
}
.ob-admin-main tbody td {
  height: 48px;
  padding: 0 12px;
  border-bottom: 0.5px solid var(--ob-rule);
  vertical-align: middle;
}
.ob-admin-main tbody tr:hover {
  background: color-mix(in srgb, var(--ob-ink) 4%, transparent);
  cursor: pointer;
}
.ob-admin-main td.num,
.ob-admin-main th.num { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 767px) {
  .ob-admin-main table thead { display: none; }
  .ob-admin-main table, .ob-admin-main tbody, .ob-admin-main tr, .ob-admin-main td { display: block; }
  .ob-admin-main tbody tr {
    background: var(--ob-surface);
    border: 0.5px solid var(--ob-rule);
    border-radius: 10px;
    margin-bottom: 10px;
    padding: 12px;
  }
  .ob-admin-main tbody td { height: auto; padding: 4px 0; border-bottom: 0; display: flex; justify-content: space-between; }
  .ob-admin-main tbody td::before {
    content: attr(data-label);
    font-family: var(--ob-font-mono);
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ob-ink-muted);
    margin-right: 12px;
  }
}

/* ════════ AUTH SHELL ═════════════════════════════════════════════════════ */
.ob-auth-shell {
  position: relative;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 32px 24px;
  background: var(--ob-bg);
}
.ob-auth-bg {
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(circle at 20% 10%, rgba(201,169,97,0.10) 0%, transparent 40%),
                    radial-gradient(circle at 80% 80%, rgba(139,58,46,0.08) 0%, transparent 40%);
  z-index: 0;
}
.ob-auth-main { position: relative; z-index: 1; width: 100%; max-width: 380px; }
.ob-auth-card {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: 14px;
  padding: 28px;
  box-shadow: var(--ob-shadow-md);
}
.ob-auth-card .ob-wordmark { font-size: 22px; margin-bottom: 4px; }
.ob-auth-card h1 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 26px;
  margin: 16px 0 6px;
  letter-spacing: -0.01em;
}
.ob-auth-card .subline { color: var(--ob-ink-muted); font-size: 13px; margin-bottom: 20px; }
.ob-auth-or {
  display: flex; align-items: center; gap: 10px;
  margin: 16px 0;
  font-family: var(--ob-font-mono);
  font-size: 10px; letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--ob-ink-muted);
}
.ob-auth-or::before, .ob-auth-or::after {
  content: ""; flex: 1; height: 1px; background: var(--ob-rule);
}
.ob-auth-footer { text-align: center; margin-top: 16px; font-size: 13px; }

/* ── OAuth provider buttons (Google / Apple) ─────────────────────────────
   Used at the top of /login and /signup. The Google button follows
   Google's brand guidelines: white background, 1px gray border, 18px
   multi-colour "G" mark on the left, label in a system font close to
   Roboto. The Apple button mirrors Apple's branded sign-in: black
   background, white logo, white label. Both stretch full-width like the
   primary submit button so they read as the dominant call-to-action.

   Dark-mode variants below: the Google button stays white-on-light
   intentionally — Google's brand spec discourages a dark variant for
   "Sign in with Google" because users associate the colour with their
   muscle memory; the Apple button stays black-on-dark which IS the
   Apple-on-dark variant. */
.ob-auth-providers {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 4px;
}

.btn-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  min-height: 44px;
  padding: 10px 16px;
  background: #fff;
  color: #1f1f1f;
  border: 1px solid #dadce0;
  border-radius: 8px;
  font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.25px;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 150ms ease,
              box-shadow 150ms ease,
              border-color 150ms ease;
}
.btn-google:hover {
  background: #f8f9fa;
  border-color: #d2e3fc;
  box-shadow: 0 1px 3px rgba(60, 64, 67, 0.15);
  color: #1f1f1f;
}
.btn-google:active {
  background: #f1f3f4;
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.2);
}
.btn-google:focus-visible {
  outline: 2px solid #4285F4;
  outline-offset: 2px;
}
.btn-google-icon {
  display: inline-flex;
  width: 18px; height: 18px;
  flex: 0 0 18px;
}
.btn-google-label {
  font-weight: 500;
}

.btn-apple {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 10px 16px;
  background: #000;
  color: #fff;
  border: 1px solid #000;
  border-radius: 8px;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', sans-serif;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 150ms ease, box-shadow 150ms ease;
}
.btn-apple:hover {
  background: #1a1a1a;
  color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
.btn-apple:active {
  background: #2a2a2a;
}
.btn-apple:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
.btn-apple-icon {
  display: inline-flex;
  width: 16px; height: 20px;
  flex: 0 0 16px;
  margin-bottom: 2px;
}

/* ── PWA install instructions modal (home page) ──────────────────────────
   Triggered by the "Get the App" button in the hero CTA row when the
   browser doesn't expose `beforeinstallprompt` (i.e. iOS Safari, iOS
   Chrome, etc.). On Android Chrome the JS skips this modal and fires
   the native install dialog directly.

   The "Get the App" button itself uses the existing .btn .btn-ghost
   styling — no extra rules needed. The icon-and-label spacing is handled
   inline in the markup. */
.install-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: flex-end;  /* mobile: rise from the bottom */
  justify-content: center;
  z-index: 10000;
  padding: 0;
  animation: install-modal-fade 200ms ease-out;
}
.install-modal[hidden] { display: none; }
@keyframes install-modal-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
.install-modal-card {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: 18px 18px 0 0;
  padding: 28px 22px 26px;
  max-width: 460px;
  width: 100%;
  position: relative;
  box-shadow: 0 -10px 60px rgba(0, 0, 0, 0.4);
  animation: install-modal-slide 280ms cubic-bezier(.2, .8, .25, 1);
  /* iOS safe-area: respect the home indicator strip on notched phones. */
  padding-bottom: max(26px, env(safe-area-inset-bottom));
}
@keyframes install-modal-slide {
  from { transform: translateY(24px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
.install-modal-close {
  position: absolute;
  top: 10px; right: 10px;
  width: 36px; height: 36px;
  border: 0;
  background: transparent;
  color: var(--ob-ink-muted);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.install-modal-close:hover {
  background: var(--ob-surface-2);
  color: var(--ob-ink);
}
.install-modal-icon {
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--ob-accent) 14%, transparent);
  color: var(--ob-accent);
  border-radius: 14px;
  margin-bottom: 12px;
}
.install-modal-card h2 {
  font-size: 22px;
  margin: 0 0 6px;
  padding-right: 32px;  /* keep clear of the × button */
  color: var(--ob-ink);
}
.install-modal-sub {
  color: var(--ob-ink-muted);
  font-size: 14px;
  line-height: 1.5;
  margin-bottom: 20px;
}
.install-modal-sub strong {
  color: var(--ob-ink);
  font-weight: 600;
}
.install-steps {
  list-style: none;
  counter-reset: install-step;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.install-steps li {
  position: relative;
  padding: 13px 14px 13px 46px;
  background: var(--ob-surface-2);
  border-radius: 10px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--ob-ink);
  counter-increment: install-step;
}
.install-steps li::before {
  content: counter(install-step);
  position: absolute;
  left: 12px; top: 50%;
  transform: translateY(-50%);
  width: 24px; height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ob-accent);
  color: #fff;
  border-radius: 50%;
  font-weight: 700;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.install-steps li strong {
  font-weight: 600;
  color: var(--ob-ink);
}
.install-steps li small {
  display: block;
  color: var(--ob-ink-muted);
  font-size: 12px;
  margin-top: 3px;
}
.install-steps-paragraph {
  background: var(--ob-surface-2);
  border-radius: 10px;
  padding: 14px 16px;
  font-size: 14px;
  color: var(--ob-ink);
  line-height: 1.5;
}
.install-steps-paragraph p { margin: 0; }
.install-steps-paragraph strong { font-weight: 600; }

/* When the modal is open, lock body scroll. */
body.install-modal-open { overflow: hidden; }

/* Tablet+: float the card centred instead of sliding from the bottom. */
@media (min-width: 600px) {
  .install-modal {
    align-items: center;
    padding: 24px;
  }
  .install-modal-card {
    border-radius: 16px;
    padding-bottom: 26px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .install-modal,
  .install-modal-card { animation: none; }
}

/* ════════ MARKETING SHELL ═════════════════════════════════════════════════ */
.ob-marketing-shell { background: var(--ob-bg); min-height: 100vh; }
.ob-marketing-nav {
  position: sticky;
  top: 0; z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 14px max(24px, calc((100vw - 1180px) / 2));
  background: color-mix(in srgb, var(--ob-bg) 88%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 0.5px solid var(--ob-rule);
}
.ob-marketing-links { display: flex; gap: 22px; }
.ob-marketing-links a {
  color: var(--ob-ink);
  text-decoration: none;
  font-size: 14px;
  border-bottom: 0;
}
.ob-marketing-links a:hover { color: var(--ob-accent); }
.ob-marketing-cta { display: flex; gap: 10px; }
.ob-marketing-main {
  padding: 0 max(24px, calc((100vw - 1180px) / 2)) 80px;
}
.ob-marketing-footer {
  border-top: 0.5px solid var(--ob-rule);
  padding: 40px max(24px, calc((100vw - 1180px) / 2));
  background: color-mix(in srgb, var(--ob-bg) 96%, var(--ob-ink));
}
.ob-marketing-footer-cols {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: 32px;
}
.ob-marketing-footer h4 {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin: 0 0 10px;
  font-weight: 500;
}
.ob-marketing-footer a {
  display: block;
  color: var(--ob-ink);
  text-decoration: none;
  padding: 4px 0;
  font-size: 13px;
  border-bottom: 0;
}
@media (max-width: 767px) {
  .ob-marketing-links { display: none; }
  .ob-marketing-footer-cols { grid-template-columns: 1fr; gap: 24px; }
}

/* Marketing landing hero */
.ob-hero {
  min-height: 80vh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 60px 0 40px;
}
.ob-hero h1 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: clamp(38px, 6vw, 60px);
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0 0 18px;
  max-width: 880px;
}
.ob-hero h1 em {
  font-style: italic;
  color: var(--ob-accent);
  font-weight: 600;
}
.ob-hero .subline {
  font-family: var(--ob-font-mono);
  font-size: 12px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  max-width: 520px;
  margin: 0 auto 28px;
}
.ob-feature-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  align-items: center;
  padding: 80px 0;
  border-top: 0.5px solid var(--ob-rule);
}
.ob-feature-row.alt { direction: rtl; }
.ob-feature-row.alt > * { direction: ltr; }
.ob-feature-row h2 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 36px;
  letter-spacing: -0.01em;
  margin: 0 0 12px;
}
.ob-feature-row p { font-size: 16px; line-height: 1.6; color: var(--ob-ink-muted); max-width: 460px; }
@media (max-width: 767px) {
  .ob-feature-row { grid-template-columns: 1fr; gap: 24px; padding: 48px 0; }
  .ob-feature-row.alt { direction: ltr; }
}

/* ════════ STREAM FEED — § 3 ═══════════════════════════════════════════════ */
.ob-stream-head {
  margin-bottom: 28px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 18px;
}
.ob-stream-head .eyebrow {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin: 0 0 8px;
}
.ob-stream-head h1 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: clamp(28px, 4vw, 48px);
  letter-spacing: -0.015em;
  margin: 0;
  line-height: 1.1;
}
.ob-stream-head h1 em {
  color: var(--ob-accent);
  font-style: italic;
  font-weight: 600;
}
.ob-stream-head .brand-dot { color: var(--ob-accent); }

.ob-filter-chips { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.ob-chip {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  color: var(--ob-ink-muted);
  text-decoration: none;
  border-bottom: 0;
}
.ob-chip:hover { color: var(--ob-ink); border-color: var(--ob-rule-strong); }
.ob-chip.on { background: var(--ob-ink); color: var(--ob-bg); border-color: var(--ob-ink); }

.ob-feed-list {
  max-width: 760px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.ob-feed-card {
  display: grid;
  grid-template-columns: 150px 1fr;
  gap: 26px;
  padding-bottom: 28px;
  border-bottom: 0.5px solid var(--ob-rule);
}
.ob-feed-card:last-child { border-bottom: 0; padding-bottom: 0; }
.ob-feed-card .friend-strip {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 8px;
  font-size: 13px;
  color: var(--ob-ink-muted);
}
.ob-feed-card .friend-strip img,
.ob-feed-card .friend-strip .avatar-placeholder {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  display: grid; place-items: center;
  font-family: var(--ob-font-display); font-weight: 600;
  font-size: 12px;
  object-fit: cover;
  overflow: hidden;
}
.ob-feed-card .friend-strip strong { color: var(--ob-ink); font-weight: 600; }
.ob-feed-card .ob-cover {
  width: 150px; height: 225px;
  border-radius: 4px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
  display: grid; place-items: center;
  font-family: var(--ob-font-display); font-weight: 600;
  color: var(--ob-ink-muted); font-size: 32px;
  object-fit: cover;
  overflow: hidden;
  box-shadow: var(--ob-shadow-sm);
}
.ob-feed-card h2 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 26px;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0 0 4px;
}
.ob-feed-card .meta-line { color: var(--ob-ink-muted); font-size: 13px; margin-bottom: 12px; }
/* 20-word description teaser shown under added-book feed cards.
   Sits between the meta-line (author) and the action row. Constrained
   line-height + max-width keep it from competing with the title. */
.ob-feed-card .ob-feed-excerpt {
  color: var(--ob-ink-muted);
  font-size: 13px;
  line-height: 1.5;
  margin: -6px 0 12px;
  max-width: 580px;
  overflow-wrap: break-word;
}
.ob-feed-card .note {
  font-family: var(--ob-font-display);
  font-style: italic;
  font-size: 15px;
  line-height: 1.5;
  max-width: 580px;
  margin: 12px 0 14px;
  color: var(--ob-ink);
}
.ob-feed-card .action-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 8px;
}
.ob-feed-card .action-counts {
  display: flex; gap: 14px;
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-mono);
  font-size: 11px;
}
.ob-feed-cover-link { display: block; border-bottom: 0; transition: transform .15s; }
.ob-feed-cover-link:hover { transform: translateY(-2px); }
.ob-feed-card h2 a { color: var(--ob-ink); border-bottom: 0; }
.ob-feed-card h2 a:hover { color: var(--ob-accent); }
.ob-feed-card .friend-strip a { color: var(--ob-ink-muted); border-bottom: 0; }
.ob-feed-card .friend-strip a:hover strong { color: var(--ob-accent); }
.ob-feed-card .friend-strip strong { color: var(--ob-ink); }
.ob-feed-card .friend-avatar-link { display: inline-flex; flex: 0 0 28px; }
.ob-feed-card .ob-time {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--ob-ink-muted);
  text-transform: lowercase;
}
.ob-feed-card-simple { grid-template-columns: 1fr; gap: 0; }

/* Borrow page covers */
.borrow-meta { display: flex; gap: 14px; align-items: flex-start; }
.borrow-cover-link { flex: 0 0 48px; border-bottom: 0; }
.borrow-thumb {
  width: 48px; height: 70px;
  object-fit: cover;
  border-radius: 3px;
  border: 0.5px solid var(--ob-rule);
  background: var(--ob-surface-2);
  display: block;
}
.borrow-meta > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.borrow-meta strong a { color: var(--ob-ink); border-bottom: 0; }
.borrow-meta strong a:hover { color: var(--ob-accent); }

.ob-home-section { margin-bottom: 36px; scroll-margin-top: 80px; }
.ob-home-section > h2.mono-meta { margin-bottom: 12px; }
.ob-stream-section { padding-top: 18px; border-top: 0.5px solid var(--ob-rule); }

/* ── Compact home tiles ─────────────────────────────────────────────────── */
/* Reduce stat-cards + lib-cards on the dashboard only, leaving admin and
   library-list pages at their original size. */
.ob-home-section .stat-row {
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin: 0 0 6px;
}
/* Mid-width tablet: 5 tiles get cramped, drop to a 3+2 grid which
   still shows everything above the fold without horizontal scroll. */
@media (max-width: 640px) {
  .ob-home-section .stat-row { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 420px) {
  .ob-home-section .stat-row { grid-template-columns: repeat(2, 1fr); }
}
.ob-home-section .stat-card {
  padding: 10px 8px;
  border-radius: var(--ob-radius-sm);
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
}
.ob-home-section .stat-card .stat-num { font-size: 18px; line-height: 1.1; }
.ob-home-section .stat-card .stat-label { font-size: 9px; margin-top: 2px; }

.ob-home-section .card-grid {
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 8px;
}
.ob-home-section .lib-card {
  padding: 10px 12px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  background: var(--ob-surface);
}
.ob-home-section .lib-card h3 {
  font-size: 14px;
  margin: 2px 0;
  line-height: 1.25;
  font-weight: 600;
}
.ob-home-section .lib-card .lib-card-meta { margin-bottom: 4px; gap: 4px; }
.ob-home-section .lib-card .lib-card-meta .pill { font-size: 9px; padding: 1px 6px; }
.ob-home-section .lib-card p.muted { font-size: 11px; margin: 0; }

/* shelf-kind feed card: stacked covers */
.ob-feed-shelf-stack {
  position: relative;
  width: 150px; height: 225px;
}
.ob-feed-shelf-stack img,
.ob-feed-shelf-stack .ph {
  position: absolute;
  width: 100px; height: 150px;
  border-radius: 3px;
  border: 0.5px solid var(--ob-rule);
  background: var(--ob-surface-2);
  box-shadow: var(--ob-shadow-sm);
  top: 30px;
  left: 0;
}
.ob-feed-shelf-stack img:nth-child(1) { transform: rotate(-3deg); left: 0; }
.ob-feed-shelf-stack img:nth-child(2) { transform: rotate(-1.5deg); left: 14px; }
.ob-feed-shelf-stack img:nth-child(3) { transform: rotate(0deg);    left: 28px; }
.ob-feed-shelf-stack img:nth-child(4) { transform: rotate(1.5deg);  left: 42px; }
.ob-feed-shelf-stack img:nth-child(5) { transform: rotate(3deg);    left: 56px; }

@media (max-width: 767px) {
  .ob-feed-card {
    grid-template-columns: 1fr;
    gap: 14px;
    padding-bottom: 36px;
  }
  .ob-feed-card .cover-row { display: flex; gap: 14px; align-items: flex-start; }
  .ob-feed-card .ob-cover { width: 132px; height: 198px; flex: 0 0 132px; font-size: 26px; }
  .ob-feed-card h2 { font-size: 22px; }
  .ob-feed-card .note { font-size: 14px; }
}

/* ════════ BOOK DETAIL — § 5 — Editorial Hero ═════════════════════════════
   Top-down anatomy:
     1. .ob-book-hero-wrap   — full-width tinted band hosting the hero
     2. .ob-book-hero        — cover (left) + title cluster (right)
     3. .ob-book-rail        — horizontal action pills below the hero
     4. .ob-book-panel       — collapsible borrow / QR drawers
     5. .ob-book-body        — synopsis prose + metadata sidebar
     6. .ob-book-activity    — quiet stat footer
     7. .ob-sticky-borrow    — mobile-only floating CTA (preserved)
   ─────────────────────────────────────────────────────────────────────── */

/* ── Hero wrap: tinted band with hairline rule. Bleeds horizontally
   to the inner content's max-width but stops short of the page
   gutters so it doesn't fight the global layout. */
.ob-book-hero-wrap {
  background: var(--ob-surface-2);
  border-radius: var(--ob-radius-lg);
  border: 0.5px solid var(--ob-rule);
  margin: 24px 0 0;
  overflow: hidden;
}

.ob-book-hero {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 40px;
  padding: 40px 44px;
  align-items: center;
  max-width: 1048px;
}

/* Cover treatment — paper-edge feel via a layered shadow. The thin
   inset hairline on the right edge hints at a spine. */
.ob-book-hero-cover { display: flex; justify-content: center; }
.ob-book-hero .ob-cover-hero {
  width: 240px;
  aspect-ratio: 2 / 3;
  border-radius: var(--ob-radius-sm);
  object-fit: cover;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  box-shadow:
    1px 0 0 0 rgba(0, 0, 0, 0.05) inset,
    var(--ob-shadow-md);
  display: block;
}
.ob-book-hero .ob-cover-hero-empty {
  display: grid; place-items: center;
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 64px;
  color: var(--ob-ink-muted);
}

/* Title cluster — eyebrow → title → byline → series → chips. */
.ob-book-hero-text { position: relative; min-width: 0; }

/* Status pill — top-right of the hero. Colour driven by data-state
   so we can swap palette without touching markup. */
.ob-book-status {
  position: absolute;
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: 999px;
  font-family: var(--ob-font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 0.5px solid var(--ob-rule);
  background: var(--ob-surface);
  color: var(--ob-ink);
  white-space: nowrap;
}
.ob-book-status-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  display: inline-block;
  background: var(--ob-ink-muted);
}
.ob-book-status[data-state="available"] .ob-book-status-dot { background: var(--ob-success); }
.ob-book-status[data-state="out"]         .ob-book-status-dot { background: var(--ob-warning); }
.ob-book-status[data-state="unavailable"] .ob-book-status-dot { background: var(--ob-danger); }
.ob-book-status[data-state="mine"]        .ob-book-status-dot { background: var(--ob-accent); }

.ob-book-eyebrow {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  color: var(--ob-ink-muted);
  margin: 0 0 14px;
  padding-right: 140px; /* space for the status pill */
}
.ob-book-eyebrow a {
  color: var(--ob-ink-muted);
  border-bottom: 0;
}
.ob-book-eyebrow a:hover { color: var(--ob-ink); }

.ob-book-hero-title {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 52px;
  letter-spacing: -0.02em;
  line-height: 1.02;
  margin: 0 0 10px;
  color: var(--ob-ink);
}

.ob-book-byline {
  font-family: var(--ob-font-display);
  font-style: italic;
  font-size: 20px;
  color: var(--ob-ink-muted);
  margin: 0 0 14px;
}

.ob-book-series {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  color: var(--ob-ink-muted);
  margin: 0 0 18px;
}
.ob-book-series-name {
  color: var(--ob-accent);
  font-weight: 600;
}

.ob-book-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 16px 0 0;
}

/* ── Action rail — horizontal, wraps. The borrow chip is filled
   primary; everything else is ghost. Forms inline-aligned via flex. */
.ob-book-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 18px 0 0;
  padding: 14px 0;
  border-bottom: 0.5px solid var(--ob-rule);
}
.ob-rail-form { display: inline-flex; margin: 0; }
.ob-rail-status select { min-width: 180px; }

/* ── Panels — collapsible drawers for borrow textarea + QR. Hidden
   by JS via [hidden]; when shown, slide into a soft surface card. */
.ob-book-panel {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  padding: 18px 20px;
  margin: 14px 0 0;
  max-width: 620px;
}
.ob-borrow-form { display: flex; flex-direction: column; gap: 12px; margin: 0; }
.ob-borrow-label { display: flex; flex-direction: column; gap: 6px; }
.ob-borrow-message {
  width: 100%;
  resize: vertical;
  min-height: 80px;
}
.ob-book-qr-panel { text-align: center; }
.ob-book-qr-panel .qr-img {
  display: inline-block;
  max-width: 220px;
  height: auto;
  background: #fff;
  padding: 10px;
  border-radius: var(--ob-radius-sm);
}

/* ── Body — synopsis prose (left) + metadata sidebar (right). */
.ob-book-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: 56px;
  margin: 36px 0 0;
  max-width: 1048px;
  align-items: start;
}

.ob-book-prose {
  min-width: 0;
}
.ob-book-synopsis {
  /* Body sans (Inter) to match the rest of the page. The display
     serif felt out-of-place against everything else; size + line-
     height are kept generous so the synopsis still reads like a
     dedicated reading block. */
  font-family: var(--ob-font-body);
  font-size: 16px;
  line-height: 1.65;
  color: var(--ob-ink);
  max-width: 65ch;
  margin: 0 0 20px;
}
.ob-book-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 24px 0 0;
}

/* Sidebar — soft-tinted card with a thin left rule. Sticky on
   tall viewports so it tracks the prose. */
.ob-book-aside {
  position: sticky;
  top: 24px;
  align-self: start;
  padding: 0 0 0 24px;
  border-left: 0.5px solid var(--ob-rule);
  min-width: 0;
}
.ob-aside-head {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin: 0 0 12px;
  padding: 0;
}
.ob-aside-head + .ob-book-meta-grid { margin-bottom: 24px; }

.ob-book-meta-grid {
  display: grid;
  grid-template-columns: 90px minmax(0, 1fr);
  column-gap: 14px;
  row-gap: 10px;
  margin: 0 0 8px;
}
.ob-book-meta-grid dt {
  font-family: var(--ob-font-mono);
  font-size: 10px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  padding-top: 2px;
}
.ob-book-meta-grid dd {
  margin: 0;
  color: var(--ob-ink);
  font-size: 13.5px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.ob-book-meta-grid dd.mono {
  font-family: var(--ob-font-mono);
  font-size: 12.5px;
}

/* ── Activity footer — quiet stat line at the bottom of the page. */
.ob-book-activity {
  margin: 36px 0 0;
  padding-top: 18px;
  border-top: 0.5px solid var(--ob-rule);
  color: var(--ob-ink-muted);
  font-size: 10.5px;
  letter-spacing: 0.12em;
}

/* ─────────────── RESPONSIVE COLLAPSE ─────────────── */
@media (max-width: 1023px) {
  .ob-book-hero {
    grid-template-columns: 1fr;
    gap: 28px;
    padding: 32px 28px;
    text-align: center;
  }
  .ob-book-hero-cover { justify-content: center; }
  .ob-book-hero .ob-cover-hero { width: 200px; }
  .ob-book-status { position: static; margin: 0 auto 16px; }
  .ob-book-eyebrow { padding-right: 0; }
  .ob-book-hero-title { font-size: 36px; }
  .ob-book-byline { font-size: 17px; }
  .ob-book-facts { justify-content: center; }

  .ob-book-body {
    grid-template-columns: 1fr;
    gap: 32px;
  }
  .ob-book-aside {
    position: static;
    padding: 24px 0 0;
    border-left: 0;
    border-top: 0.5px solid var(--ob-rule);
  }
}

@media (max-width: 640px) {
  .ob-book-hero { padding: 24px 18px; }
  .ob-book-hero-title { font-size: 28px; }
  .ob-book-byline { font-size: 15px; }
  .ob-book-synopsis { font-size: 15px; }
  .ob-book-meta-grid { grid-template-columns: 90px minmax(0, 1fr); }
}
.ob-sticky-borrow {
  display: none;
}
@media (max-width: 767px) {
  .ob-sticky-borrow {
    display: block;
    position: fixed;
    left: 14px; right: 14px; bottom: 88px;
    background: color-mix(in srgb, var(--ob-bg) 88%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    padding: 10px;
    border-radius: 14px;
    border: 0.5px solid var(--ob-rule);
    z-index: 25;
  }
  .ob-sticky-borrow .btn { width: 100%; }
}

/* ════════ BORROW FLOW — § 6 ═══════════════════════════════════════════════ */
.ob-borrow-overlay {
  position: fixed; inset: 0;
  background: var(--ob-overlay);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 24px;
}
.ob-borrow-modal {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: 18px;
  width: 460px; max-width: 100%;
  padding: 24px;
  box-shadow: var(--ob-shadow-lg);
}
@media (max-width: 767px) {
  .ob-borrow-overlay { padding: 0; align-items: end; }
  .ob-borrow-modal {
    width: 100%;
    border-radius: 22px 22px 0 0;
    max-height: 85vh;
    overflow-y: auto;
    animation: ob-sheet-up 240ms ease-out;
  }
  .ob-borrow-modal::before {
    content: "";
    display: block;
    width: 36px; height: 4px;
    background: var(--ob-rule-strong);
    border-radius: 2px;
    margin: 0 auto 14px;
  }
}
@keyframes ob-sheet-up {
  from { transform: translateY(100%); opacity: 0; }
  to { transform: none; opacity: 1; }
}
.ob-borrow-modal .eyebrow {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin-bottom: 14px;
}
.ob-borrow-message {
  margin: 16px 0;
  padding: 12px 14px;
  background: var(--ob-surface-2);
  border-radius: 10px;
  font-family: var(--ob-font-display);
  font-style: italic;
  font-size: 14px;
  line-height: 1.5;
  border: 0.5px solid var(--ob-rule);
  resize: vertical;
  width: 100%;
  min-height: 80px;
}
.ob-borrow-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }

/* sending state */
.ob-borrow-sending {
  position: relative;
  overflow: hidden;
}
.ob-borrow-sending::after {
  content: "";
  position: absolute;
  bottom: 0; left: 0; height: 2px; width: 0;
  background: var(--ob-accent);
  animation: ob-borrow-progress 800ms ease-out forwards;
}
@keyframes ob-borrow-progress { to { width: 100%; } }

/* Conversation thread — § 6.5 */
.ob-thread-pinned {
  position: sticky; top: 0;
  background: var(--ob-bg);
  z-index: 10;
  padding: 14px 0;
  border-bottom: 0.5px solid var(--ob-rule);
  display: flex; gap: 14px; align-items: center;
}
.ob-thread-list {
  max-width: 560px;
  margin: 24px auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ob-bubble {
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.45;
  max-width: 80%;
  word-wrap: break-word;
}
.ob-bubble.them {
  background: var(--ob-surface-2);
  align-self: flex-start;
  border-radius: 14px 14px 14px 4px;
}
.ob-bubble.me {
  background: var(--ob-ink);
  color: var(--ob-ink-inverse);
  align-self: flex-end;
  border-radius: 14px 14px 4px 14px;
}
.ob-thread-date {
  display: flex; align-items: center; gap: 10px;
  margin: 14px 0 6px;
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-mono);
  font-size: 10px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
}
.ob-thread-date::before, .ob-thread-date::after {
  content: ""; flex: 1; height: 1px; background: var(--ob-rule);
}

/* ════════ PROFILE — § 7 ═════════════════════════════════════════════════ */
.ob-profile-header {
  display: flex;
  gap: 24px;
  align-items: center;
  padding-bottom: 24px;
  border-bottom: 0.5px solid var(--ob-rule);
  margin-bottom: 32px;
}
.ob-profile-header img,
.ob-profile-header .avatar-placeholder {
  width: 96px; height: 96px;
  border-radius: 50%;
  border: 0.5px solid var(--ob-rule);
  background: var(--ob-surface-2);
  display: grid; place-items: center;
  font-family: var(--ob-font-display); font-weight: 600;
  font-size: 32px;
  color: var(--ob-ink-muted);
  object-fit: cover;
  overflow: hidden;
}
.ob-profile-header h1 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 38px;
  letter-spacing: -0.015em;
  margin: 0;
}
.ob-profile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 32px;
}
.ob-profile-grid h2 {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin: 0 0 14px;
  font-weight: 500;
  padding-bottom: 8px;
  border-bottom: 0.5px solid var(--ob-rule);
}
@media (max-width: 1023px) {
  .ob-profile-grid { grid-template-columns: 1fr; gap: 28px; }
}
@media (max-width: 767px) {
  .ob-profile-header { flex-direction: column; text-align: center; gap: 12px; }
  .ob-profile-header h1 { font-size: 28px; }
}

/* ════════ SETTINGS — § 8 ════════════════════════════════════════════════ */
.ob-settings-shell {
  max-width: 560px;
}
.ob-settings-section { margin-bottom: 24px; }
.ob-settings-section-head {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  font-weight: 500;
  margin: 24px 0 12px;
  padding-bottom: 8px;
  border-bottom: 0.5px solid var(--ob-rule);
}
.ob-settings-section-head:first-child { margin-top: 0; }
.ob-settings-section .danger { color: var(--ob-danger); }

/* ════════ EMPTY / LOADING / ERROR — § 12 ═════════════════════════════════ */
.ob-empty {
  text-align: center;
  padding: 80px 22px;
  max-width: 460px;
  margin: 0 auto;
}
.ob-empty .eyebrow {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin-bottom: 14px;
}
.ob-empty .quote {
  font-family: var(--ob-font-display);
  font-style: italic;
  font-size: 17px;
  line-height: 1.5;
  color: var(--ob-ink);
}
.ob-empty .cta { margin-top: 20px; }

.ob-skel {
  background: var(--ob-surface-2);
  background-image: linear-gradient(90deg,
    var(--ob-surface-2) 0%, color-mix(in oklab, var(--ob-surface-2) 60%, var(--ob-bg)) 50%, var(--ob-surface-2) 100%);
  background-size: 200px 100%;
  background-repeat: no-repeat;
  animation: ob-shimmer 1.4s linear infinite;
  border-radius: 6px;
}
@keyframes ob-shimmer {
  0%   { background-position: -200px 0; }
  100% { background-position: 200px 0; }
}
.ob-skel-cover { width: 150px; height: 225px; }
.ob-skel-line  { height: 14px; margin: 6px 0; }
.ob-skel-line.short { width: 40%; }
.ob-skel-line.medium { width: 65%; }

.ob-error-page {
  text-align: center;
  padding: 80px 22px;
}
.ob-error-page .code {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 200px;
  line-height: 1;
  color: var(--ob-accent);
  letter-spacing: -0.04em;
}
.ob-error-page .quote {
  font-family: var(--ob-font-display);
  font-style: italic;
  font-size: 18px;
  color: var(--ob-ink-muted);
  margin: 14px 0 28px;
}

/* ════════ A11Y / REDUCED-MOTION — § 17 ══════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .ob-skel { animation: none; background-image: none; }
}

/* Onboarding ─ § 9.2 */
.ob-onboarding-step {
  max-width: 480px;
  margin: 60px auto;
  text-align: center;
}
.ob-onboarding-step .progress {
  font-family: var(--ob-font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  margin-bottom: 22px;
}
.ob-onboarding-step h1 {
  font-family: var(--ob-font-display);
  font-weight: 600;
  font-size: 36px;
  letter-spacing: -0.015em;
  margin: 0 0 12px;
}
.ob-onboarding-step p { color: var(--ob-ink-muted); margin-bottom: 28px; }
.ob-onboarding-step .actions {
  display: flex; justify-content: space-between; margin-top: 32px;
}
.ob-onboarding-step .picker-card {
  display: block;
  padding: 18px 20px;
  border: 0.5px solid var(--ob-rule);
  border-radius: 12px;
  text-align: left;
  margin-bottom: 12px;
  background: var(--ob-surface);
  cursor: pointer;
  text-decoration: none;
  color: var(--ob-ink);
}
.ob-onboarding-step .picker-card:hover {
  border-color: var(--ob-accent);
}
.ob-onboarding-step .picker-card strong { display: block; font-size: 16px; margin-bottom: 4px; }
.ob-onboarding-step .picker-card small { color: var(--ob-ink-muted); }

/* Make legacy pages cohabit cleanly inside the new shell */
.shell-app .container, .shell-admin .container, .shell-marketing .container, .shell-auth .container {
  max-width: 100%;
  margin: 0;
  padding: 0;
}

/* ── Donations ─────────────────────────────────────────────────────────── */
.btn.btn-donate {
  background: color-mix(in srgb, var(--ob-danger) 88%, var(--ob-ink));
  color: #fff5ee;
  border-color: var(--ob-danger);
}
.btn.btn-donate:hover {
  background: var(--ob-danger);
  color: #fff;
  filter: brightness(1.05);
}
.btn.btn-donate svg { color: currentColor; }

.ob-donate-shell { max-width: 640px; margin: 0 auto; }

/* "One last step" amount-confirmation page before redirecting to Square */
.donate-confirm-card {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  padding: 32px 28px;
  text-align: center;
  margin-top: 18px;
}
.donate-confirm-amount {
  font-family: var(--ob-font-display);
  font-size: 56px;
  font-weight: 600;
  line-height: 1;
  margin: 8px 0 4px;
  color: var(--ob-ink);
}
.donate-confirm-cur {
  font-family: var(--ob-font-mono);
  font-size: 18px;
  color: var(--ob-ink-muted);
  margin-left: 6px;
  vertical-align: middle;
}
.donate-confirm-go { margin-top: 10px; }
@media (max-width: 480px) {
  .donate-confirm-amount { font-size: 42px; }
}

/* Admin Square mode picker */
.square-mode-picker { display: grid; gap: 10px; margin-top: 10px; }
.square-mode-option {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 14px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.square-mode-option:has(input:checked) {
  border-color: var(--ob-accent);
  background: var(--ob-surface-2);
}
.square-mode-option input { margin-top: 4px; flex-shrink: 0; }
.square-mode-option div { display: flex; flex-direction: column; gap: 4px; }
.square-mode-option strong { font-size: 14px; }
.square-mode-option small { font-size: 12px; line-height: 1.4; }

/* ── Push notification panel (notification preferences page) ─────────
   Lives at the top of /me/notifications/prefs. JS-driven — switches
   between Enable / Disable / Denied / iOS-install-hint states based
   on browser capability and current Notification.permission. */
.ob-push-panel {
  border: 0.5px solid var(--ob-rule);
  border-radius: 12px;
  padding: 18px 20px;
  background: var(--ob-surface);
  margin: 18px 0 4px;
}
.ob-push-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.ob-push-head h2 {
  font-size: 16px;
  margin: 0 0 2px;
}
.ob-push-status {
  margin: 0;
  line-height: 1.45;
}
.ob-push-status.is-ok    { color: #22863a; }
.ob-push-status.is-warn  { color: #b08800; }
.ob-push-status.is-error { color: #b62436; }
.ob-push-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  flex-shrink: 0;
}
.ob-push-ios-hint {
  margin-top: 14px;
  padding: 12px 14px;
  background: var(--ob-surface-2);
  border-left: 3px solid var(--ob-accent);
  border-radius: 0 6px 6px 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ob-ink);
}
.ob-push-ios-hint strong { font-weight: 600; }

/* Tighten the prefs table on narrow viewports — three checkbox columns
   fit comfortably on phones if we strip side padding. */
.ob-prefs-table th,
.ob-prefs-table td {
  text-align: center;
}
.ob-prefs-table th:first-child,
.ob-prefs-table td:first-child {
  text-align: left;
}
@media (max-width: 540px) {
  .ob-prefs-table {
    font-size: 12.5px;
  }
  .ob-prefs-table th,
  .ob-prefs-table td {
    padding-left: 4px;
    padding-right: 4px;
  }
}

/* ── Payment-in-progress overlay ──────────────────────────────────────────
   Full-viewport scrim used during card-tokenize / Apple-Pay-tokenize / our
   charge round-trip. The donate page injects #payment-overlay near the end
   of the content; lockForProcessing() in donate.html toggles .is-active.
   The scrim covers the viewport (position:fixed; inset:0) so pointer events
   never reach the form behind it, and body.is-processing-payment disables
   scroll. Background blur + dim gives the spotlight effect on the centred
   card. */
.payment-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.62);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  padding: 24px;
}
.payment-overlay.is-active {
  display: flex;
  animation: payment-overlay-fade 220ms ease-out;
}
@keyframes payment-overlay-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
.payment-overlay-card {
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: 12px;
  padding: 32px 36px 26px;
  min-width: 280px;
  max-width: min(420px, 90vw);
  text-align: center;
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.5),
              0 0 0 1px rgba(255, 255, 255, 0.04) inset;
  animation: payment-overlay-pop 260ms cubic-bezier(.2, .8, .25, 1);
}
@keyframes payment-overlay-pop {
  from { transform: translateY(8px) scale(0.98); opacity: 0; }
  to   { transform: translateY(0)   scale(1);    opacity: 1; }
}
.payment-spinner {
  width: 38px;
  height: 38px;
  border: 3px solid var(--ob-rule);
  border-top-color: var(--ob-accent);
  border-radius: 50%;
  margin: 0 auto 18px;
  animation: payment-spin 800ms linear infinite;
}
@keyframes payment-spin {
  to { transform: rotate(360deg); }
}
.payment-overlay-msg {
  font-size: 15px;
  font-weight: 600;
  color: var(--ob-ink);
  line-height: 1.4;
  margin-bottom: 8px;
}
.payment-overlay-sub {
  margin-top: 6px;
  line-height: 1.4;
}
/* When processing: lock body scroll. The overlay itself captures pointer
   events for visual elements behind it — this rule purely stops the
   browser's scroll-on-touch-drag during the (usually <3s) wait. */
body.is-processing-payment {
  overflow: hidden;
}
/* Honour reduced-motion preferences — drop spinner + pop animation. The
   scrim still appears, the spinner just sits still. */
@media (prefers-reduced-motion: reduce) {
  .payment-overlay.is-active,
  .payment-overlay-card,
  .payment-spinner {
    animation: none;
  }
}

/* Apple Pay button — rendered by WebKit via the `-apple-pay-button`
   appearance value. Square's SDK does NOT mount the button itself for
   Apple Pay (only for Card); we render a real <button> and the browser
   draws the official Apple Pay glyph + chrome. The wrap stays hidden via
   the [hidden] attribute on non-Safari browsers and on devices where
   `paymentsInstance.applePay()` rejects (e.g. no card in Wallet). */
.apple-pay-wrap {
  margin-bottom: 18px;
}
.apple-pay-button {
  /* WebKit-only — gracefully degrades on non-Safari (the wrap is hidden
     anyway via JS, so the bare button never paints). */
  -webkit-appearance: -apple-pay-button;
  appearance: -apple-pay-button;
  -apple-pay-button-type: donate;
  -apple-pay-button-style: black;
  display: block;
  width: 100%;
  min-height: 48px;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  /* Reset default <button> styling that would interfere with Apple's
     drawn glyph. */
  background: transparent;
  padding: 0;
  margin: 0;
  font: inherit;
}
.apple-pay-button:focus-visible {
  outline: 2px solid var(--ob-accent);
  outline-offset: 2px;
}
.apple-pay-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 14px 0 4px;
  color: var(--ob-ink-muted);
  font-family: var(--ob-font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.apple-pay-divider::before,
.apple-pay-divider::after {
  content: '';
  flex: 1;
  height: 0.5px;
  background: var(--ob-rule);
}

/* In-page Square card form on the Donate page */
.square-card-shell {
  margin-top: 18px;
  padding: 16px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  background: var(--ob-surface);
}
.square-card-shell .mono-meta { display: block; margin-bottom: 8px; }
#square-card-container {
  min-height: 90px;
  background: var(--ob-bg);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  padding: 14px 12px;
}
.square-card-status {
  margin-top: 10px;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 13px;
}
.square-card-status.is-error {
  background: color-mix(in srgb, #b00 10%, var(--ob-surface-2));
  color: #b00;
  border: 0.5px solid color-mix(in srgb, #b00 30%, transparent);
}
.square-card-status.is-loading { color: var(--ob-ink-muted); }
.ob-donate-intro {
  font-family: var(--ob-font-display);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ob-ink);
  margin: 14px 0 32px;
}
.ob-donate-intro p { margin: 0 0 14px; }
.ob-donate-intro p.muted { color: var(--ob-ink-muted); font-size: 13px; }

/* ── Monthly coverage progress bar ────────────────────────────────────────
   Sits between the intro text and the donation form. Intentionally thin
   (6px track) so it reads as ambient context, not a hero element — donors
   are here to give, not to be stared down by a fundraising thermometer.
   The fill animates in on page load (width transitions from 0 to the
   computed percent) for a subtle "coverage growing" feel. */
.donate-coverage {
  margin: -10px 0 28px;  /* claws back the intro's bottom margin a touch */
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.donate-coverage-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.donate-coverage-title {
  font-family: var(--ob-font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
}
.donate-coverage-figures {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: 14px;
  color: var(--ob-ink);
}
.donate-coverage-figures strong {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.donate-coverage-figures .muted {
  color: var(--ob-ink-muted);
  font-variant-numeric: tabular-nums;
}
.donate-coverage-pct {
  margin-left: 8px;
  padding: 2px 7px;
  border: 0.5px solid var(--ob-rule);
  border-radius: 999px;
  font-size: 10.5px;
  color: var(--ob-ink-muted);
  font-variant-numeric: tabular-nums;
}
.donate-coverage-track {
  position: relative;
  height: 6px;
  background: var(--ob-surface-2, var(--ob-surface));
  border: 0.5px solid var(--ob-rule);
  border-radius: 999px;
  overflow: hidden;
}
.donate-coverage-fill {
  position: absolute;
  inset: 0 auto 0 0;
  height: 100%;
  background: linear-gradient(90deg,
                              var(--ob-accent) 0%,
                              var(--ob-accent) 100%);
  border-radius: 999px;
  /* Animate the bar growing on page load. The width is set inline by
     Jinja; the transition catches it as it overrides the 0 baseline
     applied by the @keyframes below. */
  animation: donate-coverage-grow 900ms cubic-bezier(.22, .8, .27, 1) both;
  transform-origin: left center;
}
@keyframes donate-coverage-grow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@media (prefers-reduced-motion: reduce) {
  .donate-coverage-fill { animation: none; }
}

.ob-donate-form { gap: 22px; }
.ob-donate-fieldset {
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  padding: 18px 18px 14px;
  background: var(--ob-surface);
  display: flex; flex-direction: column; gap: 12px;
}
.ob-donate-fieldset legend { padding: 0 8px; }

.ob-amount-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
  gap: 8px;
}
.ob-amount-pill {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  padding: 10px 10px;
  /* Border is 2px in BOTH states so the layout doesn't shift when the
     selection changes — the unselected pill just uses the muted rule colour. */
  border: 2px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  cursor: pointer;
  background: var(--ob-bg);
  font-family: var(--ob-font-display);
  transition: background .12s, border-color .12s, box-shadow .12s, transform .12s;
}
.ob-amount-pill:hover { border-color: color-mix(in srgb, var(--ob-accent) 50%, var(--ob-rule)); }
.ob-amount-pill input { position: absolute; opacity: 0; pointer-events: none; }
.ob-amount-pill:has(input:checked) {
  border-color: var(--ob-accent);
  background: color-mix(in srgb, var(--ob-accent) 28%, var(--ob-bg));
  /* Outer glow ring — clearly distinguishes the selected pill at a glance */
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ob-accent) 22%, transparent);
  transform: translateY(-1px);
}
.ob-amount-pill:has(input:checked) .ob-amount-pill-figure {
  color: var(--ob-accent);
  font-weight: 700;
}
.ob-amount-pill:has(input:checked) .ob-amount-pill-cur {
  color: color-mix(in srgb, var(--ob-accent) 80%, var(--ob-ink));
}
.ob-amount-pill-figure { font-size: 17px; font-weight: 600; line-height: 1.1; }
.ob-amount-pill-cur {
  font-family: var(--ob-font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  color: var(--ob-ink-muted);
  text-transform: uppercase;
}
.ob-amount-pill.ob-amount-other .ob-amount-pill-figure { font-size: 13px; color: var(--ob-ink-muted); }
.ob-amount-pill.ob-amount-other:has(input:checked) .ob-amount-pill-figure {
  color: var(--ob-accent);
}

.ob-amount-custom-wrap {
  margin-top: 10px;
}
.ob-amount-custom {
  display: flex; flex-direction: column; gap: 4px;
}

.ob-processor-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
}
@media (min-width: 480px) { .ob-processor-grid { grid-template-columns: 1fr 1fr; } }
.ob-processor-card {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  cursor: pointer;
  background: var(--ob-bg);
  transition: border-color .12s, background .12s;
}
.ob-processor-card input {
  margin: 0;
  flex: 0 0 16px;
}
.ob-processor-card:has(input:checked) {
  border-color: var(--ob-accent);
  background: color-mix(in srgb, var(--ob-accent) 12%, var(--ob-bg));
}
.ob-processor-card div { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.ob-processor-card strong { font-size: 13px; }
.ob-processor-card small { color: var(--ob-ink-muted); font-size: 11px; }

/* ── Badges ────────────────────────────────────────────────────────────── */
.ob-badge-progress {
  height: 6px;
  background: var(--ob-surface-2);
  border-radius: 3px;
  overflow: hidden;
  margin: 0 0 28px;
  border: 0.5px solid var(--ob-rule);
}
.ob-badge-progress > div {
  height: 100%;
  background: linear-gradient(90deg, var(--ob-accent), color-mix(in srgb, var(--ob-accent) 60%, var(--ob-success)));
  transition: width .4s ease-out;
}
.ob-badge-section { margin-bottom: 28px; }
.ob-badge-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 10px;
  margin-top: 12px;
}
.ob-badge-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  background: var(--ob-surface);
  opacity: 0.55;
  filter: grayscale(0.7);
  transition: opacity .12s, filter .12s, border-color .12s;
}
.ob-badge-card.ob-badge-held {
  opacity: 1;
  filter: none;
  border-color: var(--ob-accent);
  background: color-mix(in srgb, var(--ob-accent) 8%, var(--ob-surface));
}
.ob-badge-glyph {
  flex: 0 0 38px;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ob-surface-2);
  font-size: 18px;
  border: 0.5px solid var(--ob-rule);
}
.ob-badge-held .ob-badge-glyph {
  background: color-mix(in srgb, var(--ob-accent) 22%, var(--ob-surface));
  border-color: var(--ob-accent);
}
.ob-badge-text { display: flex; flex-direction: column; min-width: 0; }
.ob-badge-text strong { font-size: 13px; line-height: 1.2; }
.ob-badge-text small { font-size: 11px; color: var(--ob-ink-muted); margin-top: 2px; line-height: 1.3; }
.ob-badge-when { margin-top: 4px; font-size: 9.5px; }

/* ── Dropdown menu (used for Quick actions on home) ────────────────────── */
.ob-dropdown { position: relative; display: inline-block; }
.ob-dropdown > summary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 16px;
  border-radius: 999px;
  background: var(--ob-btn-bg);
  color: var(--ob-btn-fg);
  border: 0.5px solid var(--ob-btn-bg);
  cursor: pointer;
  list-style: none;
  font-family: var(--ob-font-body);
  font-size: 14px;
  font-weight: 500;
  user-select: none;
  transition: filter .12s, transform .12s;
}
.ob-dropdown > summary:hover { filter: brightness(1.08); }
.ob-dropdown > summary::-webkit-details-marker,
.ob-dropdown > summary::marker { display: none; }
.ob-dropdown-trigger { /* alias for summary styling outside details */ }
.ob-dropdown[open] > summary .ob-dropdown-caret { transform: rotate(180deg); }
.ob-dropdown-caret { transition: transform .15s; }

.ob-dropdown-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 280px;
  max-width: 90vw;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  box-shadow: var(--ob-shadow-lg);
  padding: 6px;
  z-index: 60;
  animation: ob-dd-in 140ms ease-out;
}
@keyframes ob-dd-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
.ob-dropdown-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--ob-radius-sm);
  color: var(--ob-ink);
  text-decoration: none;
  border-bottom: 0;
  font-size: 14px;
  cursor: pointer;
}
.ob-dropdown-item:hover { background: color-mix(in srgb, var(--ob-ink) 5%, transparent); }
.ob-dropdown-item strong { display: block; font-size: 13px; line-height: 1.25; }
.ob-dropdown-item small { display: block; font-size: 11px; color: var(--ob-ink-muted); line-height: 1.25; }
.ob-dropdown-icon {
  display: inline-grid;
  place-items: center;
  width: 26px; height: 26px;
  flex: 0 0 26px;
  border-radius: 50%;
  background: var(--ob-surface-2);
  font-size: 13px;
}
.ob-dropdown-sep {
  height: 1px;
  background: var(--ob-rule);
  margin: 4px 8px;
}
.ob-dropdown-pill {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--ob-accent);
  color: var(--ob-accent-ink);
  font-family: var(--ob-font-mono);
  font-size: 10px;
  font-weight: 600;
}

/* ── Inline barcode scan on the Add Book form ──────────────────────────── */
.ob-scan-inline {
  margin-bottom: 14px;
}
.ob-scan-details {
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  background: var(--ob-surface);
  overflow: hidden;
  transition: border-color .15s;
}
.ob-scan-details[open] { border-color: var(--ob-accent); }
.ob-scan-details > summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.ob-scan-details > summary::-webkit-details-marker { display: none; }
.ob-scan-details > summary::after {
  content: "▾";
  margin-left: auto;
  color: var(--ob-ink-muted);
  font-size: 12px;
  transition: transform .15s;
}
.ob-scan-details[open] > summary::after { transform: rotate(180deg); }
.ob-scan-summary-icon {
  display: inline-grid;
  place-items: center;
  width: 36px; height: 36px;
  flex: 0 0 36px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--ob-accent) 18%, var(--ob-surface-2));
  color: var(--ob-ink);
}
.ob-scan-details > summary strong { display: block; font-size: 14px; }
.ob-scan-details > summary small { display: block; font-size: 11.5px; line-height: 1.3; }
.ob-scan-body {
  padding: 0 16px 16px;
  border-top: 0.5px solid var(--ob-rule);
}
.ob-scan-body > #scan-status,
.ob-scan-body > #cover-status {
  padding: 12px 0 8px;
  font-size: 11px;
}
.ob-scan-body video {
  width: 100%;
  max-height: 320px;
  background: #000;
  border-radius: var(--ob-radius-sm);
  margin-bottom: 10px;
  object-fit: cover;
}
.ob-scan-controls {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}

/* ── Cover-OCR scanner — picker + manual fallback ────────────────────
   Lives inside .ob-scan-body of the #cover-details panel. The panel
   itself shares all the .ob-scan-* layout (collapsible details,
   chevron, summary icon, etc.) — only the picker + manual-fallback
   children below are unique to OCR. */

/* Top-3 candidate picker — three full-width tiles, each a button
   showing cover thumb + title/author/source. Click any tile to
   accept its ISBN and continue the auto-fill flow. */
.ob-cover-candidates {
  border-top: 0.5px dashed var(--ob-rule);
  margin-top: 12px;
  padding-top: 10px;
}
.ob-cover-candidates-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ob-cover-candidate {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 10px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  background: var(--ob-surface);
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: var(--ob-ink);
  transition: border-color 140ms, transform 140ms,
              box-shadow 140ms, background 140ms;
}
.ob-cover-candidate:hover,
.ob-cover-candidate:focus-visible {
  border-color: var(--ob-accent);
  background: color-mix(in srgb, var(--ob-accent) 6%, var(--ob-surface));
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  outline: none;
}
.ob-cover-candidate:active { transform: translateY(1px); }
.ob-cover-candidate-cover {
  flex: 0 0 56px;
  width: 56px;
  height: 84px;
  background: var(--ob-surface-2);
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ob-cover-candidate-cover img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.ob-cover-candidate-meta {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  line-height: 1.4;
}
.ob-cover-candidate-meta strong {
  font-weight: 600;
  /* Keep titles to two lines max so a long title doesn't blow up the
     tile height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ob-cover-candidate-meta .muted {
  font-size: 12px;
  color: var(--ob-ink-muted);
}
.ob-cover-candidate-meta .mono-meta {
  font-size: 10px;
  color: var(--ob-ink-muted);
  letter-spacing: 0.02em;
}

/* Manual title/author fallback when OCR returns nothing usable. */
.ob-cover-manual {
  border-top: 0.5px dashed var(--ob-rule);
  margin-top: 12px;
  padding-top: 10px;
}
.ob-cover-manual input {
  width: 100%;
  padding: 8px 10px;
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-sm);
  background: var(--ob-surface);
  color: var(--ob-ink);
  font-size: 14px;
}
.ob-cover-manual input:focus-visible {
  border-color: var(--ob-accent);
  outline: none;
}

/* Decorative book-cover stand-in for marketing pages — solid, not a skeleton. */
.ob-deco-cover {
  width: 150px; height: 225px;
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--ob-accent) 18%, var(--ob-surface)) 0%,
    var(--ob-surface-2) 100%);
  border: 0.5px solid var(--ob-rule);
  border-radius: 4px;
  box-shadow: var(--ob-shadow-md);
  position: relative;
}
.ob-deco-cover::before {
  content: "";
  position: absolute;
  left: 8px; top: 0; bottom: 0;
  width: 1px;
  background: rgba(14,14,12,0.10);
}

/* Marketing illustrations — bookcase + lending. Drop-shadow so they "sit" in
   the feature row, and constrain max-size so wide viewports don't over-scale. */
.ob-illustration {
  max-width: 100%;
  height: auto;
  filter: drop-shadow(0 14px 30px rgba(14,14,12,0.20));
}
@media (max-width: 480px) {
  .ob-illustration { max-width: 240px; }
}

/* Donation status pills */
.pill-status-pending   { background: color-mix(in srgb, var(--ob-warning) 18%, var(--ob-surface-2)); }
.pill-status-completed { background: color-mix(in srgb, var(--ob-success) 18%, var(--ob-surface-2)); }
.pill-status-failed    { background: color-mix(in srgb, var(--ob-danger)  18%, var(--ob-surface-2)); }
.pill-status-refunded  { background: var(--ob-surface-2); color: var(--ob-ink-muted); }

/* ── v5 production-readiness UI ──────────────────────────────────────────── */

/* Live banner — appears top-center when SSE pushes new feed events */
.ob-live-banner {
  position: fixed;
  top: 16px; left: 50%;
  transform: translateX(-50%);
  background: var(--ob-ink);
  color: var(--ob-ink-inverse);
  font-family: var(--ob-font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 8px 16px;
  border-radius: 999px;
  box-shadow: var(--ob-shadow-md);
  z-index: 200;
  text-decoration: none;
  border-bottom: 0;
  animation: ob-fade 0.2s ease-out;
  display: none;
}
.ob-live-banner:hover { color: var(--ob-accent); }

/* Mention + hashtag autolinks */
.ob-mention { color: var(--ob-accent); border-bottom: 0; font-weight: 500; }
.ob-mention:hover { text-decoration: underline; }
.ob-hashtag { color: var(--ob-info); border-bottom: 0; font-weight: 500; }
.ob-hashtag:hover { text-decoration: underline; }

/* Distance pill (privacy-bucketed only — never raw values) */
.ob-distance-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--ob-font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--ob-surface-2);
  color: var(--ob-ink-muted);
  border: 0.5px solid var(--ob-rule);
}
.ob-distance-pill::before { content: "⌖"; }
.ob-distance-near     .ob-distance-pill,
.ob-distance-pill.near     { background: color-mix(in srgb, var(--ob-success) 14%, var(--ob-surface-2)); color: var(--ob-ink); }
.ob-distance-pill.local    { background: color-mix(in srgb, var(--ob-accent)  10%, var(--ob-surface-2)); }
.ob-distance-pill.regional { background: var(--ob-surface-2); }
.ob-distance-pill.far      { background: var(--ob-surface-2); color: var(--ob-ink-subtle); }

/* Badge gallery — used by /me/badges */
.ob-badge-progress {
  height: 8px;
  background: var(--ob-surface-2);
  border-radius: 999px;
  border: 0.5px solid var(--ob-rule);
  overflow: hidden;
  margin: 14px 0 28px;
}
.ob-badge-progress > div {
  height: 100%;
  background: var(--ob-accent);
  transition: width .4s ease-out;
}
.ob-badge-section { margin-bottom: 28px; }
.ob-badge-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 10px;
}
.ob-badge-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  background: var(--ob-surface);
  border: 0.5px solid var(--ob-rule);
  border-radius: var(--ob-radius-md);
  opacity: 0.45;
  transition: opacity .12s, border-color .12s;
}
.ob-badge-card.ob-badge-held {
  opacity: 1;
  border-color: var(--ob-accent);
  background: color-mix(in srgb, var(--ob-accent) 8%, var(--ob-surface));
}
.ob-badge-glyph {
  font-size: 28px;
  line-height: 1;
  flex: 0 0 32px;
  text-align: center;
  filter: grayscale(1);
}
.ob-badge-held .ob-badge-glyph { filter: none; }
.ob-badge-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ob-badge-text strong { font-size: 14px; }
.ob-badge-text small { color: var(--ob-ink-muted); font-size: 12px; }
.ob-badge-when { font-size: 9px; margin-top: 4px; }

/* UGC hidden-by-mod indicator */
.ob-content-hidden {
  font-style: italic;
  color: var(--ob-ink-muted);
  background: var(--ob-surface-2);
  padding: 8px 12px;
  border-radius: var(--ob-radius-sm);
  border-left: 2px solid var(--ob-warning);
}

/* ── Phase A/D — Admin sidebar clusters ──────────────────────────────────── */

.ob-admin-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  margin-bottom: 18px;
  border-radius: 999px;
  background: var(--ob-surface-2);
  border: 0.5px solid var(--ob-rule);
}
.ob-admin-search input[type="search"] {
  flex: 1;
  border: 0;
  outline: 0;
  padding: 0;
  background: transparent;
  color: var(--ob-ink);
  font-family: var(--ob-font-body);
  font-size: 13px;
  min-width: 0;
}
.ob-admin-search input[type="search"]::placeholder { color: var(--ob-ink-muted); }
.ob-admin-search input[type="search"]:focus + .ob-search-kbd { display: none; }

.ob-admin-clusters {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ob-admin-cluster {
  border-radius: 8px;
  background: transparent;
}
.ob-admin-cluster[open] {
  background: color-mix(in srgb, var(--ob-ink) 3%, transparent);
}
.ob-admin-cluster > summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  padding: 0 10px;
  border-radius: 8px;
  cursor: pointer;
  font-family: var(--ob-font-mono);
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ob-ink-muted);
  user-select: none;
}
.ob-admin-cluster > summary::-webkit-details-marker { display: none; }
.ob-admin-cluster > summary::marker { content: ''; }
.ob-admin-cluster > summary::after {
  content: "›";
  margin-left: auto;
  color: var(--ob-ink-subtle);
  transition: transform 0.15s;
}
.ob-admin-cluster[open] > summary::after { transform: rotate(90deg); }
.ob-admin-cluster.on > summary {
  color: var(--ob-ink);
  font-weight: 600;
}
.ob-admin-cluster > summary:hover {
  background: color-mix(in srgb, var(--ob-ink) 5%, transparent);
}
.ob-admin-cluster > summary > a {
  color: inherit;
  text-decoration: none;
  flex: 1;
  border-bottom: 0;
}
.ob-cluster-icon {
  font-size: 12px;
  width: 16px;
  text-align: center;
  flex: 0 0 16px;
}
.ob-cluster-badge {
  background: var(--ob-danger);
  color: #fff;
  font-family: var(--ob-font-mono);
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 999px;
  font-weight: 600;
  letter-spacing: 0;
  margin-left: 4px;
}
.ob-cluster-badge-warn {
  background: var(--ob-warning);
  color: var(--ob-ink);
}
.ob-cluster-items {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px 0 6px 24px;
}
.ob-cluster-items .ob-nav-row {
  height: 32px;
  font-size: 13px;
}
