/* ── Reset & base ────────────────────────────────────────────────────────── */

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

:root {
  /* ── Signal Room theme: dark graphite/blue-black workstation ───────────── */
  --bg:           #0b0f14;            /* page background — deep graphite */
  --bg-grid:      rgba(155, 200, 230, 0.035); /* faint cybernetic grid */
  --panel:        #11181f;            /* primary surface (cards, inputs) */
  --panel-2:      #161e26;            /* slightly raised surface */
  --panel-3:      #1c2530;            /* hover / active surface */
  --surface:      var(--panel);       /* legacy alias */
  --border:       #243140;            /* subtle blue-grey edge */
  --border-strong:#324558;            /* stronger panel edge */
  --border-focus: #00d4c0;            /* cyan focus ring */
  --text:         #e6eef5;            /* primary text — cool near-white */
  --text-muted:   #7d8b9b;            /* secondary text — cool grey */
  --text-dim:     #58697a;            /* tertiary, e.g. table chrome */
  --accent:       #00d4c0;            /* cyan — primary signal */
  --accent-hover: #4ee9d8;
  --accent-2:     #ff9b3e;            /* amber — warnings, secondary chips */
  --accent-violet:#a78bfa;            /* violet — categories, accents */
  --accent-acid:  #b6f560;            /* acid-green — success-y accents */
  --danger:       #ff6b6b;            /* alert red */
  --success:      #50d890;            /* success green */
  --shadow:       0 1px 0 rgba(255, 255, 255, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.25);
  --radius:       4px;                /* tighter, console-like */
  --font:         -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-mono:    "SF Mono", "JetBrains Mono", "Fira Code", ui-monospace, Menlo, Consolas, monospace;
}

html { font-size: 16px; }

body {
  font-family: var(--font);
  background: var(--bg);
  background-image:
    linear-gradient(to right, var(--bg-grid) 1px, transparent 1px),
    linear-gradient(to bottom, var(--bg-grid) 1px, transparent 1px);
  background-size: 32px 32px;
  background-attachment: fixed;
  color: var(--text);
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: inherit; }
a:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
::selection { background: rgba(0, 212, 192, 0.25); color: var(--text); }

/* ── Layout ──────────────────────────────────────────────────────────────── */

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 1.5rem;
  height: 52px;
  border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, #0d141c 0%, #0a1117 100%);
  position: sticky;
  top: 0;
  z-index: 10;
  /* Thin cyan top stripe — workstation chrome */
  box-shadow: inset 0 1px 0 var(--accent), 0 1px 0 rgba(0, 0, 0, 0.5);
}

/* Brand lockup: Delightful logo + "// Signal Room" product label.
   The image scales with width, height stays auto so it never distorts. */
.brand-lockup {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  color: var(--text);
  min-width: 0;
}

.brand-logo {
  display: block;
  width: 120px;
  height: auto;
  max-height: 32px;
  object-fit: contain;
  flex-shrink: 0;
}

.brand-product {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 0.7rem;
  color: var(--text-dim);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

.site-header nav {
  display: flex;
  gap: 1rem;
}

.site-header nav a {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  text-decoration: none;
  padding: 0.25rem 0.5rem;
  border-radius: 3px;
  transition: color 0.15s, background 0.15s;
}

.site-header nav a:hover { color: var(--text); background: var(--panel-3); }

main {
  max-width: 620px;
  margin: 0 auto;
  padding: 2.5rem 1.5rem 4rem;
}

/* ── Page header ─────────────────────────────────────────────────────────── */

.page-header { margin-bottom: 1.75rem; }

.page-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent);
  margin-bottom: 0.4rem;
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.page-eyebrow::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  background: var(--accent);
  border-radius: 50%;
  box-shadow: 0 0 8px var(--accent);
}

.page-eyebrow-tag {
  color: var(--text-dim);
  font-weight: 400;
  letter-spacing: 0.12em;
}

.page-header h1 {
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.page-subhead {
  margin-top: 0.4rem;
  font-size: 0.8125rem;
  color: var(--text-muted);
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
}

/* ── Form ────────────────────────────────────────────────────────────────── */

.form { display: flex; flex-direction: column; gap: 1.25rem; }

.field { display: flex; flex-direction: column; gap: 0.375rem; }

label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.label-hint {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0.02em;
  color: var(--text-dim);
}

/* Small helper note that sits just under the Comment textarea, reminding
   the user that Markdown link syntax is the only supported formatting.
   Intentionally low-emphasis — there is no UI control to interact with. */
.comment-hint {
  margin-top: 0.375rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
  line-height: 1.4;
}

input[type="text"],
input[type="url"],
input[type="number"],
input[type="file"],
select,
textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--font);
  font-size: 0.9375rem;
  color: var(--text);
  background: var(--panel);
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
  appearance: none;
  caret-color: var(--accent);
}

input[type="text"]::placeholder,
input[type="url"]::placeholder,
textarea::placeholder { color: var(--text-dim); }

input[type="text"]:focus,
input[type="url"]:focus,
input[type="number"]:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px rgba(0, 212, 192, 0.15);
  background: var(--panel-2);
}

select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%237d8b9b' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.625rem center;
  padding-right: 2rem;
  cursor: pointer;
}

select:focus {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2300d4c0' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
}

select option { background: var(--panel-2); color: var(--text); }

textarea { resize: vertical; min-height: 80px; font-family: var(--font); }

input[type="file"] {
  padding: 0.4rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--text-muted);
}
input[type="file"]::file-selector-button {
  background: var(--panel-3);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--text);
  padding: 0.3rem 0.7rem;
  margin-right: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { background: var(--accent); color: #07101a; border-color: var(--accent); }

/* ── Actions ─────────────────────────────────────────────────────────────── */

.actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding-top: 0.25rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 1rem;
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background 0.15s, color 0.15s, border-color 0.15s, box-shadow 0.15s;
  white-space: nowrap;
}

.btn-primary {
  background: var(--accent);
  color: #07101a;
  border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(0, 212, 192, 0.3), 0 0 12px rgba(0, 212, 192, 0.15);
}

.btn-primary:hover:not(:disabled) {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  box-shadow: 0 0 0 1px rgba(78, 233, 216, 0.4), 0 0 16px rgba(0, 212, 192, 0.25);
}

.btn-primary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  box-shadow: none;
}

.btn-ghost {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--border);
}

.btn-ghost:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--panel);
}

.btn-secondary {
  background: var(--panel-2);
  color: var(--text);
  border-color: var(--border-strong);
}

.btn-secondary:hover {
  background: var(--panel-3);
  border-color: var(--accent);
  color: var(--accent);
}

.header-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* ── Status / alerts ─────────────────────────────────────────────────────── */

.status {
  font-size: 0.875rem;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
}

.status.hidden { display: none; }

.status-info {
  background: rgba(0, 212, 192, 0.08);
  color: var(--accent);
  border-color: rgba(0, 212, 192, 0.3);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
}

.status-error {
  background: rgba(255, 107, 107, 0.08);
  color: var(--danger);
  border-color: rgba(255, 107, 107, 0.3);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
}

.alert {
  padding: 0.625rem 0.875rem;
  border-radius: var(--radius);
  font-size: 0.875rem;
  margin-bottom: 1rem;
}

.alert-error {
  background: rgba(255, 107, 107, 0.08);
  color: var(--danger);
  border: 1px solid rgba(255, 107, 107, 0.3);
}

.error-box {
  background: rgba(255, 107, 107, 0.08);
  color: var(--danger);
  border: 1px solid rgba(255, 107, 107, 0.3);
  border-radius: var(--radius);
  padding: 0.6rem 0.85rem;
  font-size: 0.875rem;
}

.alert-success {
  background: rgba(80, 216, 144, 0.08);
  color: var(--success);
  border: 1px solid rgba(80, 216, 144, 0.3);
  border-radius: var(--radius);
  padding: 0.5rem 0.75rem;
  font-size: 0.875rem;
  font-family: var(--font-mono);
  margin-bottom: 1rem;
}

/* ── Issue actions / edit-links pages ───────────────────────────────────── */

.issue-actions {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0.5rem 0 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}
.issue-actions a { color: var(--accent); text-decoration: none; }
.issue-actions a:hover { text-decoration: underline; }
.issue-actions span { color: var(--text-dim); }

.links-edit-form { margin-top: 1rem; }

/* Horizontal-scroll wrapper around the bulk-edit table. Below the table's
   min-width, the wrapper scrolls horizontally rather than collapsing
   columns into single-character-wide URL cells. Scoped tightly to this
   wrapper so it doesn't affect /issues/:id/clicks's `.clicks-table`. */
.links-edit-wrap {
  overflow-x: auto;
  margin-bottom: 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
}

.links-edit-form .links-edit-table {
  width: 100%;
  min-width: 1080px;
  border-collapse: collapse;
  table-layout: fixed;
  margin: 0;
}
.links-edit-form .links-edit-table th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  padding: 0.5rem 0.5rem;
  border-bottom: 1px solid var(--border);
}
.links-edit-form .links-edit-table td {
  padding: 0.5rem 0.5rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.links-edit-form .links-edit-table input[type="text"],
.links-edit-form .links-edit-table textarea,
.links-edit-form .links-edit-table select {
  width: 100%;
  font-size: 0.85rem;
}
.links-edit-form .links-edit-table textarea {
  font-family: var(--font);
  resize: vertical;
  min-height: 2.5rem;
}

/* Per-column widths sum to 100% of the 1080px table min-width. URL stays
   wide enough for a typical host+path; gentler `overflow-wrap: anywhere`
   replaces the prior `word-break: break-all` so URLs only break at
   character boundaries when they have to. */
.links-edit-form .links-edit-th-url      { width: 22%; }
.links-edit-form .links-edit-th-title    { width: 16%; }
.links-edit-form .links-edit-th-source   { width: 12%; }
.links-edit-form .links-edit-th-quote    { width: 22%; }
.links-edit-form .links-edit-th-type     { width: 14%; }
.links-edit-form .links-edit-th-category { width: 14%; }

.links-edit-form .links-edit-url {
  overflow-wrap: anywhere;
}
.links-edit-form .links-edit-url a {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--accent);
  text-decoration: none;
}
.links-edit-form .links-edit-url a:hover { text-decoration: underline; }
.links-edit-form .links-edit-meta {
  margin-top: 0.25rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  display: flex;
  gap: 0.4rem;
}
.links-edit-form .links-edit-type select {
  min-width: 0;
}
.links-edit-form .links-edit-no-context {
  font-family: var(--font-mono);
  color: var(--text-dim);
}

/* ── Review page specifics ───────────────────────────────────────────────── */

.review-meta {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-bottom: 1.75rem;
  overflow: hidden;
}

.review-url {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  letter-spacing: 0.02em;
}

.review-url:hover { color: var(--accent-hover); text-decoration: underline; }
.review-url::before { content: "↳ "; color: var(--text-dim); }

/* ── Badges ──────────────────────────────────────────────────────────────── */

.badge {
  display: inline-block;
  padding: 0.18rem 0.5rem;
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 500;
  white-space: nowrap;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  background: var(--panel-3);
  color: var(--text-muted);
  border: 1px solid var(--border);
  flex-shrink: 0;
}

.badge-article   { background: rgba(0, 180, 255, 0.1);  color: #6cc5f5; border-color: rgba(0, 180, 255, 0.25); }
.badge-paywalled { background: rgba(255, 155, 62, 0.1); color: #ffb979; border-color: rgba(255, 155, 62, 0.3); }
.badge-video     { background: rgba(255, 107, 107, 0.1); color: #ff8f8f; border-color: rgba(255, 107, 107, 0.3); }
.badge-game      { background: rgba(182, 245, 96, 0.08); color: #c8f57a; border-color: rgba(182, 245, 96, 0.25); }
.badge-social    { background: rgba(167, 139, 250, 0.1); color: #c7b2ff; border-color: rgba(167, 139, 250, 0.3); }

.badge-quote-type {
  font-size: 0.68rem;
  font-weight: 400;
  background: var(--panel-3);
  color: var(--text-dim);
  text-transform: lowercase;
  letter-spacing: 0.04em;
  border-color: var(--border);
}

/* ── Nav active state ────────────────────────────────────────────────────── */

.site-header nav a.nav-active {
  color: var(--accent);
  background: rgba(0, 212, 192, 0.08);
  box-shadow: inset 0 -1px 0 var(--accent);
}

/* ── Inbox ───────────────────────────────────────────────────────────────── */

.inbox-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.inbox-header h1 { margin-bottom: 0; }

/* Read-only link counter that sits just under "Incoming Signals" so the
   user can see at a glance how full the current issue is getting. Not
   included in clipboard / digest output — purely a meter. */
.inbox-link-counter {
  margin: 0.2rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.count {
  font-size: 0.8em;
  font-weight: 400;
  color: var(--text-muted);
  margin-left: 0.3em;
}

.category-group {
  margin-bottom: 1.25rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.875rem 1rem 0.25rem;
  position: relative;
  box-shadow: var(--shadow);
}

.category-group::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent) 0%, transparent 60%);
  opacity: 0.5;
  border-radius: var(--radius) var(--radius) 0 0;
}

.category-heading {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  padding-bottom: 0.625rem;
  margin-bottom: 0;
}

.category-heading::before {
  content: "» ";
  color: var(--accent);
}

.link-list {
  list-style: none;
  display: flex;
  flex-direction: column;
}

.link-row {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.75rem 0;
  border-top: 1px solid var(--border);
}

.link-row:first-child { border-top-color: rgba(255, 255, 255, 0.04); }

.link-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.link-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.375rem;
  min-width: 0;
}

.link-title {
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  color: var(--accent);
  word-break: break-word;
  transition: color 0.15s;
}

.link-title:hover { color: var(--accent-hover); text-decoration: underline; }

.link-source {
  font-size: 0.8125rem;
  color: var(--text-muted);
  flex-shrink: 0;
}

.link-quote {
  font-size: 0.875rem;
  color: var(--text-muted);
  font-style: italic;
  line-height: 1.5;
  padding-left: 0;
}

.link-notes {
  font-size: 0.8125rem;
  color: var(--text-muted);
  padding-left: 0;
}

/* ── Card preview line ───────────────���───────────────────────────────────── */

.card-preview {
  font-size: 0.9375rem;
  line-height: 1.55;
  flex: 1;
  min-width: 0;
}

.card-quote {
  font-style: italic;
  color: var(--text);
}

.card-source {
  font-style: italic;
  color: var(--accent-violet);
}

/* "Decks + other artifacts" cards use the same two-line structure as every
   other category; only the LEAD token of the second line differs — the
   description renders as plain text with NO quotation marks, because this
   category's content model is a one-sentence summary, not a pull-quote.
   Color is the same as body text for that reason. Emitted by
   services/linkFormat.js for both the server-side card render (inbox.js
   linkRow) and the inline JS re-render (buildDisplay). */
.card-description {
  color: var(--text);
}
.shelf-link-description .card-description-text {
  color: var(--text);
  font-style: normal;
  font-size: 0.875rem;
  line-height: 1.45;
}

/* Per-link comment surfaced in the card preview / digest. Rendered as
   "// comment //" between the quote and the title; the wrapping marks
   come from the renderer (src/services/comment.js + the inline mirror
   in inbox.js), not from CSS pseudo-elements, so plain-text copy stays
   identical to what's on screen. */
.card-comment {
  font-style: italic;
  color: var(--accent-2);
}
.card-comment a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: rgba(255, 155, 62, 0.5);
}
.card-comment a:hover {
  text-decoration-color: var(--accent-2);
}

/* ── Captured link: two-line card ─────────────────────────────────────────
   Every category renders what the clipboard emits, minus the bullet:

     line 1   the linked title
     line 2   "Quote." // Comment. // [Source] via domain ↗

   ("Decks + other artifacts" swaps the leading quote for its unquoted
   description — same structure, see .card-description above.)

   The hard return is a real <br> in the markup — the same element the
   clipboard HTML uses — so the card on screen and a rich paste break in the
   same place. Scoped to this component: public/delightful-os.css is not
   touched. */
.card-preview-stacked .link-title {
  font-size: 1rem;
  font-weight: 600;
}

.card-second-line {
  display: inline-block;
  margin-top: 0.2rem;
  /* Editorial serif body treatment for the quote/comment line. The stack
     mirrors the reader serif the shelf chrome uses; declared locally so the
     shared chrome file stays untouched. */
  font-family: "Iowan Old Style", Charter, "Palatino Linotype", Palatino,
               "Book Antiqua", Georgia, Cambria, "Times New Roman", Times, serif;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--text);
}

/* Keep the via attribution's "domain ↗" together — on the longer metadata
   line the arrow would otherwise wrap onto a line of its own. A break after
   "via" is still allowed. Rendering-only: the copied markup is untouched. */
.card-second-line .card-via a { white-space: nowrap; }

/* Two-line rows need a little more air than the old single-line cards did,
   so a long inbox stays scannable. */
.link-row:has(.card-preview-stacked) { padding: 1rem 0; }

.digest-comment {
  font-style: italic;
  color: var(--accent-2);
}
.digest-comment a {
  color: inherit;
  text-decoration: underline;
}

.copy-link-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0.1rem 0;
  transition: color 0.15s;
}
.copy-link-btn:hover { color: var(--accent); }

.digest-preview {
  font-size: 0.8rem;
  color: var(--text-dim);
  line-height: 1.45;
  margin-top: 0.25rem;
  padding-top: 0.375rem;
  border-top: 1px dashed var(--border);
}

.preview-link {
  color: var(--text-dim);
  text-decoration: none;
  font-weight: 500;
}

.preview-link:hover { text-decoration: underline; }

.preview-source {
  font-style: italic;
}

.delete-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  cursor: pointer;
  padding: 0.1rem 0;
  transition: color 0.15s;
}

.delete-btn:hover:not(:disabled) { color: var(--danger); }
.delete-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.link-actions { display: flex; gap: 0.75rem; flex-shrink: 0; }

.edit-btn {
  flex-shrink: 0;
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0.1rem 0;
  transition: color 0.15s;
}
.edit-btn:hover { color: var(--accent); }

/* ── Inline edit form ────────────────────────────────────────────────────── */

.edit-form {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  padding: 0.25rem 0 0.25rem;
}

.edit-label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}
.edit-label::before {
  content: "▸ ";
  color: var(--accent);
}

/* ── Category select on card ─────────────────────────────────────────────── */

.link-category-row { padding-top: 0.375rem; }

.category-select {
  font-size: 0.8rem;
  padding: 0.25rem 1.75rem 0.25rem 0.5rem;
  color: var(--text-muted);
  border-color: var(--border);
  background-color: transparent;
}

.category-select:focus { color: var(--text); }

/* Auto-hide empty category sections */
.category-group:not(:has(li.link-row)) { display: none; }

/* ── Inbox: wider container + sources sidebar ─────────────────── */
main.main-wide { max-width: 960px; }

/* Wider container for the archive page so the sidebar+main grid still
   leaves room for the full-width All Issues table at typical desktop
   widths. Below 980px the layout collapses to a single column (see
   .archive-layout media query) and the extra width is unused. */
main.main-archive { max-width: 1280px; }

.inbox-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 240px;
  gap: 2rem;
  align-items: start;
}

.inbox-sidebar { position: sticky; top: 1.5rem; }

.sources-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.875rem 1rem 1rem;
  position: relative;
  box-shadow: var(--shadow);
}

.sources-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent-violet) 0%, transparent 60%);
  opacity: 0.5;
  border-radius: var(--radius) var(--radius) 0 0;
}

.sources-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.sources-card-header h2 {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin: 0;
}
.sources-card-header h2::before {
  content: "» ";
  color: var(--accent-violet);
}

.sources-summary {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0 0 0.5rem;
}

.sources-list {
  list-style: disc;
  padding-left: 1.1rem;
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.5;
}

.sources-list li { margin: 0.15rem 0; }
.sources-list li::marker { color: var(--accent-violet); }
.sources-list a { color: var(--text); text-decoration: none; border-bottom: 1px dotted transparent; }
.sources-list a:hover { color: var(--accent-violet); border-bottom-color: var(--accent-violet); }
.sources-empty { font-size: 0.875rem; color: var(--text-muted); margin: 0; font-style: italic; }

/* ── Via section ("Where the wild links are from this week") ─────────────────
   The main-column credit line: one inline, comma-delimited list of the domains
   that referred this issue's links. Replaces the old .issue-sources-* block in
   the main column — the SIDEBAR source card above is untouched. Card chrome is
   borrowed from .reading-card; only the list needs its own rules. */
.via-section-card {
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--panel);
  padding: 1rem 1.25rem 1.125rem;
  margin: 0 0 2.25rem;
}

.via-section-card .field { margin-bottom: 0; }

.via-section-card .reading-input { width: 100%; box-sizing: border-box; }

/* One inline comma-delimited line — no bullets, no list markers. */
.via-section-list {
  margin: 0.875rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--text);
}
.via-section-list a {
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px dotted var(--accent-violet);
}
.via-section-list a:hover { color: var(--accent-violet); }

.inbox-sidebar > .sources-card + .sources-card { margin-top: 1rem; }

.cited-list {
  list-style: decimal;
  padding-left: 1.4rem;
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.5;
}

.cited-list li { margin: 0.15rem 0; }
.cited-list li::marker { color: var(--accent-violet); }
.cited-list a { color: var(--text); text-decoration: none; border-bottom: 1px dotted transparent; }
.cited-list a:hover { color: var(--accent-violet); border-bottom-color: var(--accent-violet); }

@media (max-width: 720px) {
  .inbox-layout { grid-template-columns: minmax(0, 1fr); }
  .inbox-sidebar { position: static; }
}

/* ── Scratch pad card ──────────────────────────────────────────────────────
   Private freeform notes for the in-progress issue. Sits at the top of the
   inbox column, above the images strip / category groups. Not part of the
   digest output — purely a drafting surface. */

.scratchpad-card {
  margin-bottom: 1.5rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.875rem 1rem 1rem;
  position: relative;
  box-shadow: var(--shadow);
}

.scratchpad-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent-2) 0%, transparent 60%);
  opacity: 0.5;
  border-radius: var(--radius) var(--radius) 0 0;
}

.scratchpad-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}

.scratchpad-header h2 {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin: 0;
}
.scratchpad-header h2::before {
  content: "» ";
  color: var(--accent-2);
}

.scratchpad-status {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}
.scratchpad-status[data-state="saved"]  { color: var(--success); }
.scratchpad-status[data-state="saving"] { color: var(--text-muted); }
.scratchpad-status[data-state="dirty"]  { color: var(--accent-2); }
.scratchpad-status[data-state="error"]  { color: var(--danger); }

.scratchpad-hint {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0 0 0.5rem;
}

.scratchpad-textarea {
  width: 100%;
  min-height: 8rem;
  resize: vertical;
  font-family: var(--font);
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.625rem 0.75rem;
}
.scratchpad-textarea:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 2px rgba(0, 212, 192, 0.12);
}

@media (max-width: 720px) {
  .scratchpad-card { padding: 0.75rem 0.875rem 0.875rem; }
  .scratchpad-textarea { min-height: 6.5rem; font-size: 1rem; }
}

/* In demo mode, the scratch pad is read-only on the server. Reflect that
   in the UI — keep the card visible (notes may still be interesting to read)
   but disable input. */
body.is-demo .scratchpad-textarea { pointer-events: none; opacity: 0.7; }
body.is-demo .scratchpad-status { display: none; }

/* ── Reading section (manual book recommendation, bottom of digest) ──────── */

.reading-card {
  margin-top: 1.5rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.875rem 1rem 1rem;
  position: relative;
  box-shadow: var(--shadow);
}

.reading-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}

.reading-status {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}
.reading-status[data-state="saved"]  { color: var(--success); }
.reading-status[data-state="saving"] { color: var(--text-muted); }
.reading-status[data-state="dirty"]  { color: var(--accent-2); }
.reading-status[data-state="error"]  { color: var(--danger); }

.reading-hint {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0 0 0.75rem;
}

.reading-body {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
}

.reading-cover {
  flex: 0 0 auto;
  width: 130px;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.reading-cover-img {
  width: 130px;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  display: block;
}

.reading-cover-empty {
  width: 130px;
  min-height: 170px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 0.75rem;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  margin: 0;
  padding: 0.5rem;
}

.reading-cover-actions {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.reading-cover-label { text-align: center; cursor: pointer; }

.reading-cover-status {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  min-height: 1em;
}
.reading-cover-status[data-state="error"] { color: var(--danger); }

.reading-fields {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.reading-input {
  width: 100%;
  font-family: var(--font);
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem 0.75rem;
}
.reading-textarea { resize: vertical; min-height: 6rem; }
.reading-input:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 2px rgba(0, 212, 192, 0.12);
}

@media (max-width: 720px) {
  .reading-card { padding: 0.75rem 0.875rem 0.875rem; }
  .reading-body { flex-direction: column; }
  .reading-cover, .reading-cover-img, .reading-cover-empty { width: 100%; }
  .reading-cover-empty { min-height: 8rem; }
}

/* In demo mode the Reading section is read-only on the server (POST/DELETE
   return 403). Mirror that in the UI: keep it visible but disable input. */
body.is-demo .reading-input,
body.is-demo .reading-cover-actions { pointer-events: none; opacity: 0.7; }
body.is-demo .reading-status,
body.is-demo .reading-cover-status { display: none; }

/* Newsletter footer card reuses .reading-card layout; only the inline <code>
   in its hint (the { archive } token) needs a touch of styling. */
.footer-card .reading-hint code {
  font-family: var(--font-mono);
  font-size: 0.78em;
  padding: 0.05em 0.3em;
  border-radius: 4px;
  background: var(--panel-2);
  border: 1px solid var(--border);
}

/* ── Empty state ─────────────────────────────────────────────────────────── */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1rem;
  padding: 2rem 0;
  color: var(--text-muted);
}

/* ── Digest ──────────────────────────────────────────────────────────────── */

.digest-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.digest-header h1 { margin-bottom: 0; }

.digest-section { margin-bottom: 2rem; }

.digest-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.digest-row {
  font-size: 0.9375rem;
  line-height: 1.55;
}

.digest-quote {
  color: var(--text);
}

.digest-title {
  font-weight: 500;
  text-decoration: none;
  color: var(--accent);
}

.digest-title:hover { color: var(--accent-hover); text-decoration: underline; }

.digest-source {
  color: var(--accent-violet);
  font-size: 0.875rem;
  font-style: italic;
}

/* ── Mobile ───────────────────────────────────────────────────────────────── */

/* Safe-area insets for notched iPhones (standalone mode) */
@supports (padding: env(safe-area-inset-top)) {
  .site-header {
    padding-top: env(safe-area-inset-top);
    height: calc(52px + env(safe-area-inset-top));
  }
  main {
    padding-bottom: calc(4rem + env(safe-area-inset-bottom));
  }
}

@media (max-width: 540px) {
  /* Header */
  .site-header {
    padding-left:  max(1rem, env(safe-area-inset-left));
    padding-right: max(1rem, env(safe-area-inset-right));
    gap: 0.75rem;
  }

  .site-header nav { gap: 0.875rem; }

  /* Main content */
  main {
    padding-top:   1.75rem;
    padding-left:  max(1rem, env(safe-area-inset-left));
    padding-right: max(1rem, env(safe-area-inset-right));
  }

  /* Prevent iOS auto-zoom on focus: inputs must be >= 16px */
  input[type="text"],
  input[type="url"],
  select,
  textarea {
    font-size: 1rem;
  }

  /* Comfortable touch targets */
  .btn           { min-height: 44px; }
  .delete-btn    { min-height: 44px; padding: 0.5rem 0; }

  /* Stack form actions, full-width primary button */
  .actions {
    flex-direction: column;
    align-items: stretch;
  }
  .btn-ghost { text-align: center; }

  /* Inbox: give the remove button more breathing room */
  .link-top  { gap: 0.5rem; }
  .link-list { gap: 1.125rem; }

  /* Inbox header: stack actions below title on small screens */
  .inbox-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
  }
}

/* ── Archive layout: left sidebar (history) + main (dashboard) ──────────── */

.archive-layout {
  display: grid;
  grid-template-columns: 280px minmax(0, 1fr);
  grid-template-areas: "sidebar main";
  gap: 1.5rem;
  align-items: start;
}

.archive-sidebar { grid-area: sidebar; min-width: 0; position: sticky; top: 1.5rem; }
.archive-main { grid-area: main; min-width: 0; }

.archive-sidebar-card { padding: 0.85rem 0.95rem; }
.archive-sidebar-heading {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin-bottom: 0.5rem;
}
.archive-sidebar-heading::before { content: "» "; color: var(--accent); }
.archive-sidebar-action {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 0.65rem;
}
.archive-sidebar-action a { color: var(--accent); text-decoration: none; }
.archive-sidebar-action a:hover { text-decoration: underline; }

/* Preview-image admin card — sits below the archive history list. Same
   sidebar-card chrome as the history list above it, just a top margin
   so they don't visually fuse. The button submits a POST to the batch
   backfill endpoint; the route stamps a ?ok=... onto the redirect so
   the message lands in the page-level banner area. */
.preview-image-admin-card { margin-top: 1rem; }
.preview-image-admin-summary,
.preview-image-admin-remaining {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin: 0 0 0.4rem;
  line-height: 1.4;
}
.preview-image-admin-form { margin-top: 0.4rem; }
.preview-image-admin-form button[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Archive-import warnings list — small, muted, with a left amber bar */
.archive-import-warnings {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0 1rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--text-muted);
}
.archive-import-warnings li {
  padding: 0.35rem 0.6rem;
  border-left: 2px solid var(--accent-2);
  background: var(--panel-2);
  margin-bottom: 0.25rem;
  word-break: break-word;
}

.archive-sidebar-scroll {
  max-height: calc(100vh - 9rem);
  overflow-y: auto;
  padding-right: 0.25rem;
  /* Hide the scrollbar gutter when nothing to scroll, keep light when scrolling */
  scrollbar-gutter: stable;
}

/* Below ~980px the sidebar drops underneath the main content (DOM order
   inside .archive-layout is sidebar-then-main, so we override areas to
   put main first when stacking). Static positioning + uncapped height
   so a long history list scrolls with the page rather than its own
   container. */
@media (max-width: 980px) {
  .archive-layout {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "main"
      "sidebar";
  }
  .archive-sidebar { position: static; }
  .archive-sidebar-scroll { max-height: none; overflow-y: visible; }
}

/* Issues row styles below — sized for the narrower 280px sidebar column. */

.issues-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}

.issues-row {
  padding: 0.55rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.issues-row:first-child { border-top: none; padding-top: 0.25rem; }

.issues-row-title {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.issues-row-title:hover { color: var(--accent); }
.issues-row-title .muted { color: var(--text-dim); font-style: italic; font-weight: 400; }

.issues-row-meta {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

.issues-row-action {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--accent);
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  align-self: flex-start;
}

.issues-row-action:hover { color: var(--accent-hover); text-decoration: underline; }

/* Right-side affordance cluster on each archive row: the existing
   "View clicks" / "Import clicks" link plus the public-URL status
   ("Public issue ↗" outbound link, or quieter "No public URL"). */
.issues-row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.85rem;
}

.issues-row-public {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.issues-row-public:not(.issues-row-public-missing) {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dotted var(--accent);
}
.issues-row-public:not(.issues-row-public-missing):hover {
  color: var(--accent-hover);
  border-bottom-color: var(--accent-hover);
}
.issues-row-public-missing { color: var(--text-dim); font-style: italic; }

/* Inline edit form per issue row: single URL input + Save button.
   Quiet by default so it doesn't compete with the row title/meta. */
.issues-row-public-form {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0.35rem 0 0;
}
.issues-row-public-form input[type="url"] {
  flex: 1 1 auto;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  padding: 0.35rem 0.55rem;
}
.btn-small {
  padding: 0.3rem 0.7rem;
  font-size: 0.7rem;
}

/* Focus highlight after a public-URL save redirect ?issue=<id>. */
.issues-row-focused {
  outline: 1px solid rgba(0, 212, 192, 0.35);
  outline-offset: 2px;
  border-radius: 4px;
  background: rgba(0, 212, 192, 0.04);
}

/* ── Clicked-link cards (shared by Summary, lists, Issues) ──────────────── */

.click-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 0.875rem 0.875rem;
  position: relative;
  box-shadow: var(--shadow);
}

.click-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent) 0%, transparent 60%);
  opacity: 0.45;
  border-radius: var(--radius) var(--radius) 0 0;
}

.click-card h3 {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin-bottom: 0.5rem;
}
.click-card h3::before { content: "» "; color: var(--accent); }

.click-card-empty { font-size: 0.875rem; color: var(--text-muted); }
.click-unmatched-tag { color: var(--text-muted); font-size: 0.75rem; }

/* Primary stats — large, prominent (used in Summary card) */
.stat-primary {
  display: flex;
  flex-direction: column;
  margin-bottom: 0.5rem;
}
.stat-primary-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.stat-primary-value {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: 0.01em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

/* Two-column key-value list — label left, number right */
.kv-list {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.4;
}
.kv-list li {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.75rem;
  align-items: baseline;
  padding: 0.3rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.04);
}
.kv-list li:first-child { border-top: none; }
.kv-list .kv-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kv-list .kv-num {
  color: var(--accent);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  white-space: nowrap;
}
.kv-list a { color: var(--text); text-decoration: none; }
.kv-list a:hover { color: var(--accent); }

/* Secondary stat block in Summary card — smaller, more muted */
.kv-list-secondary {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  font-size: 0.75rem;
}
.kv-list-secondary li { padding: 0.15rem 0; border-top: none; }
.kv-list-secondary .kv-label { color: var(--text-muted); }

/* ── Headline form on clicks page ───────────────────────────────────────── */

.headline-form { margin: 0 0 1.25rem; }
.headline-form label {
  display: block;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 0.35rem;
}
.headline-form-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}
.headline-form-row input[type="text"] { flex: 1; min-width: 0; }

/* ── Click-import category dropdowns ────────────────────────────────────── */

.cat-select {
  font-size: 0.85rem;
  padding: 0.25rem 1.75rem 0.25rem 0.5rem;
  margin-top: 0.25rem;
  max-width: 100%;
}

/* ── Archive bar (Digest page) ───────────────────────────────────────────── */

.archive-bar {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px dashed var(--border);
}

.archive-form {
  margin-top: 0.875rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.archive-form p { font-size: 0.875rem; color: var(--text-muted); }

.archive-form-row {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  flex-wrap: wrap;
}

.archive-form-row label {
  font-size: 0.875rem;
  font-weight: 500;
}

.archive-form-row input[type="number"] {
  width: 5rem;
  font-size: 0.9375rem;
}

#archive-status {
  font-size: 0.875rem;
  color: var(--danger);
}

/* ── Images divider ────────────────────────────────────────────────────────── */

.images-divider {
  border: none;
  border-top: 1px dashed var(--border);
  margin: 0.75rem 0;
}

/* ── Category images (pool + assigned) ────────────────────────── */

/* Collapsible unassigned-image pool. Sits between the Scratch pad and
   Readers clicked; hidden entirely (the `hidden` attribute) when empty. */
.image-pool {
  margin: 0 0 2.25rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--panel);
}

.image-pool[hidden] { display: none; }

.image-pool-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.75rem;
  padding: 0.75rem 1rem;
  cursor: pointer;
  list-style: none;
}

.image-pool-summary::-webkit-details-marker { display: none; }

.image-pool-summary::before {
  content: "\25b8";
  font-size: 0.75rem;
  transition: transform 0.15s ease;
}

.image-pool[open] > .image-pool-summary::before { transform: rotate(90deg); }

.image-pool-title {
  font-weight: 600;
  letter-spacing: 0.01em;
}

.image-pool-status {
  font-size: 0.8125rem;
  color: var(--muted, #888);
}

.image-pool-list {
  list-style: none;
  margin: 0;
  padding: 0 1rem 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.pool-image {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 180px;
}

/* Frame is the positioning context for the overlaid delete control. */
.image-frame {
  position: relative;
  display: block;
}

.pool-image-img {
  width: 180px;
  height: 135px;
  object-fit: cover;
  border-radius: 3px;
  display: block;
  background: var(--bg, #111);
  border: 1px solid var(--border);
}

.image-delete-btn {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  width: 1.5rem;
  height: 1.5rem;
  line-height: 1;
  font-size: 1rem;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  cursor: pointer;
  padding: 0;
}

.image-delete-btn:hover { background: rgba(180, 30, 30, 0.9); }
.image-delete-btn:disabled { opacity: 0.5; cursor: default; }

.image-actions {
  display: flex;
  gap: 0.5rem;
}

/* Main image: the issue's lead image, between the Scratch pad and the Image
   pool. Rendered only when assigned — there is no empty placeholder. Reuses
   .category-image for the figure itself so Main and category images get
   identical treatment; this block only owns the surrounding label + spacing. */
.main-image-block {
  margin: 0 0 2.25rem;
}

.main-image-label {
  margin: 0 0 0.625rem;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted, #888);
}

.main-image-block .category-image {
  margin-bottom: 0;
}

/* Assigned image: below the category heading, above the link list. Capped at a
   newsletter-appropriate width; never stretched past its natural size. */
.category-image {
  margin: 0 0 1.25rem;
  max-width: 640px;
}

.category-image-img {
  display: block;
  max-width: 100%;
  width: auto;
  height: auto;
  border-radius: 3px;
  border: 1px solid var(--border);
}

/* Omitted entirely when there is no source URL, so it contributes no spacing. */
.category-image-source {
  margin: 0.4rem 0 0;
  font-size: 0.8125rem;
  word-break: break-all;
}

.category-image .image-actions { margin-top: 0.5rem; }

/* Compact edit interface, shared by pool cards and assigned images. */
.image-editor {
  margin-top: 0.625rem;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg, #111);
  max-width: 420px;
}

.image-editor[hidden] { display: none; }

.image-editor-filename {
  margin: 0 0 0.5rem;
  font-size: 0.8125rem;
  color: var(--muted, #888);
  word-break: break-all;
}

.image-editor-hint {
  margin: 0 0 0.5rem;
  font-size: 0.8125rem;
  color: var(--muted, #888);
}

.image-editor .field { margin-bottom: 0.625rem; }

.image-editor input,
.image-editor select {
  width: 100%;
  box-sizing: border-box;
}

.image-editor .actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.image-editor-status {
  font-size: 0.8125rem;
  color: var(--muted, #888);
}

.image-editor-status[data-state="error"] { color: var(--error, #e0685f); }

.add-image-no-categories {
  margin: 0 0 1rem;
}

/* Archive: stored-image reminder counts (no thumbnails yet). */
.archive-stored-images {
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  color: var(--muted, #888);
}

/* ── Digest image list ─────────────────────────────────────────────────────── */

.digest-image-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.digest-image-row {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  width: 180px;
}

.digest-image {
  width: 180px;
  height: 180px;
  object-fit: cover;
  border-radius: 3px;
  display: block;
  background: var(--panel);
  border: 1px solid var(--border);
}

.digest-image-meta {
  font-size: 0.8125rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.digest-image-title {
  font-weight: 500;
  color: var(--text);
}

/* ── Unified Archive page (history + click-performance dashboard) ───────── */

.archive-section { margin: 1.25rem 0 1.5rem; }
.archive-section:first-of-type { margin-top: 0.5rem; }
.archive-section-heading {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text-muted);
  margin-bottom: 0.6rem;
}
.archive-section-heading::before { content: "▸ "; color: var(--accent); }

/* One-line caveat that sits between an archive section's heading and its
   card — used for the "X was added YYYY-MM-DD" note above Category
   performance. Intentionally low-emphasis. */
.archive-section-note {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-dim);
  line-height: 1.4;
  margin: -0.25rem 0 0.6rem;
}

.archive-dashboard-coverage {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: baseline;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 0.5rem;
  letter-spacing: 0.02em;
}
.archive-dashboard-coverage strong { color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }

.archive-dashboard-fullrow { margin-bottom: 1rem; }
.archive-dashboard-fullrow:last-child { margin-bottom: 0; }

.archive-dashboard-grid-equal {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.archive-dashboard-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  margin-bottom: 1rem;
  align-items: start;
}
@media (max-width: 900px) {
  .archive-dashboard-grid-3 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
@media (max-width: 600px) {
  .archive-dashboard-grid-3 { grid-template-columns: minmax(0, 1fr); }
}

.archive-dashboard-stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem;
  margin: 1rem 0 1.25rem;
}
.archive-dashboard-stat {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
  position: relative;
  box-shadow: var(--shadow);
}
.archive-dashboard-stat::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent) 0%, transparent 60%);
  opacity: 0.5;
  border-radius: var(--radius) var(--radius) 0 0;
}
.archive-dashboard-stat-label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}
.archive-dashboard-stat-value {
  font-family: var(--font-mono);
  font-size: 1.7rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--accent);
}
.archive-dashboard-stat-detail {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
  letter-spacing: 0.02em;
}

@media (max-width: 720px) {
  .archive-dashboard-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.archive-dashboard-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: 1rem;
  margin-bottom: 1rem;
  align-items: start;
}
@media (max-width: 720px) {
  .archive-dashboard-grid { grid-template-columns: minmax(0, 1fr); }
}

.archive-dashboard-card { padding: 1rem 1.1rem 1.1rem; }

.archive-dashboard-toggles {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}
.archive-dashboard-toggle {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.3rem 0.7rem;
  border-radius: 3px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.15s;
}
.archive-dashboard-toggle:hover { color: var(--text); border-color: var(--border-strong); }
.archive-dashboard-toggle.is-active {
  background: rgba(0, 212, 192, 0.12);
  color: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 8px rgba(0, 212, 192, 0.2);
}

.archive-dashboard-bars { display: flex; flex-direction: column; gap: 0.4rem; }
.archive-dashboard-bar {
  display: grid;
  grid-template-columns: minmax(0, 11rem) 1fr auto;
  gap: 0.6rem;
  align-items: center;
  font-family: inherit;
  font-size: 0.8125rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 0.3rem 0.5rem;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  width: 100%;
}
.archive-dashboard-bar:hover { background: rgba(255, 255, 255, 0.03); }
.archive-dashboard-bar.is-selected {
  border-color: var(--accent);
  background: rgba(0, 212, 192, 0.06);
}
.archive-dashboard-bar-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.archive-dashboard-bar-track {
  height: 0.5rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 2px;
  overflow: hidden;
  display: block;
  min-width: 0;
  border: 1px solid rgba(255, 255, 255, 0.04);
}
.archive-dashboard-bar-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-violet));
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(0, 212, 192, 0.4);
}
.archive-dashboard-bar.is-selected .archive-dashboard-bar-fill { background: var(--accent-2); box-shadow: 0 0 8px rgba(255, 155, 62, 0.4); }
.archive-dashboard-bar-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
  color: var(--accent);
  min-width: 3rem;
  text-align: right;
}

.archive-dashboard-selected-head { margin-bottom: 0.6rem; }
.archive-dashboard-selected-issue { font-weight: 600; font-size: 0.95rem; }
.archive-dashboard-selected-title { color: var(--text); font-size: 0.95rem; }
.archive-dashboard-selected-title .muted { color: var(--text-muted); font-style: italic; }
.archive-dashboard-selected-stats {
  list-style: none; padding: 0; margin: 0.25rem 0 0.6rem;
  display: flex; flex-direction: column; gap: 0.05rem;
}
.archive-dashboard-selected-stats li {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  padding: 0.3rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  font-size: 0.875rem;
}
.archive-dashboard-selected-stats li:first-child { border-top: none; }
.archive-dashboard-selected-k { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.78rem; }
.archive-dashboard-selected-v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; color: var(--accent); }
.archive-dashboard-selected-link { font-size: 0.8125rem; }
.archive-dashboard-selected-link a { color: var(--accent); text-decoration: none; border-bottom: 1px dotted var(--accent); }
.archive-dashboard-selected-link a:hover { color: var(--accent-hover); }

.archive-dashboard-axis-note { font-size: 0.75rem; color: var(--text-muted); margin-bottom: 0.5rem; }

.archive-dashboard-bubble { width: 100%; }
.archive-dashboard-svg { width: 100%; height: auto; display: block; }
.archive-dashboard-axis-line { stroke: rgba(255, 255, 255, 0.2); stroke-width: 1; }
.archive-dashboard-grid-line { stroke: rgba(255, 255, 255, 0.06); stroke-width: 1; }
.archive-dashboard-tick { font-size: 10px; fill: var(--text-muted); font-family: var(--font-mono); }
.archive-dashboard-axis-label { font-size: 10px; fill: var(--text-muted); font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.1em; }
.archive-dashboard-bubble-g { cursor: pointer; }
.archive-dashboard-bubble-circle {
  fill: var(--accent);
  fill-opacity: 0.2;
  stroke: var(--accent);
  stroke-width: 1;
}
.archive-dashboard-bubble-g:hover .archive-dashboard-bubble-circle { fill-opacity: 0.38; }
.archive-dashboard-bubble-g.is-selected .archive-dashboard-bubble-circle {
  fill: var(--accent-2);
  fill-opacity: 0.45;
  stroke: var(--accent-2);
  stroke-width: 2;
}
.archive-dashboard-bubble-label { font-size: 10px; fill: var(--text); font-family: var(--font-mono); pointer-events: none; }

/* The table now lives in a full-width row, so it should fit without
   horizontal scroll at typical desktop widths. We keep overflow-x: auto
   only as a safety net for narrow viewports. */
.archive-dashboard-table-wrap { width: 100%; overflow-x: auto; }
.archive-dashboard-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
  table-layout: auto;
}
.archive-dashboard-table th,
.archive-dashboard-table td {
  padding: 0.45rem 0.55rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  vertical-align: top;
}
.archive-dashboard-th {
  background: rgba(255, 255, 255, 0.02);
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.68rem;
  cursor: pointer;
  user-select: none;
  border-bottom: 1px solid var(--border-strong);
}
.archive-dashboard-sort-btn {
  font: inherit;
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  cursor: pointer;
  letter-spacing: inherit;
  text-transform: inherit;
}
.archive-dashboard-sort-indicator { display: inline-block; width: 0.7em; margin-left: 0.2em; }
.archive-dashboard-th.is-sorted-asc .archive-dashboard-sort-indicator::after { content: '▲'; font-size: 0.7em; }
.archive-dashboard-th.is-sorted-desc .archive-dashboard-sort-indicator::after { content: '▼'; font-size: 0.7em; }
.archive-dashboard-th-left { text-align: left; }
.archive-dashboard-th-right { text-align: right; }
.archive-dashboard-td-left { text-align: left; }
.archive-dashboard-td-right { text-align: right; font-variant-numeric: tabular-nums; }
/* Title column gets the leftover space: no max-width cap at desktop, wraps
   gracefully if a headline runs long. The numeric columns (right-aligned,
   tabular-nums) absorb their natural width. */
.archive-dashboard-td-title {
  white-space: normal;
  overflow-wrap: anywhere;
}
.archive-dashboard-td-title .muted { color: var(--text-muted); font-style: italic; }
.archive-dashboard-th-left,
.archive-dashboard-td-left { white-space: nowrap; }
.archive-dashboard-th-left[data-key="title"],
.archive-dashboard-td-title { white-space: normal; }
.archive-dashboard-tr { cursor: pointer; }
.archive-dashboard-tr:hover { background: rgba(255, 255, 255, 0.03); }
.archive-dashboard-tr.is-selected { background: rgba(255, 155, 62, 0.08); }
.archive-dashboard-tr a { color: var(--accent); text-decoration: none; border-bottom: 1px dotted var(--accent); }
.archive-dashboard-tr a:hover { color: var(--accent-hover); }

.archive-dashboard-learnings {
  background: linear-gradient(180deg, #0d141c 0%, #0a1117 100%);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem 1.25rem;
  margin: 0.5rem 0 0.75rem;
  position: relative;
  box-shadow: 0 0 24px rgba(0, 212, 192, 0.08), inset 0 1px 0 rgba(0, 212, 192, 0.15);
}
.archive-dashboard-learnings::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent-violet) 50%, transparent 100%);
  border-radius: var(--radius) var(--radius) 0 0;
}
.archive-dashboard-learnings h3 {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent);
  margin-bottom: 0.6rem;
  font-weight: 600;
}
.archive-dashboard-learnings h3::before { content: "» "; }
.archive-dashboard-learnings-caveat { font-size: 0.8125rem; color: var(--text-muted); margin-bottom: 0.85rem; font-style: italic; }
.archive-dashboard-learnings-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.75rem;
}
@media (max-width: 720px) {
  .archive-dashboard-learnings-grid { grid-template-columns: 1fr; }
}
.archive-dashboard-learning {
  background: rgba(0, 212, 192, 0.04);
  border: 1px solid rgba(0, 212, 192, 0.15);
  border-radius: 3px;
  padding: 0.75rem 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.archive-dashboard-learning.is-clickable { cursor: pointer; transition: background 0.15s, border-color 0.15s; }
.archive-dashboard-learning.is-clickable:hover { background: rgba(0, 212, 192, 0.08); border-color: var(--accent); }
.archive-dashboard-learning-label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--accent);
}
.archive-dashboard-learning-text { font-size: 0.875rem; line-height: 1.45; margin: 0; color: var(--text); }

.archive-dashboard-footnote { font-size: 0.75rem; color: var(--text-muted); margin: 0.5rem 0 0; font-family: var(--font-mono); letter-spacing: 0.02em; }

/* ── Status chip (utility) ──────────────────────────────────────────────── */

.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 0.2rem 0.55rem;
  border-radius: 2px;
  background: var(--panel-3);
  color: var(--text-muted);
  border: 1px solid var(--border);
}
.status-chip::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-dim);
}
.status-chip.is-live { color: var(--accent); border-color: rgba(0, 212, 192, 0.3); }
.status-chip.is-live::before { background: var(--accent); box-shadow: 0 0 6px var(--accent); }

/* ── Click-import page: dark-theme overrides for inline styles ──────────── */
/* clickImport.js uses inline borders/colors that were tuned for the light
   theme. Override them with high-specificity selectors so the table chrome
   blends with the Signal Room theme without rewriting that view. */

.clicks-table { color: var(--text); }
.clicks-table tr { border-color: var(--border) !important; }
.clicks-table thead tr { border-bottom-color: var(--border-strong) !important; }
.clicks-table th {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}
.clicks-table td a { color: var(--accent); text-decoration: none; border-bottom: 1px dotted var(--accent); }
.clicks-table td a:hover { color: var(--accent-hover); }

/* Horizontal-scroll wrapper around the clicks-table. Below the table's
   min-width, the wrapper scrolls horizontally rather than collapsing the
   URL/Title column to one-character-per-line wrapping. Replaces the prior
   inline `word-break: break-all` on URL cells (now `class="clicks-url"`),
   which forced character-by-character breaks at any column width. */
.clicks-table-wrap {
  overflow-x: auto;
  margin-bottom: 1rem;
}
.clicks-table-wrap > .clicks-table {
  min-width: 720px;
}
.clicks-table .clicks-url {
  overflow-wrap: anywhere;
  min-width: 240px;
}

/* ── Archive-import preview: 6-column form layout ───────────────────────── */
/* The archive-import preview adds two <select> columns (Newsletter category
   and Page section / type) that need predictable, clickable widths even when
   the table has to scroll horizontally inside its wrapper. Scoped tightly
   under .archive-import-preview so the other .clicks-table renderings
   (issue clicks page, click-import preview, unmatched-categorize) keep
   their existing column behavior. */
.clicks-table-wrap.archive-import-preview { background: var(--panel); }
.clicks-table-wrap.archive-import-preview > .clicks-table { min-width: 880px; }

.archive-import-preview .archive-import-th-rank   { width: 36px; }
.archive-import-preview .archive-import-th-url    { width: auto; }
.archive-import-preview .archive-import-th-clicks { width: 64px; }
.archive-import-preview .archive-import-th-rate   { width: 64px; }
.archive-import-preview .archive-import-th-cat    { width: 220px; }
.archive-import-preview .archive-import-th-ctx    { width: 200px; }

/* Ensure the per-row select cells are always wide enough that the <select>
   element is comfortably clickable; the overflow-x: auto wrapper handles
   anything narrower than the table's min-width. */
.archive-import-preview select[name="cat_value[]"],
.archive-import-preview select[name="ctx_value[]"] {
  min-width: 180px;
}

.click-summary { color: var(--accent); font-family: var(--font-mono); letter-spacing: 0.04em; }
.text-muted { color: var(--text-muted); }
.required { color: var(--danger); }
.field-hint { color: var(--text-dim); font-family: var(--font-mono); font-size: 0.7rem; text-transform: none; letter-spacing: 0.02em; margin-left: 0.4rem; }

/* ── Empty state polish ─────────────────────────────────────────────────── */

.empty-state {
  background: var(--panel);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 2rem 1.5rem;
  margin: 1rem 0;
}
.empty-state p {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.empty-state p::before { content: "// "; color: var(--text-dim); }
.empty-state a { color: var(--accent); text-decoration: none; border-bottom: 1px dotted var(--accent); }
.empty-state a::before { content: ""; }
.empty-state a:hover { color: var(--accent-hover); }

/* ── Add-image page tweaks ──────────────────────────────────────────────── */
.add-form { display: flex; flex-direction: column; gap: 1.25rem; }

/* ── Headline form refinement ───────────────────────────────────────────── */
.headline-form label {
  font-family: var(--font-mono);
  letter-spacing: 0.12em;
  color: var(--text-muted);
}
.headline-form label::before { content: "▸ "; color: var(--accent); }

/* ── Archive sub-page back link ─────────────────────────────────────────── */
.archive-back-link {
  margin: 0 0 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
}
.archive-back-link a {
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 1px dotted transparent;
  transition: color 0.15s, border-color 0.15s;
}
.archive-back-link a:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* ── /archive top clicked links card: caption + "See full list" link ────── */
.click-card-caption {
  margin: -0.25rem 0 0.45rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.click-card-action {
  margin: 0.6rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.click-card-action a { color: var(--accent); text-decoration: none; }
.click-card-action a:hover { text-decoration: underline; }

/* ── /archive/top-links page ────────────────────────────────────────────── */
.top-links-controls { margin: 0.5rem 0 0.5rem; }

.top-links-toggles {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
}
.top-links-toggle {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.35rem 0.75rem;
  border-radius: 3px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-muted);
  text-decoration: none;
  transition: all 0.15s;
}
.top-links-toggle:hover { color: var(--text); border-color: var(--border-strong); }
.top-links-toggle.is-active {
  background: rgba(0, 212, 192, 0.12);
  color: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 8px rgba(0, 212, 192, 0.2);
}

.top-links-note {
  font-size: 0.78rem;
  color: var(--text-muted);
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
  margin: 0 0 1rem;
}

.top-links-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
@media (max-width: 900px) {
  .top-links-layout { grid-template-columns: minmax(0, 1fr); }
}

.top-links-main, .top-links-side { min-width: 0; }

/* Bar chart card */
.top-links-chart-card { padding: 1rem 1.1rem 1.1rem; }
.top-links-bars {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  counter-reset: top-links-bar;
}
.top-links-bar-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto;
  gap: 0.7rem;
  align-items: center;
  font-size: 0.85rem;
}
.top-links-bar-label {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}
.top-links-bar-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.top-links-bar-title a { color: var(--text); text-decoration: none; }
.top-links-bar-title a:hover { color: var(--accent); }
.top-links-bar-meta {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.top-links-source {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
}
.top-links-bar-track {
  height: 0.55rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 2px;
  overflow: hidden;
  display: block;
  min-width: 0;
  border: 1px solid rgba(255, 255, 255, 0.04);
}
.top-links-bar-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-violet));
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(0, 212, 192, 0.4);
}
.top-links-bar-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.8rem;
  color: var(--accent);
  min-width: 3rem;
  text-align: right;
}

/* Ranked top-50 list */
.top-links-list-card { padding: 0.85rem 1rem; }
.top-links-rank-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.top-links-rank-row {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr);
  gap: 0.6rem;
  padding: 0.55rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  align-items: baseline;
}
.top-links-rank-row:first-child { border-top: none; padding-top: 0.25rem; }
.top-links-rank-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.8rem;
  color: var(--accent);
  letter-spacing: 0.02em;
}
.top-links-rank-body { min-width: 0; }
.top-links-rank-title {
  font-size: 0.92rem;
  line-height: 1.35;
  word-wrap: break-word;
}
.top-links-rank-title a { color: var(--text); text-decoration: none; }
.top-links-rank-title a:hover { color: var(--accent); }
.top-links-rank-meta {
  margin-top: 0.15rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}

/* Paste-ready by-category card */
.top-links-paste-card { padding: 0.85rem 1rem 1rem; }
.top-links-paste-head {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
@media (min-width: 540px) {
  .top-links-paste-head {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.75rem;
  }
}
.top-links-paste-help {
  margin: 0;
  font-size: 0.78rem;
  color: var(--text-muted);
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
  flex: 1;
  min-width: 0;
}
.top-links-paste-cat { margin-top: 0.85rem; }
.top-links-paste-cat:first-of-type { margin-top: 0.4rem; }
.top-links-paste-cat-heading {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent);
  margin: 0 0 0.35rem;
}
.top-links-paste-cat-empty {
  margin: 0;
  font-size: 0.78rem;
  color: var(--text-dim);
  font-family: var(--font-mono);
  letter-spacing: 0.02em;
  font-style: italic;
}

/* ── /archive Learnings card + /archive/learnings page ──────────────────── */
.learnings-card { padding: 0.85rem 1rem 1rem; }

.learnings-list {
  list-style: none;
  padding: 0;
  margin: 0.25rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}
.learnings-row {
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  padding-top: 0.45rem;
}
.learnings-row:first-child { border-top: none; padding-top: 0; }
.learnings-headline {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text);
  margin: 0 0 0.15rem;
}
.learnings-body {
  margin: 0;
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.45;
}
.learnings-tentative {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 0.12rem 0.4rem;
  border-radius: 2px;
  border: 1px solid var(--accent-2);
  color: var(--accent-2);
  background: rgba(255, 155, 62, 0.08);
  vertical-align: middle;
  margin-left: 0.35rem;
}

/* Bucket / table-like rows on the full Learnings page */
.learnings-bucket-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.learnings-bucket-row {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 1fr) auto;
  grid-template-areas:
    "label track num"
    "label meta meta";
  gap: 0.5rem 0.75rem;
  align-items: center;
  padding: 0.3rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.04);
}
.learnings-bucket-row:first-child { border-top: none; padding-top: 0; }
.learnings-bucket-label {
  grid-area: label;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}
.learnings-bucket-name {
  font-size: 0.85rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.learnings-bucket-track {
  grid-area: track;
  height: 0.5rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 2px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.04);
  min-width: 0;
}
.learnings-bucket-fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--accent-violet));
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(0, 212, 192, 0.4);
}
.learnings-bucket-num {
  grid-area: num;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.8rem;
  color: var(--accent);
  white-space: nowrap;
}
.learnings-bucket-meta {
  grid-area: meta;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.learnings-bucket-empty {
  grid-area: meta;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
  letter-spacing: 0.02em;
  font-style: italic;
}
.learnings-bucket-row.is-uncategorized .learnings-bucket-name {
  color: var(--text-muted);
  font-style: italic;
}
.learnings-uncat-tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 0.1rem 0.35rem;
  margin-top: 0.1rem;
  align-self: flex-start;
}

@media (max-width: 600px) {
  .learnings-bucket-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "label num"
      "track track"
      "meta meta";
  }
}

.learnings-card-foot {
  margin: 0.6rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
  letter-spacing: 0.02em;
}

/* Uncategorized high performers section */
.learnings-uncat-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}
.learnings-uncat-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "title  cta"
    "meta   cta";
  gap: 0.15rem 0.75rem;
  align-items: center;
  padding: 0.55rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.learnings-uncat-row:first-child { border-top: none; padding-top: 0.25rem; }
.learnings-uncat-title {
  grid-area: title;
  font-size: 0.92rem;
  line-height: 1.35;
  word-wrap: break-word;
}
.learnings-uncat-title a { color: var(--text); text-decoration: none; }
.learnings-uncat-title a:hover { color: var(--accent); }
.learnings-uncat-meta {
  grid-area: meta;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.learnings-source {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
}
.learnings-categorize {
  grid-area: cta;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid var(--accent);
  border-radius: 3px;
  padding: 0.3rem 0.6rem;
  background: rgba(0, 212, 192, 0.06);
  align-self: center;
}
.learnings-categorize:hover {
  background: rgba(0, 212, 192, 0.16);
  color: var(--accent-hover);
}

/* ─────────────────────────────────────────────────────────────────────────────
   Responsive pass — preserves desktop look; tightens spacing/typography and
   stacks action rows on phones/tablets. Builds on the existing
   `@media (max-width: 540px)` block above; rules here apply at wider phone
   breakpoints (≤700px) plus a tighter pass for ≤480px.
   ──────────────────────────────────────────────────────────────────────────── */

/* Belt-and-braces: never let a stray fixed-width element create a horizontal
   scrollbar on mobile. Images already get max-width via card styles; this
   covers any future authored content. */
img, svg, video, canvas, iframe { max-width: 100%; }

@media (max-width: 700px) {
  /* Header — compact padding so the title + 4 nav items fit a phone row.
     The header still flexes title-on-left / nav-on-right; the nav itself
     wraps if needed. */
  .site-header {
    padding-left: max(0.875rem, env(safe-area-inset-left));
    padding-right: max(0.875rem, env(safe-area-inset-right));
    height: auto;
    min-height: 52px;
    padding-top: 0.4rem;
    padding-bottom: 0.4rem;
    gap: 0.5rem;
    flex-wrap: wrap;
  }
  .site-header nav {
    gap: 0.4rem;
    flex-wrap: wrap;
    justify-content: flex-end;
  }
  .site-header nav a {
    padding: 0.35rem 0.5rem;
  }
  /* Brand: shrink the Delightful mark so it doesn't dominate the row. */
  .brand-logo { width: 100px; max-height: 28px; }

  /* Main container — slightly tighter padding, no horizontal overflow. */
  main {
    padding: 1.5rem 1rem 3rem;
    max-width: 100%;
  }
  main.main-wide,
  main.main-archive { max-width: 100%; }

  /* Page header — slightly smaller h1, tighter eyebrow letter-spacing. */
  .page-header { margin-bottom: 1.25rem; }
  .page-header h1 { font-size: 1.25rem; }
  .page-eyebrow { letter-spacing: 0.12em; }

  /* Inbox header — stack title above actions and let actions span full width. */
  .inbox-header {
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
  }
  .header-actions {
    width: 100%;
    gap: 0.5rem;
  }
  .header-actions .btn { flex: 1 1 calc(50% - 0.25rem); min-width: 0; }

  /* Archive confirmation — keep label inline, but let the input span the row
     and put the two buttons on a tidy 2-up under that. */
  .archive-form-row {
    gap: 0.5rem;
  }
  .archive-form-row label { width: 100%; }
  .archive-form-row input[type="number"] { width: 100%; flex: 1 1 auto; }
  .archive-form-row .btn { flex: 1 1 calc(50% - 0.25rem); }

  /* Inbox link cards — stack the row's preview text above its action buttons
     so long titles/quotes don't push actions off-screen. The card-preview
     already had `flex: 1; min-width: 0`, so wrapping was already correct on
     desktop; on mobile the cleanest move is a vertical stack with actions
     pinned to the right edge. */
  .link-top {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
  }
  .link-actions {
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 1rem;
  }

  /* Category card — slightly tighter padding so the cyan top stripe and
     content reach close to the viewport edges without feeling cramped. */
  .category-group { padding: 0.75rem 0.875rem 0.25rem; }

  /* Inbox images strip — let thumbs flex with the viewport rather than
     locking to 140px when the row only has room for one. */
  .pool-image { width: calc(50% - 0.5rem); }
  .pool-image-img { width: 100%; height: auto; aspect-ratio: 4 / 3; }

  /* Edit form action row — primary first, ghost second, both full width. */
  .edit-form .actions { flex-direction: column; align-items: stretch; }
  .edit-form .actions .btn { width: 100%; }
}

@media (max-width: 480px) {
  /* Tighter brand lockup on very narrow phones so the nav still fits. */
  .brand-logo { width: 90px; }
  .brand-product { font-size: 0.65rem; letter-spacing: 0.1em; }

  main { padding: 1.25rem 0.875rem 2.75rem; }

  .page-header h1 { font-size: 1.125rem; }
  .page-header { margin-bottom: 1rem; }

  /* Stack capture/copy/archive buttons one per row at very narrow widths —
     two-column gets cramped under ~360–400px. */
  .header-actions { flex-direction: column; gap: 0.45rem; }
  .header-actions .btn { width: 100%; flex: none; }

  /* Archive confirm/cancel still 2-up under 480 — that pair fits cleanly. */
  .archive-form-row .btn { flex: 1 1 calc(50% - 0.25rem); }

  /* Slightly tighter card paddings */
  .category-group { padding: 0.625rem 0.75rem 0.25rem; }

  /* Card text wraps anywhere when a long URL has no break opportunities. */
  .card-preview, .link-title { overflow-wrap: anywhere; }

  /* Image thumbs single column at the smallest sizes. */
  .pool-image { width: 100%; }
  .image-editor { max-width: 100%; }
  /* Bigger tap target for the overlaid delete control on touch screens. */
  .image-delete-btn { width: 2rem; height: 2rem; font-size: 1.25rem; }
}

/* ── Collection tags ────────────────────────────────────────────────────────
   Optional "shelves" surfaced on review + inbox cards. Pills are compact and
   intentionally low-emphasis so they don't compete with the quote/title row.
   Never rendered in the digest output. */

.link-tags-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.625rem;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px dashed rgba(255, 255, 255, 0.05);
}

.tag-pills {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin: 0;
  padding: 0;
}

.tag-pill {
  display: inline-block;
  padding: 0.18rem 0.55rem;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  background: rgba(167, 139, 250, 0.08);
  color: #c7b2ff;
  border: 1px solid rgba(167, 139, 250, 0.25);
  white-space: nowrap;
}

.tag-pills-empty {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
  font-style: italic;
}

.tag-edit-btn {
  margin-left: auto;
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0.1rem 0;
  transition: color 0.15s;
}
.tag-edit-btn:hover { color: var(--accent); }

.link-tags-row.tag-editing {
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
}

.tag-editor-hint,
.tag-editor-empty {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
  line-height: 1.4;
}

.tag-options {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem 0.5rem;
  margin: 0;
  padding: 0;
}

.tag-option {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  user-select: none;
}

.tag-option input[type="checkbox"] {
  width: auto;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.tag-option .tag-pill {
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-muted);
  border-color: var(--border);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.tag-option input[type="checkbox"]:checked + .tag-pill {
  background: rgba(167, 139, 250, 0.12);
  color: #c7b2ff;
  border-color: rgba(167, 139, 250, 0.4);
}

.tag-editor-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.tag-editor-status {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
}
.tag-editor-status[data-state="error"] { color: var(--danger); }
.tag-editor-status[data-state="saving"] { color: var(--text-muted); }

/* ── Review page: suggested + other collection tags ─────────────────────── */

.collections-field .tag-suggestions,
.collections-field .tag-all {
  margin-top: 0.625rem;
}

.tag-suggestions-label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin-bottom: 0.375rem;
}

.tag-empty-hint {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
}

.tag-option-suggested .tag-pill {
  background: rgba(0, 212, 192, 0.08);
  color: var(--accent);
  border-color: rgba(0, 212, 192, 0.35);
}

.tag-option-suggested input[type="checkbox"]:checked + .tag-pill {
  background: rgba(0, 212, 192, 0.18);
  color: var(--accent-hover);
  border-color: var(--accent);
}

/* ── Readers clicked (top-3 from last archived issue with click data) ──────
   Sits between the scratch pad and the captured-link sections. Server-side
   helper buildReadersClickedView() in src/views/inbox.js produces the
   visible card + a hidden plain/HTML pair that the Copy-all clipboard
   handler splices in above the captured-link payload. Hidden entirely
   when no archived issue has click data yet. */

.readers-clicked-section {
  margin-bottom: 1.25rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.875rem 1rem;
  position: relative;
  box-shadow: var(--shadow);
}

.readers-clicked-section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent-violet, var(--accent)) 0%, transparent 60%);
  opacity: 0.5;
  border-radius: var(--radius) var(--radius) 0 0;
}

.readers-clicked-section .category-heading {
  padding-bottom: 0.25rem;
}

.readers-clicked-hint {
  margin: 0 0 0.625rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
}

/* Waiting state — latest archived issue exists but its click report
   has not been imported yet. Visible on the page, intentionally NOT
   included in Copy all (the view omits the hidden clipboard pair). */
.readers-clicked-waiting {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  font-style: italic;
}

.readers-clicked-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}

.readers-clicked-row {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.9375rem;
  line-height: 1.5;
}

.readers-clicked-rank {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-muted);
  min-width: 1.5rem;
  flex-shrink: 0;
}

/* ── /settings/tags — collection tag management UI v1 ──────────────────────
   Server-rendered table + per-row form. POST → redirect → reload; no JS
   required. Visual style matches the archive/inbox cards (panel surface,
   muted mono headings) without inventing a new design system. */

.tags-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1.5rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.tags-table th,
.tags-table td {
  text-align: left;
  vertical-align: top;
  padding: 0.65rem 0.85rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
}

.tags-table thead th {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  background: var(--panel-2);
  border-bottom-color: var(--border);
}

.tags-table tbody tr:last-child td { border-bottom: none; }

.tags-col-name        { width: 14%; }
.tags-col-slug        { width: 12%; }
.tags-col-description { width: 38%; }
.tags-col-usage       { width: 7%;  text-align: right; }
.tags-col-status      { width: 9%;  }
.tags-col-actions     { width: 20%; }

.tags-cell-name   { font-weight: 600; color: var(--text); }
.tags-cell-slug code {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  background: transparent;
  padding: 0;
}
.tags-cell-description { color: var(--text-muted); line-height: 1.45; }
.tags-cell-usage  { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.tags-muted       { color: var(--text-dim); }

.tags-row-inactive .tags-cell-name,
.tags-row-inactive .tags-cell-description,
.tags-row-inactive .tags-cell-slug code {
  opacity: 0.7;
}

.tags-group-divider td {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-dim);
  background: var(--panel-2);
  padding: 0.5rem 0.85rem;
}

.tag-status-pill {
  display: inline-block;
  padding: 0.12rem 0.5rem;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border: 1px solid;
}
.tag-status-active {
  background: rgba(0, 212, 192, 0.08);
  color: var(--accent);
  border-color: rgba(0, 212, 192, 0.35);
}
.tag-status-inactive {
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-dim);
  border-color: var(--border);
}

.tags-cell-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.6rem;
  align-items: center;
}
.tags-inline-form { display: inline; margin: 0; padding: 0; }
.tags-action-btn {
  background: none;
  border: 1px solid transparent;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0.2rem 0.45rem;
  border-radius: 3px;
  text-decoration: none;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.tags-action-btn:hover {
  color: var(--accent);
  border-color: var(--border);
  background: var(--panel-2);
}

/* Inline editing row */
.tags-row-editing td { background: var(--panel-2); }
.tags-edit-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem 1rem;
  margin-bottom: 0.5rem;
}
.tags-edit-grid .tags-edit-description { grid-column: 1 / -1; }
.tags-edit-form input[disabled] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Add tag card */
.tags-add-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  box-shadow: var(--shadow);
}
.tags-add-card h2 {
  margin-top: 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
}

/* Status banner (?error=… / ?ok=…) */
.alert {
  margin-bottom: 1rem;
  padding: 0.6rem 0.85rem;
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  border: 1px solid;
}
.alert-error {
  background: rgba(255, 107, 107, 0.08);
  color: #ff8f8f;
  border-color: rgba(255, 107, 107, 0.35);
}
.alert-ok {
  background: rgba(0, 212, 192, 0.08);
  color: var(--accent);
  border-color: rgba(0, 212, 192, 0.35);
}

@media (max-width: 760px) {
  /* Hide secondary columns on narrow viewports — keep Name / Status /
     Actions visible, fold slug + description into the name cell via
     overflow wrapping. */
  .tags-col-slug, .tags-cell-slug,
  .tags-col-description, .tags-cell-description {
    display: none;
  }
  .tags-edit-grid { grid-template-columns: 1fr; }
}

/* Small footer link beneath /settings/tags pointing to /archive/catalogue.
   Low-emphasis cross-link, not a primary nav entry. */
.tags-footer-link {
  margin-top: 1rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
}
.tags-footer-link a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dotted var(--accent);
}
.tags-footer-link a:hover { color: var(--accent-hover); }

/* Shelf management entry point on /settings/tags. Sits below the tag
   table + footer link, with a visible divider so it doesn't blur into
   the tag list above. Each list item pairs the link with a one-line
   helper. */
.settings-section { margin-top: 1.75rem; }
.settings-section-divider {
  border: 0;
  border-top: 1px dashed var(--border);
  margin: 0 0 1rem;
}
.settings-section-heading {
  margin: 0 0 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}
.settings-section-help {
  margin: 0 0 0.85rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}
.settings-shelf-management-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}
.settings-shelf-management-link {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  border-bottom: 1px dotted var(--accent);
}
.settings-shelf-management-link:hover { color: var(--accent-hover); }
.settings-shelf-management-help {
  display: block;
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 0.15rem;
}

/* Admin backlink on /settings/shelves/:slug — quiet "← View public shelf"
   under the header so the editor can hop to the reader surface. */
.admin-shelf-backlink {
  margin: 0.5rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
}
.admin-shelf-backlink a {
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.admin-shelf-backlink a:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* Admin-only "Settings" link in the private shelf-page header. Modest,
   sits in the header action area below the stats line. */
.shelf-header-actions { margin: 0.85rem 0 0; }

/* ── /archive/catalogue — the link catalogue ───────────────────────────────
   Replaces the retired /archive/tagging styles (that page now 301s here).

   Large rows, 50 at a time, appended by "Show more". Every control
   auto-saves; the per-row/per-control status spans are the only feedback,
   so they must stay visible rather than collapsing the layout when empty.

   The three axes are visually separated on purpose — metadata grid, then
   collection tags, then shelf groups — because conflating tags with groups
   is the single easiest mistake to make with this data model. */

.catalogue-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.6rem 1rem;
  margin-bottom: 1rem;
  padding: 0.9rem 1rem;
  background: var(--panel, #121820);
  border: 1px solid var(--border, #223041);
  border-radius: 8px;
}
.catalogue-filter { display: flex; flex-direction: column; gap: 0.25rem; }
.catalogue-filter-search { flex: 1 1 16rem; }
.catalogue-filter-search input { width: 100%; }
.catalogue-filter-label {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted, #7d8fa3);
}
.catalogue-filter-check {
  flex-direction: row;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--muted, #7d8fa3);
}
.catalogue-filter-actions {
  display: flex;
  gap: 0.5rem;
  margin-left: auto;
}

/* Gap chips are links, so each filtered view is bookmarkable and the browser
   back button behaves. A chip whose count is zero is dimmed rather than
   hidden — "this gap is clear" is information worth showing. */
.catalogue-gaps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: 0.9rem;
}
.catalogue-gaps-label {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted, #7d8fa3);
  margin-right: 0.3rem;
}
.catalogue-gap-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--border, #223041);
  border-radius: 999px;
  font-size: 0.8rem;
  text-decoration: none;
  color: var(--text, #d8e2ec);
  background: var(--panel, #121820);
}
.catalogue-gap-chip:hover { border-color: var(--accent, #4da3ff); }
.catalogue-gap-chip.is-active {
  background: var(--accent, #4da3ff);
  border-color: var(--accent, #4da3ff);
  color: #08121c;
  font-weight: 600;
}
.catalogue-gap-chip-zero { opacity: 0.45; }
.catalogue-gap-chip-count {
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  opacity: 0.85;
}

.catalogue-count {
  font-size: 0.85rem;
  color: var(--muted, #7d8fa3);
  margin: 0 0 0.75rem;
}

.catalogue-list { display: flex; flex-direction: column; gap: 1rem; }

.catalogue-row {
  border: 1px solid var(--border, #223041);
  border-radius: 8px;
  background: var(--panel, #121820);
  padding: 1rem 1.1rem;
}
/* A link kept off the shelves stays fully editable — it is dimmed, not
   disabled, because the whole point of showing it is to let you change
   your mind. */
.catalogue-row.is-shelf-excluded { opacity: 0.72; border-style: dashed; }

.catalogue-row-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.75rem;
  margin-bottom: 0.85rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--border, #223041);
}
.catalogue-row-ident { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }
.catalogue-chip {
  font-size: 0.75rem;
  padding: 0.15rem 0.45rem;
  border-radius: 4px;
  background: var(--chip-bg, #1b2531);
  color: var(--muted, #7d8fa3);
  font-variant-numeric: tabular-nums;
}
.catalogue-chip-inbox { background: #24401f; color: #b6e5a5; }
.catalogue-row-id { font-size: 0.75rem; color: var(--muted, #7d8fa3); }
.catalogue-badge {
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.15rem 0.45rem;
  border-radius: 4px;
}
.catalogue-badge-warn { background: #4a2b16; color: #f0b98a; }
.catalogue-badge-off { background: #3c1f2c; color: #f0a0bd; }
.catalogue-row-url {
  font-size: 0.78rem;
  color: var(--muted, #7d8fa3);
  word-break: break-all;
  margin-left: auto;
  max-width: 100%;
}

.catalogue-row-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.75rem 1rem;
}
.catalogue-field-wide { grid-column: 1 / -1; }
.catalogue-field input,
.catalogue-field select,
.catalogue-field textarea { width: 100%; }

.catalogue-block-label {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted, #7d8fa3);
  margin-bottom: 0.4rem;
}
.catalogue-block-label .label-hint {
  text-transform: none;
  letter-spacing: 0;
  opacity: 0.8;
}

.catalogue-tags { margin-top: 1rem; }
.catalogue-tag-options {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.catalogue-inactive-pills { margin-bottom: 0.4rem; }
/* Marks a tag whose shelf has a group vocabulary — ticking it reveals a
   picker below, and that is not otherwise guessable. */
.catalogue-tag-catalogued { opacity: 0.7; }
.catalogue-tags-status,
.catalogue-group-status,
.catalogue-row-status {
  display: inline-block;
  min-height: 1em;
  font-size: 0.75rem;
  color: var(--muted, #7d8fa3);
  margin-left: 0.5rem;
}
.catalogue-tags-status.is-error,
.catalogue-group-status.is-error,
.catalogue-row-status.is-error { color: #ff8b8b; }

.catalogue-groups { margin-top: 1rem; }
.catalogue-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0;
}
.catalogue-group[hidden] { display: none; }
.catalogue-group-shelf {
  min-width: 10rem;
  font-size: 0.8rem;
  text-transform: capitalize;
  color: var(--text, #d8e2ec);
}
/* Orphan: a group stored for a shelf the link is no longer tagged into.
   Every other read path scopes these away, so this is the only place one
   can be seen — flag it loudly. */
.catalogue-group.is-orphan { border-left: 2px solid #d08a3c; padding-left: 0.6rem; }
.catalogue-group-orphan-note { font-size: 0.72rem; color: #e0a969; }
.catalogue-groups-empty {
  font-size: 0.8rem;
  color: var(--muted, #7d8fa3);
  margin: 0.2rem 0 0;
}

.catalogue-row-foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--border, #223041);
}

/* ── Actioned state ────────────────────────────────────────────────────────
   Two signals doing two jobs on a long maintenance pass.

   Colour lands the instant a save succeeds, so you get feedback without the
   layout moving. Collapse waits until focus leaves the card (see the
   focusout handler in the view's inline script), which is what actually
   shortens the page and lifts remaining work toward the viewport.

   "Done" is decided by the DATA — the card no longer has the gap you are
   filtering on, or has no gaps at all when nothing is filtered — not by
   whether a button was pressed. So a touched-but-still-broken card gets its
   own amber state and cannot quietly pass as finished. */

.catalogue-row.is-touched {
  border-color: #7a6a2e;
  background: linear-gradient(0deg, rgba(190, 150, 40, 0.05), rgba(190, 150, 40, 0.05)), var(--panel, #121820);
}
.catalogue-row.is-resolved {
  border-color: #2f6b45;
  background: linear-gradient(0deg, rgba(60, 190, 120, 0.06), rgba(60, 190, 120, 0.06)), var(--panel, #121820);
}
/* Shelf-excluded stays dashed even once actioned — the two states describe
   different things and both are worth seeing at once. */
.catalogue-row.is-shelf-excluded { border-style: dashed; }

.catalogue-row-state {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
}
.catalogue-row.is-touched .catalogue-row-state { background: #4a3f16; color: #e8cd85; }
.catalogue-row.is-resolved .catalogue-row-state { background: #1e4530; color: #94e0b4; }

.catalogue-row-toggle {
  background: none;
  border: none;
  color: var(--muted, #7d8fa3);
  cursor: pointer;
  font-size: 0.85rem;
  line-height: 1;
  padding: 0.15rem 0.3rem;
}
.catalogue-row-toggle:hover { color: var(--text, #d8e2ec); }

.catalogue-row.is-collapsed { padding: 0.55rem 1.1rem; }
.catalogue-row.is-collapsed .catalogue-row-head {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: none;
}
.catalogue-row-summary {
  margin: 0.3rem 0 0;
  font-size: 0.85rem;
  color: var(--muted, #7d8fa3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.catalogue-progress {
  margin-left: 0.6rem;
  padding: 0.1rem 0.45rem;
  border-radius: 3px;
  background: #1e4530;
  color: #94e0b4;
  font-size: 0.78rem;
}

.catalogue-more {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.25rem 0 2rem;
}
.catalogue-more-note { font-size: 0.8rem; color: var(--muted, #7d8fa3); }

@media (max-width: 760px) {
  .catalogue-filters { flex-direction: column; align-items: stretch; }
  .catalogue-filter-actions { margin-left: 0; }
  .catalogue-row-url { margin-left: 0; }
  .catalogue-group-shelf { min-width: 0; flex-basis: 100%; }
}

/* ── /shelves and /shelves/:slug — read-only collection shelf pages ───────
   Index renders a grid of shelf cards (each is a link to the detail page).
   Detail renders one row per tagged link with a "Current issue" or
   "Issue #N" chip and the link's other collection pills.
   Reuses .tag-pill / .tag-pill-inactive from above. */

/* Index: /shelves — one tile per active tag.
   Renamed from `.shelf-card` so the `.shelf-card` namespace is free for
   per-link cards on /shelves/:slug. */

.shelves-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.75rem;
}

.shelves-grid-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.shelves-grid-card:hover {
  border-color: var(--border-focus);
  box-shadow: 0 0 0 1px rgba(0, 212, 192, 0.18), 0 4px 16px rgba(0, 0, 0, 0.25);
  transform: translateY(-1px);
}

.shelves-grid-card-link {
  display: block;
  padding: 0.85rem 1rem;
  text-decoration: none;
  color: inherit;
}

.shelves-grid-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.35rem;
}

.shelves-grid-card-name {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}

.shelves-grid-card-count {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--accent);
  white-space: nowrap;
}

.shelves-grid-card-description {
  margin: 0 0 0.45rem;
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.4;
}

.shelves-grid-card-slug code {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-dim);
  background: transparent;
  padding: 0;
}

/* Detail page: /shelves/:slug — curated reading-shelf layout.
   Two sections (Featured / All) of `.shelf-card` tiles in responsive
   grids. Each card carries a `.shelf-card-media` block at the top (a
   CSS-only fallback tile today; a real <img> later) above a
   `.shelf-card-body` with title → source → quote → pills. */

.shelf-stats {
  margin-top: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}

.shelf-section { margin-bottom: 1.5rem; }
.shelf-section:last-child { margin-bottom: 0; }

.shelf-section-heading {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin: 0 0 0.75rem;
}

.shelf-featured .shelf-section-heading { color: var(--accent); }

/* Card grid containers. `auto-fit` + `minmax` gives free responsive
   behavior: 3-up on wide screens for Featured, 2-up for All, stacking
   to 1 column when the viewport is too narrow to honor the minmax. */
.shelf-card-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.85rem;
}
.shelf-card-grid-featured {
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.shelf-card-grid-all {
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
}

/* Card base. */
.shelf-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.shelf-card:hover {
  border-color: var(--border-focus);
  box-shadow: 0 0 0 1px rgba(0, 212, 192, 0.18), 0 4px 16px rgba(0, 0, 0, 0.25);
}

.shelf-card-featured {
  /* Slightly stronger surface treatment than the All-links cards. */
  border-color: rgba(0, 212, 192, 0.22);
}
.shelf-card-featured:hover {
  transform: translateY(-1px);
  box-shadow: 0 0 0 1px rgba(0, 212, 192, 0.28), 0 8px 24px rgba(0, 0, 0, 0.35);
}

/* Media tile. Future-proofed: a real preview image would slot in as
   <img class="shelf-card-media-image"> inside this block, with the
   fallback hidden via :has() or a server-side flag. For now the
   fallback is always present and is the only thing rendered.
   `aria-hidden="true"` in the markup keeps screen readers off it —
   every label in here is duplicated in `.shelf-card-body`. */
.shelf-card-media {
  position: relative;
  width: 100%;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
  flex-shrink: 0;
}
.shelf-card-featured .shelf-card-media { aspect-ratio: 16 / 9; }
.shelf-card-all      .shelf-card-media { aspect-ratio: 24 / 7; }

/* Article-level preview image (when `links.preview_image_url` is set).
   Fills the aspect-ratio box; the patterned fallback below is NOT
   rendered in this case (the view emits one or the other, never both).
   If the remote asset 404s the inline `onerror` handler hides the
   <img>; the corner badge stays positioned on the empty tile. */
.shelf-card-media-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Surface color shows through while the asset is loading and as the
     final layer beneath a transparent / partially-loaded image. */
  background: var(--panel-2);
}

/* Animated CSS-only fallback tile for cards with no `preview_image_url`.
   Six deterministic variants (`.variant-0`..`.variant-5`) — the view
   layer picks one stably per card via `pickFallbackVariant` in
   src/views/shelves.js. Each variant overrides a small set of CSS
   custom properties so the same base recipe lands as a different
   visual on each card without needing per-variant rule blocks.
   Visuals built up in three layers:
     1. The element itself: dark panel + faint phosphor dot-grid +
        horizontal scanlines, slowly drifting via background-position.
     2. `::before`: the existing cyan top-stripe, per-variant direction
        and intensity. Sits above the sweep so the editorial anchor
        line stays present.
     3. `::after`: a slow diagonal sweep (rotating linear gradient).
        Always-on but very low contrast. Sits below the stripe and the
        source label, so neither becomes hard to read.
   `prefers-reduced-motion: reduce` disables the loops but keeps every
   visual layer in place (see the @media block below). */
.shelf-card-media-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  text-align: center;
  overflow: hidden;

  /* Per-variant tuning surface — defaults here mirror variant-0; each
     `.variant-N` rule below overrides the ones it cares about. */
  --shelf-fallback-grid-angle: 20deg;
  --shelf-fallback-scanline-step: 3px;
  --shelf-fallback-scanline-alpha: 0.045;
  --shelf-fallback-accent-alpha: 0.10;
  --shelf-fallback-stripe-direction: 90deg;
  --shelf-fallback-sweep-duration: 18s;
  --shelf-fallback-drift-duration: 28s;
  --shelf-fallback-drift-distance: 120px;

  background-color: var(--panel-2);
  background-image:
    /* horizontal scanlines */
    repeating-linear-gradient(
      0deg,
      transparent 0,
      transparent calc(var(--shelf-fallback-scanline-step) - 1px),
      rgba(255, 255, 255, var(--shelf-fallback-scanline-alpha)) calc(var(--shelf-fallback-scanline-step) - 1px),
      rgba(255, 255, 255, var(--shelf-fallback-scanline-alpha)) var(--shelf-fallback-scanline-step)
    ),
    /* phosphor dot-grid, tinted with the variant's accent intensity */
    radial-gradient(circle at 1px 1px,
      rgba(0, 212, 192, var(--shelf-fallback-accent-alpha)) 1px,
      transparent 1.6px);
  background-size: 100% 100%, 12px 12px;
  background-position: 0 0, 0 0;
  /* The whole tile drifts: scanlines stay put (100%/100% means there's
     no room to translate), the dot-grid slowly cycles its Y offset. */
  animation: shelf-fallback-drift var(--shelf-fallback-drift-duration) linear infinite;
}

.shelf-card-media-fallback::before {
  /* Top-stripe accent — the editorial anchor line we've always had,
     now per-variant (direction + featured-variant intensity bump). */
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: linear-gradient(var(--shelf-fallback-stripe-direction), var(--accent) 0%, transparent 70%);
  opacity: 0.35;
  z-index: 1;
}
.shelf-card-featured .shelf-card-media-fallback::before { opacity: 0.6; }

.shelf-card-media-fallback::after {
  /* Slow rotating sweep. Inset is negative so the rotated rectangle
     covers the tile through the whole 360° cycle. Z-index 0 puts it
     below the stripe and the source label. */
  content: "";
  position: absolute;
  inset: -25%;
  background: linear-gradient(
    var(--shelf-fallback-grid-angle),
    transparent 35%,
    rgba(0, 212, 192, calc(var(--shelf-fallback-accent-alpha) * 1.4)) 50%,
    transparent 65%
  );
  pointer-events: none;
  animation: shelf-fallback-sweep var(--shelf-fallback-sweep-duration) linear infinite;
  z-index: 0;
}

@keyframes shelf-fallback-drift {
  /* Move only the dot-grid layer (2nd background-image); the scanlines
     stay anchored, which keeps the horizontal lines visually stable. */
  0%   { background-position: 0 0, 0 0; }
  100% { background-position: 0 0, 0 var(--shelf-fallback-drift-distance); }
}

@keyframes shelf-fallback-sweep {
  /* Translate so the rotation center stays roughly over the tile,
     then rotate a full cycle. Slow durations + low gradient contrast
     keep this from feeling like a spinner. */
  0%   { transform: translate(-12%, -12%) rotate(0deg); }
  100% { transform: translate(-12%, -12%) rotate(360deg); }
}

/* Six deterministic variants. Sweep angle, scanline density, accent
   intensity, stripe direction, and the two cycle durations are tuned
   so two adjacent cards look noticeably different at a glance. All
   six share the same base recipe — no extra HTML elements per variant. */
.shelf-card-media-fallback.variant-0 {
  --shelf-fallback-grid-angle: 20deg;
  --shelf-fallback-scanline-step: 3px;
  --shelf-fallback-scanline-alpha: 0.045;
  --shelf-fallback-accent-alpha: 0.10;
  --shelf-fallback-stripe-direction: 90deg;
  --shelf-fallback-sweep-duration: 16s;
  --shelf-fallback-drift-duration: 26s;
}
.shelf-card-media-fallback.variant-1 {
  --shelf-fallback-grid-angle: 70deg;
  --shelf-fallback-scanline-step: 4px;
  --shelf-fallback-scanline-alpha: 0.035;
  --shelf-fallback-accent-alpha: 0.08;
  --shelf-fallback-stripe-direction: 270deg;
  --shelf-fallback-sweep-duration: 22s;
  --shelf-fallback-drift-duration: 32s;
}
.shelf-card-media-fallback.variant-2 {
  --shelf-fallback-grid-angle: 110deg;
  --shelf-fallback-scanline-step: 5px;
  --shelf-fallback-scanline-alpha: 0.05;
  --shelf-fallback-accent-alpha: 0.12;
  --shelf-fallback-stripe-direction: 90deg;
  --shelf-fallback-sweep-duration: 19s;
  --shelf-fallback-drift-duration: 36s;
}
.shelf-card-media-fallback.variant-3 {
  --shelf-fallback-grid-angle: 160deg;
  --shelf-fallback-scanline-step: 3px;
  --shelf-fallback-scanline-alpha: 0.03;
  --shelf-fallback-accent-alpha: 0.07;
  --shelf-fallback-stripe-direction: 270deg;
  --shelf-fallback-sweep-duration: 28s;
  --shelf-fallback-drift-duration: 24s;
}
.shelf-card-media-fallback.variant-4 {
  --shelf-fallback-grid-angle: 220deg;
  --shelf-fallback-scanline-step: 4px;
  --shelf-fallback-scanline-alpha: 0.04;
  --shelf-fallback-accent-alpha: 0.11;
  --shelf-fallback-stripe-direction: 90deg;
  --shelf-fallback-sweep-duration: 17s;
  --shelf-fallback-drift-duration: 30s;
}
.shelf-card-media-fallback.variant-5 {
  --shelf-fallback-grid-angle: 290deg;
  --shelf-fallback-scanline-step: 6px;
  --shelf-fallback-scanline-alpha: 0.045;
  --shelf-fallback-accent-alpha: 0.09;
  --shelf-fallback-stripe-direction: 270deg;
  --shelf-fallback-sweep-duration: 24s;
  --shelf-fallback-drift-duration: 34s;
}

/* Respect reduced-motion preference. Keep every visual layer (dot
   grid, scanlines, stripe, sweep colors) so the design system stays
   coherent; just stop the loops. The static result is still
   variant-distinct because the per-variant CSS custom properties
   change the painted gradient angles + scanline densities. */
@media (prefers-reduced-motion: reduce) {
  .shelf-card-media-fallback,
  .shelf-card-media-fallback::after {
    animation: none;
  }
}

.shelf-card-media-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: rgba(7, 16, 26, 0.55);
  border: 1px solid var(--border);
  padding: 0.12rem 0.45rem;
  border-radius: 999px;
  max-width: calc(100% - 1rem);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-decoration: none;
  /* Sits above the preview image / fallback tile. Subtle drop shadow
     keeps the pill readable over a bright photo without needing a
     heavier scrim. */
  z-index: 1;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* When the archived issue has a public Substack URL, the badge is an
   outbound link. Tint the text and lift the border-color so the
   affordance reads at a glance without competing with the title. */
a.shelf-card-media-badge.shelf-card-media-badge-link {
  color: var(--accent);
  border-color: rgba(0, 212, 192, 0.45);
  background: rgba(0, 212, 192, 0.08);
}
a.shelf-card-media-badge.shelf-card-media-badge-link:hover,
a.shelf-card-media-badge.shelf-card-media-badge-link:focus-visible {
  color: var(--accent-hover);
  border-color: var(--accent-hover);
  background: rgba(0, 212, 192, 0.15);
}

/* Card body. Splits into two stacks:
     .shelf-card-main   — title → source → quote (top of card body)
     .shelf-card-footer — tag pills (bottom of card body)
   The body itself is a flex column with the main block sized to its
   content and the footer pushed to the bottom via `margin-top: auto`
   on the footer. Within a grid row, sibling cards stretch to the
   tallest card, so footers align consistently across the row. */
.shelf-card-body {
  padding: 0.85rem 1rem 0.95rem;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.shelf-card-main {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.shelf-card-footer {
  margin-top: auto;
  padding-top: 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

/* Quote block. Only rendered when the link actually has quote text
   (the no-quote case omits the paragraph entirely), so we no longer
   reserve a minimum height — cards without a quote just shrink that
   row, and the footer's `margin-top: auto` keeps tag pills pinned to
   the bottom of the card. Long quotes are clamped (ellipsis on the
   final line) so a single oversized pull-quote never blows up the
   row height. */
.shelf-link-quote {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.shelf-card-featured .shelf-link-quote {
  -webkit-line-clamp: 5;
}
.shelf-card-all .shelf-link-quote {
  font-size: 0.9rem;
  -webkit-line-clamp: 4;
}
.shelf-link-quote .card-quote {
  color: var(--text-muted);
  font-style: italic;
}

/* Title block. Clamp to 2 lines AND reserve 2 lines of min-height so the
   source/meta line below sits at a consistent Y position across cards
   in the same grid row. */
.shelf-link-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  min-height: 2.7em; /* 2 lines × 1.35 */
}
.shelf-card-all .shelf-link-title { font-size: 0.98rem; }
.shelf-link-title-anchor {
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.shelf-link-title-anchor:hover { color: var(--accent); border-bottom-color: var(--accent); }

.shelf-link-meta {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.shelf-link-source { color: var(--text); }

/* (Footer issue chip was removed: the issue label now appears once,
   in the `.shelf-card-media-badge` corner pill. Linked-badge styles
   live with the badge rules above.) */

.shelf-link-pills {
  margin: 0;
  opacity: 0.9;
}
.shelf-link-pills .tag-pill {
  font-size: 0.65rem;
  padding: 0.12rem 0.45rem;
}
.shelf-link-pills .tag-pill-inactive {
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-dim);
  border-color: var(--border);
  font-style: italic;
}

/* Narrow viewports: collapse both grids to a single column with a
   tighter media tile so cards don't waste vertical space. Drop the
   reserved title height too — cards no longer sit side-by-side, so
   the alignment guarantee isn't worth the empty space short titles
   would otherwise leave. Line-clamp stays in place so very long
   titles or quotes still don't run away. */
@media (max-width: 640px) {
  .shelf-card-grid-featured,
  .shelf-card-grid-all {
    grid-template-columns: 1fr;
  }
  .shelf-card-featured .shelf-card-media { aspect-ratio: 16 / 8; }
  .shelf-card-all .shelf-card-media { aspect-ratio: 16 / 5; }
  .shelf-link-title { min-height: 0; }
}

/* ── Demo mode (DEMO_MODE=true) ──────────────────────────────────────────────
   Server-side, demoReadOnlyGuard in src/app.js rejects every non-GET. These
   rules hide the controls that would have invoked those endpoints so a
   demo visitor doesn't see broken buttons. The body.is-demo class is added
   by src/views/layout.js when isDemoMode() is true. */

.demo-banner {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-align: center;
  padding: 0.55rem 1rem;
  background: linear-gradient(180deg, #1a2330 0%, #131a23 100%);
  color: var(--text);
  border-bottom: 1px solid var(--border-strong);
  box-shadow: inset 0 2px 0 var(--accent-2);
  position: sticky;
  top: 0;
  z-index: 11;
}

/* Hide direct mutation entry points: inbox link/image add buttons, archive
   bar, inbox row Edit/Remove buttons, archive sidebar import link, the per-
   issue edit-action links, and any "Import archived issue" anchors. */
body.is-demo .header-actions a[href="/add"],
body.is-demo .header-actions a[href="/add-image"],
body.is-demo #archive-bar,
body.is-demo #archive-toggle-btn,
body.is-demo .link-actions .edit-btn,
body.is-demo .link-actions .delete-btn,
body.is-demo .tag-edit-btn,
body.is-demo .tags-action-btn,
body.is-demo .tags-add-card,
body.is-demo .catalogue-tags,
body.is-demo .catalogue-groups,
body.is-demo .catalogue-row-foot,
body.is-demo .catalogue-row-toggle,
body.is-demo .issues-row-public-form,
body.is-demo .image-delete-btn,
body.is-demo .image-edit-btn,
body.is-demo .archive-sidebar-action,
body.is-demo .issue-actions,
body.is-demo .preview-image-admin-card,
body.is-demo a[href="/archive/import"] {
  display: none !important;
}

/* Disable any submit-style button on form pages reachable by direct URL
   (/add, /add-image, /review/:token, /archive/import, /archive/import/preview,
   /issues/:id/edit, /issues/:id/links/edit, /issues/:id/clicks paste form).
   The server still 403s the POST; this gives a visual cue that the form
   is inert. */
body.is-demo .catalogue-row-grid input,
body.is-demo .catalogue-row-grid select,
body.is-demo .catalogue-row-grid textarea {
  pointer-events: none;
  opacity: 0.6;
}
body.is-demo form button[type="submit"],
body.is-demo form .btn-primary,
body.is-demo form button.btn-primary,
body.is-demo form button.btn-secondary {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
}

/* ── Archive data-quality report (/archive/quality) ────────────────────── */

.archive-quality-link-card { margin-top: 1rem; }
.archive-quality-link-summary {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin: 0 0 0.4rem;
  line-height: 1.4;
}

.archive-quality-back {
  display: inline-block;
  margin-left: 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent);
  text-decoration: none;
}
.archive-quality-back:hover { text-decoration: underline; }

.archive-quality-card { padding: 1rem 1.1rem 1.1rem; }

.archive-quality-table-wrap {
  width: 100%;
  overflow-x: auto;
  margin-top: 0.5rem;
}

.archive-quality-table {
  width: 100%;
  min-width: 920px;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.archive-quality-table thead th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: 0.4rem 0.55rem 0.5rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.archive-quality-table tbody td,
.archive-quality-table tbody th {
  padding: 0.55rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
  vertical-align: top;
}

.archive-quality-table tbody tr:last-child td,
.archive-quality-table tbody tr:last-child th {
  border-bottom: none;
}

.archive-quality-table tbody tr:hover td,
.archive-quality-table tbody tr:hover th {
  background: rgba(0, 212, 192, 0.03);
}

.archive-quality-issue {
  text-align: left;
  font-weight: 400;
  min-width: 12rem;
  max-width: 18rem;
}
.archive-quality-issue-number {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent);
  margin-right: 0.4rem;
  font-variant-numeric: tabular-nums;
}
.archive-quality-issue-headline {
  color: var(--text);
  word-break: break-word;
}
.archive-quality-issue .muted { color: var(--text-dim); font-style: italic; }

.archive-quality-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.archive-quality-num-quiet { color: var(--text-muted); }
.archive-quality-num-warn { color: var(--accent-2); }
.archive-quality-pct {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.archive-quality-public {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-decoration: none;
}
.archive-quality-public:not(.archive-quality-public-missing) {
  color: var(--accent);
}
.archive-quality-public:not(.archive-quality-public-missing):hover {
  text-decoration: underline;
}
.archive-quality-public-missing {
  color: var(--accent-2);
  font-style: italic;
}

.archive-quality-status {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}
.archive-quality-status-ok { color: var(--accent-acid); }
.archive-quality-status-missing { color: var(--accent-2); }

.archive-quality-warnings {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.archive-quality-warnings li {
  font-size: 0.78rem;
  color: var(--accent-2);
  padding-left: 0.55rem;
  border-left: 2px solid var(--accent-2);
  line-height: 1.35;
}

@media (max-width: 720px) {
  .archive-quality-table { font-size: 0.8rem; min-width: 760px; }
}

/* Issue cell on the quality table is a link to the per-issue cleanup page. */
.archive-quality-issue-link {
  display: inline-flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  gap: 0.05rem;
}
.archive-quality-issue-link:hover .archive-quality-issue-headline {
  color: var(--accent);
  text-decoration: underline;
}

.archive-quality-warning-link {
  color: var(--accent-2);
  text-decoration: none;
  border-bottom: 1px dotted var(--accent-2);
}
.archive-quality-warning-link:hover {
  color: var(--accent-hover);
  border-bottom-color: var(--accent-hover);
}

/* ── Per-issue metadata cleanup (/archive/issues/:num/links) ──────────── */

.archive-cleanup-back {
  display: inline-block;
  margin-left: 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent);
  text-decoration: none;
}
.archive-cleanup-back:hover { text-decoration: underline; }

.archive-cleanup-intro {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin: 0 0 1.25rem;
  max-width: 60ch;
  line-height: 1.5;
}

.archive-cleanup-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.archive-cleanup-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.1rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  position: relative;
  box-shadow: var(--shadow);
}
.archive-cleanup-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, var(--accent) 0%, transparent 60%);
  opacity: 0.45;
  border-radius: var(--radius) var(--radius) 0 0;
}
.archive-cleanup-card-focused {
  box-shadow: 0 0 0 1px var(--accent), var(--shadow);
}

.archive-cleanup-card-head {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}
.archive-cleanup-card-id {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}
.archive-cleanup-card-url {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--accent);
  text-decoration: none;
  word-break: break-all;
  line-height: 1.4;
}
.archive-cleanup-card-url:hover { text-decoration: underline; }

.archive-cleanup-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0.85rem 1rem;
}
.archive-cleanup-quote-field { grid-column: 1 / -1; }

@media (max-width: 720px) {
  .archive-cleanup-grid { grid-template-columns: minmax(0, 1fr); }
}

.archive-cleanup-card-actions {
  display: flex;
  justify-content: flex-end;
  padding-top: 0.25rem;
}

/* Tag editor inside each cleanup card. Spans both grid columns so the
   pill wrap has the full width to flow into; reuses the existing
   `.tag-option`/`.tag-pill` chrome from the inbox + review surfaces so
   the checkbox styling and focus states stay consistent. */
.archive-cleanup-tags-field { grid-column: 1 / -1; }

.archive-cleanup-tag-options {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.5rem;
  margin: 0;
  padding: 0;
}

/* Inactive companion pills sit above the active-tag checkboxes, muted
   so it's clear they're not selectable — they live on the link but the
   editor can't touch them from this surface (matching the `/archive/catalogue`
   convention). */
.archive-cleanup-inactive-pills {
  margin: 0 0 0.5rem;
}
.archive-cleanup-inactive-pills .tag-pill-inactive {
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-dim);
  border-color: var(--border);
  font-style: italic;
}

/* Soft-remove section sits BELOW the metadata save form on every visible
   cleanup card. The action is a `<details>` collapsed by default so it
   stays visually secondary; the inner form posts to /exclude with a
   required confirm checkbox. */
.archive-cleanup-remove {
  margin-top: 0.6rem;
  border-top: 1px dashed var(--border);
  padding-top: 0.6rem;
}
.archive-cleanup-remove > summary {
  cursor: pointer;
  list-style: none;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0.2rem 0;
}
.archive-cleanup-remove > summary::-webkit-details-marker { display: none; }
.archive-cleanup-remove > summary::before {
  content: "▸ ";
  color: var(--accent-2);
}
.archive-cleanup-remove[open] > summary::before { content: "▾ "; }
.archive-cleanup-remove > summary:hover { color: var(--text); }

.archive-cleanup-remove-form {
  margin-top: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.archive-cleanup-remove-hint {
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-muted);
  line-height: 1.4;
  max-width: 60ch;
}
.archive-cleanup-remove-confirm {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent-2);
  cursor: pointer;
}
.archive-cleanup-remove-confirm input[type="checkbox"] { width: auto; margin: 0; }
.archive-cleanup-remove-actions {
  display: flex;
  justify-content: flex-end;
}

/* "Remove from archive" button — visually distinct from the cyan Save.
   Uses the existing --danger token for the accent so the styling fits
   the Signal Room palette without inventing a new color. Muted by
   default; warms up on hover so the destructive intent reads only at
   the point of action. */
.btn-danger {
  background: rgba(255, 107, 107, 0.08);
  color: var(--danger);
  border-color: rgba(255, 107, 107, 0.35);
}
.btn-danger:hover:not(:disabled) {
  background: rgba(255, 107, 107, 0.18);
  border-color: rgba(255, 107, 107, 0.55);
  color: #ff8f8f;
}

/* Removed-links section — lower-priority list of soft-removed archived
   links with a Restore action. Compact, muted, and slightly indented so
   the visual weight stays below the main editable list above. */
.archive-cleanup-removed {
  margin-top: 1.75rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}
.archive-cleanup-removed-heading {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 0.4rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.archive-cleanup-removed-heading::before {
  content: "▸ ";
  color: var(--accent-2);
}
.archive-cleanup-removed-count {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--accent-2);
  font-variant-numeric: tabular-nums;
}
.archive-cleanup-removed-hint {
  margin: 0 0 0.75rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  max-width: 60ch;
  line-height: 1.4;
}
.archive-cleanup-removed-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.archive-cleanup-removed-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.6rem 0.8rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  opacity: 0.85;
}
.archive-cleanup-removed-main { flex: 1 1 0; min-width: 0; }
.archive-cleanup-removed-title {
  color: var(--text);
  text-decoration: none;
  font-weight: 500;
}
.archive-cleanup-removed-title:hover { color: var(--accent); }
.archive-cleanup-removed-title .muted { font-style: italic; color: var(--text-dim); font-weight: 400; }
.archive-cleanup-removed-source {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent-violet);
  margin-left: 0.4rem;
}
.archive-cleanup-removed-meta {
  margin: 0.2rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-dim);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}
.archive-cleanup-removed-reason {
  color: var(--accent-2);
  font-style: italic;
}
.archive-cleanup-removed-form { flex: 0 0 auto; }

@media (max-width: 480px) {
  .archive-cleanup-removed-row { flex-direction: column; align-items: stretch; }
  .archive-cleanup-removed-form button { width: 100%; }
}

/* ── Duplicate-link report (/archive/duplicates) ─────────────────────── */

.archive-quality-side-links {
  margin: 0.25rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
}
.archive-quality-side-links a {
  color: var(--accent-2);
  text-decoration: none;
}
.archive-quality-side-links a:hover { text-decoration: underline; }

.archive-sidebar-action-secondary { margin-top: 0.25rem; }
.archive-sidebar-action-secondary a { color: var(--accent-2); }

.duplicate-summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.75rem;
  margin: 1rem 0 1.5rem;
}
.duplicate-summary-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 0.9rem;
}
.duplicate-summary-num {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.duplicate-summary-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-top: 0.3rem;
}

.duplicate-section {
  margin-top: 1.5rem;
}
.duplicate-section-heading {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
  margin: 0 0 0.25rem;
}
.duplicate-section-heading::before { content: "» "; color: var(--accent); }
.duplicate-section-blurb {
  margin: 0 0 0.75rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  max-width: 70ch;
  line-height: 1.45;
}

.duplicate-group-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.duplicate-group {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem 0.85rem 0.85rem;
}
.duplicate-group-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.duplicate-group-key {
  flex: 1 1 0;
  min-width: 0;
}
.duplicate-group-key-label {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-right: 0.5rem;
}
.duplicate-group-key-value {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--accent);
  word-break: break-all;
}
.duplicate-group-count {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--accent-2);
  background: rgba(255, 196, 90, 0.08);
  border: 1px solid rgba(255, 196, 90, 0.35);
  border-radius: var(--radius);
  padding: 0.15rem 0.5rem;
  white-space: nowrap;
}

.duplicate-group-rows {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.duplicate-row {
  background: var(--panel-2, var(--panel));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.55rem 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.duplicate-row-main {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
}
.duplicate-row-title {
  color: var(--text);
  text-decoration: none;
  font-weight: 500;
}
.duplicate-row-title:hover { color: var(--accent); }
.duplicate-row-title .muted { color: var(--text-dim); font-style: italic; font-weight: 400; }
.duplicate-row-source {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent-violet);
}
.duplicate-row-meta {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.85rem;
}
.duplicate-row-location {
  color: var(--accent);
  text-decoration: none;
}
.duplicate-row-location:hover { text-decoration: underline; }
.duplicate-row-category { color: var(--text-muted); }
.duplicate-row-tags { margin: 0.1rem 0 0; }
.duplicate-row-url {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-dim);
  word-break: break-all;
}
.duplicate-row-url a {
  color: var(--text-dim);
  text-decoration: none;
}
.duplicate-row-url a:hover { color: var(--accent); text-decoration: underline; }

.duplicate-group-excluded-note {
  margin: 0.6rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  line-height: 1.45;
}
.duplicate-excluded-chip {
  display: inline-block;
  font-style: italic;
  color: var(--text-dim);
  margin-right: 0.35rem;
}

.duplicate-intro-note {
  margin: 0.5rem 0 1.25rem;
  padding: 0.65rem 0.85rem;
  background: var(--panel);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text);
  max-width: 80ch;
}
.duplicate-intro-note a {
  color: var(--accent);
  text-decoration: none;
}
.duplicate-intro-note a:hover { text-decoration: underline; }

/* ── Click attribution audit (/archive/click-attribution) ────────────── */

.click-attribution-intro {
  margin: 0.5rem 0 1.25rem;
  padding: 0.65rem 0.85rem;
  background: var(--panel);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  max-width: 80ch;
}
.click-attribution-intro p { margin: 0 0 0.5rem; line-height: 1.5; }
.click-attribution-intro p:last-child { margin-bottom: 0; }
.click-attribution-intro-detail { color: var(--text-muted); font-size: 0.85rem; }
.click-attribution-legend {
  list-style: none;
  padding: 0;
  margin: 0.6rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.click-attribution-legend code {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  background: var(--panel-2, var(--panel));
  padding: 0 0.25rem;
  border-radius: 3px;
  color: var(--text-dim);
}

.click-attribution-pill {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--panel-2, var(--panel));
  color: var(--text-muted);
  white-space: nowrap;
}
.click-attribution-pill-issue {
  color: var(--accent);
  border-color: rgba(78, 196, 255, 0.35);
  background: rgba(78, 196, 255, 0.08);
}
.click-attribution-pill-lifetime {
  color: var(--accent-acid);
  border-color: rgba(160, 240, 80, 0.35);
  background: rgba(160, 240, 80, 0.08);
}
.click-attribution-pill-placement {
  color: var(--accent-violet);
  border-color: rgba(180, 140, 255, 0.35);
  background: rgba(180, 140, 255, 0.08);
}

.click-attribution-list { gap: 1rem; }
.click-attribution-group { padding-bottom: 1rem; }
.click-attribution-totals {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  align-items: flex-end;
}

.click-attribution-flags {
  list-style: none;
  padding: 0;
  margin: 0 0 0.6rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.click-attribution-flag {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--panel-2, var(--panel));
  white-space: nowrap;
}
.click-attribution-flag-info {
  color: var(--accent);
  border-color: rgba(78, 196, 255, 0.35);
  background: rgba(78, 196, 255, 0.08);
}
.click-attribution-flag-warn {
  color: var(--accent-2);
  border-color: rgba(255, 196, 90, 0.35);
  background: rgba(255, 196, 90, 0.08);
}

.click-attribution-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  margin-top: 0.4rem;
}
.click-attribution-table thead th {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: 0.35rem 0.45rem 0.45rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  white-space: nowrap;
}
.click-attribution-table tbody td {
  padding: 0.45rem 0.5rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.click-attribution-th-right,
.click-attribution-td-right {
  text-align: right;
  white-space: nowrap;
}

.click-attribution-issue-link {
  font-family: var(--font-mono);
  color: var(--accent);
  text-decoration: none;
}
.click-attribution-issue-link:hover { text-decoration: underline; }
.click-attribution-issue-headline {
  color: var(--text-muted);
  font-family: inherit;
}

.click-attribution-saved-link {
  margin-bottom: 0.25rem;
}
.click-attribution-saved-link:last-child { margin-bottom: 0; }
.click-attribution-link-anchor {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent);
  text-decoration: none;
  margin-right: 0.25rem;
}
.click-attribution-link-anchor:hover { text-decoration: underline; }
.click-attribution-source {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent-violet);
}
.click-attribution-category {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
}
.click-attribution-excluded {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--accent-2);
  font-style: italic;
}

.click-attribution-match {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
}
.click-attribution-match-saved     { color: var(--accent-acid); }
.click-attribution-match-url       { color: var(--accent); }
.click-attribution-match-excluded  { color: var(--accent-2); font-style: italic; }
.click-attribution-match-unmatched { color: var(--text-muted); }
.click-attribution-match-no-clicks { color: var(--text-dim); }

.click-attribution-excluded-note { opacity: 0.85; }
.click-attribution-excluded-reason {
  color: var(--text-muted);
  font-style: italic;
}

@media (max-width: 720px) {
  .click-attribution-table { font-size: 0.78rem; }
  .click-attribution-totals { align-items: flex-start; }
}

/* ── Archive tools card (/archive sidebar, top) ─────────────────────────── */

.archive-tools-card { padding: 0.9rem 1rem 1rem; }
.archive-tools-help {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin: 0 0 0.7rem;
  line-height: 1.4;
}
.archive-tools-list {
  list-style: none;
  padding: 0;
  margin: 0 0 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
.archive-tools-item {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.archive-tools-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}
.archive-tools-link {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: var(--accent);
  text-decoration: none;
}
.archive-tools-link:hover { text-decoration: underline; }
.archive-tools-help-line {
  font-size: 0.72rem;
  color: var(--text-muted);
  line-height: 1.35;
}
.archive-tools-subheading {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  margin: 0 0 0.4rem;
}
.archive-tools-subheading::before { content: "» "; color: var(--accent); }
.archive-tools-preview-block {
  margin-top: 0.5rem;
  padding-top: 0.7rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* ── Recent issues card (/archive sidebar) ──────────────────────────────── */

.archive-recent-issues-card { margin-top: 1rem; }
.archive-recent-issues-meta {
  font-size: 0.72rem;
  color: var(--text-muted);
  margin: 0 0 0.45rem;
  line-height: 1.35;
}
.archive-recent-issues-viewall {
  margin-top: 0.7rem;
  padding-top: 0.55rem;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* ── /archive/issues (full archived-issue index) ────────────────────────── */

.archive-issues-search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1rem;
}
.archive-issues-search-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}
.archive-issues-search input[type="search"] {
  flex: 1 1 220px;
  min-width: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.4rem 0.6rem;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.85rem;
}
.archive-issues-search input[type="search"]:focus {
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}
.archive-issues-search-reset {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  text-decoration: none;
}
.archive-issues-search-reset:hover { color: var(--accent); text-decoration: underline; }

.archive-issues-card { padding: 1rem 1.1rem 1.1rem; }

.archive-issues-table-wrap {
  width: 100%;
  overflow-x: auto;
}
.archive-issues-table {
  width: 100%;
  min-width: 820px;
  border-collapse: collapse;
  font-size: 0.85rem;
}
.archive-issues-table thead th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: 0.4rem 0.55rem 0.5rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.archive-issues-table tbody td,
.archive-issues-table tbody th {
  padding: 0.55rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
  vertical-align: top;
}
.archive-issues-table tbody tr:last-child td,
.archive-issues-table tbody tr:last-child th {
  border-bottom: none;
}
.archive-issues-table tbody tr:hover td,
.archive-issues-table tbody tr:hover th {
  background: rgba(0, 212, 192, 0.03);
}

.archive-issues-issue {
  text-align: left;
  font-weight: 400;
  min-width: 14rem;
  max-width: 22rem;
}
.archive-issues-issue-link {
  display: inline-flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  gap: 0.05rem;
}
.archive-issues-issue-link:hover .archive-issues-issue-headline {
  color: var(--accent);
  text-decoration: underline;
}
.archive-issues-issue-number {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.archive-issues-issue-headline {
  color: var(--text);
  word-break: break-word;
}
.archive-issues-issue .muted { color: var(--text-dim); font-style: italic; }

.archive-issues-date {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.archive-issues-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.archive-issues-num-quiet { color: var(--text-muted); }

.archive-issues-public {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-decoration: none;
}
.archive-issues-public:not(.archive-issues-public-missing) {
  color: var(--accent);
}
.archive-issues-public:not(.archive-issues-public-missing):hover {
  text-decoration: underline;
}
.archive-issues-public-missing { color: var(--text-dim); }

.archive-issues-status {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}
.archive-issues-status-ok      { color: var(--accent-acid); }
.archive-issues-status-missing { color: var(--text-dim); font-style: italic; }

.archive-issues-actions {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
}
.archive-issues-actions a {
  color: var(--accent);
  text-decoration: none;
}
.archive-issues-actions a:hover { text-decoration: underline; }

@media (max-width: 720px) {
  .archive-issues-table { font-size: 0.8rem; min-width: 720px; }
}

/* ── /archive: Issue performance card ──────────────────────────────────── */
.issue-performance-card .issue-performance-groups {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}
@media (max-width: 900px) {
  .issue-performance-card .issue-performance-groups {
    grid-template-columns: minmax(0, 1fr);
  }
}
.issue-performance-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}
.issue-performance-heading {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  border-bottom: 1px dashed var(--border);
  padding-bottom: 0.35rem;
}
.issue-performance-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
.issue-performance-row {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  font-size: 0.85rem;
  min-width: 0;
}
.issue-performance-title {
  color: var(--text);
  text-decoration: none;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
a.issue-performance-title:hover { color: var(--accent); }
.issue-performance-metrics {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.issue-performance-reason {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  color: var(--accent-2, var(--accent));
  text-transform: uppercase;
}
.issue-performance-empty {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin: 0;
}

/* ── /archive: Clickiness trend card ──────────────────────────────────── */
.clickiness-trend-card .clickiness-trend-svg {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
}
.clickiness-trend-stats {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
  margin: 0 0 0.5rem;
}
.clickiness-trend-note {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  margin: 0.5rem 0 0;
}
.clickiness-trend-grid { stroke: rgba(255, 255, 255, 0.06); stroke-width: 1; }
.clickiness-trend-axis-line { stroke: rgba(255, 255, 255, 0.18); stroke-width: 1; }
.clickiness-trend-tick {
  font-family: var(--font-mono);
  font-size: 9px;
  fill: var(--text-muted);
  letter-spacing: 0.04em;
}
.clickiness-trend-axis-label {
  font-family: var(--font-mono);
  font-size: 9px;
  fill: var(--text-muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.clickiness-trend-line {
  stroke: var(--accent-2);
  stroke-width: 1.4;
  stroke-dasharray: 5 4;
  fill: none;
  opacity: 0.85;
}
.clickiness-actual-line {
  stroke: var(--accent);
  stroke-width: 1.75;
  fill: none;
}
.clickiness-trend-dot {
  fill: var(--accent);
  stroke: var(--panel);
  stroke-width: 1;
  transition: r 0.15s;
}
.clickiness-trend-dot-g:hover .clickiness-trend-dot { r: 5; }

/* ── Shelves: directory layout (Creative Tools etc.) ──────────────────── */
/*
   The directory layout replaces the standard shelf-card grid for shelves
   with a manual group catalog in services/shelfGroups.js. The page keeps
   the standard header chrome (page-eyebrow + h1 + subhead + stats) and
   adds three pieces: filter chips, a sortable table, and per-row admin
   selects. The whole section sits inside `.shelf-directory[data-shelf-slug]`
   so the inline client script can scope its filter/sort/save wiring.
*/

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

.shelf-directory {
  margin-top: 1.5rem;
}

/* Filter chips ─────────────────────────────────────────────────────────── */
.shelf-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 1.1rem;
}
.shelf-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.32rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-muted);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.shelf-filter-chip:hover {
  color: var(--text);
  border-color: var(--border-strong);
}
.shelf-filter-chip.is-active {
  color: var(--text);
  border-color: var(--accent);
  background: rgba(0, 212, 192, 0.10);
  box-shadow: 0 0 0 1px rgba(0, 212, 192, 0.25) inset;
}
.shelf-filter-chip-count {
  font-size: 0.62rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.shelf-filter-chip.is-active .shelf-filter-chip-count { color: var(--text-muted); }

/* Table layout ─────────────────────────────────────────────────────────── */
.shelf-directory-table-wrap {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  overflow: hidden;
}
.shelf-directory-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.shelf-directory-th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 600;
  padding: 0.7rem 0.85rem;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
}
.shelf-directory-th.is-sortable .shelf-directory-sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}
.shelf-directory-th.is-sortable:hover { color: var(--text); }
.shelf-directory-sort-indicator {
  display: inline-block;
  width: 0.6rem;
  text-align: center;
  color: var(--text-dim);
}
.shelf-directory-th.is-sorted-asc .shelf-directory-sort-indicator::after { content: '▲'; color: var(--accent); }
.shelf-directory-th.is-sorted-desc .shelf-directory-sort-indicator::after { content: '▼'; color: var(--accent); }

.shelf-directory-row {
  border-top: 1px solid var(--border);
  transition: background 0.12s;
}
.shelf-directory-row:first-child { border-top: 0; }
.shelf-directory-row:hover { background: var(--panel-2); }
.shelf-directory-cell {
  padding: 0.7rem 0.85rem;
  vertical-align: top;
  color: var(--text);
}
.shelf-directory-cell-tool { width: 22%; }
.shelf-directory-cell-type { width: 18%; }
.shelf-directory-cell-use  { width: 30%; }
.shelf-directory-cell-source { width: 16%; color: var(--text-muted); font-family: var(--font-mono); font-size: 0.78rem; letter-spacing: 0.02em; }
.shelf-directory-cell-issue  { width: 14%; }

.shelf-directory-tool-link {
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px dotted var(--text-muted);
}
.shelf-directory-tool-link:hover { color: var(--accent); border-bottom-color: var(--accent); }

.shelf-directory-use {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  color: var(--text-muted);
  font-size: 0.85rem;
  line-height: 1.45;
}
.shelf-directory-use-empty,
.shelf-directory-source-empty { color: var(--text-dim); }

.shelf-directory-issue-link,
.shelf-directory-issue {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  text-decoration: none;
}
.shelf-directory-issue-link:hover { color: var(--accent); }

.shelf-directory-empty td {
  padding: 1.5rem 0.85rem;
  text-align: center;
  color: var(--text-muted);
  font-style: italic;
}

/* Group pills ─────────────────────────────────────────────────────────── */
.shelf-group-pill {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.18rem 0.55rem;
  border-radius: 3px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  vertical-align: middle;
}

/* Per-group muted-color variants. Each pair sets a tinted background
   + matching border + readable foreground; the colors are pulled from
   the existing Signal Room theme variables so a future theme tweak
   carries through. Keep these in sync with the catalog in
   src/services/shelfGroups.js. */
.shelf-group-pill-design         { background: rgba(167, 139, 250, 0.14); border-color: rgba(167, 139, 250, 0.4);  color: #c8b6ff; }
.shelf-group-pill-typography     { background: rgba(0, 212, 192, 0.12);   border-color: rgba(0, 212, 192, 0.4);    color: #6fe7d8; }
.shelf-group-pill-animation      { background: rgba(120, 119, 255, 0.14); border-color: rgba(120, 119, 255, 0.45); color: #b1b0ff; }
.shelf-group-pill-generative-art { background: rgba(80, 216, 144, 0.12);  border-color: rgba(80, 216, 144, 0.4);   color: #8ee9b4; }
.shelf-group-pill-color          { background: rgba(255, 107, 107, 0.12); border-color: rgba(255, 107, 107, 0.4);  color: #ffb1b1; }
.shelf-group-pill-writing        { background: rgba(255, 107, 174, 0.12); border-color: rgba(255, 107, 174, 0.4);  color: #ffb1d4; }
.shelf-group-pill-decks          { background: rgba(255, 155, 62, 0.12);  border-color: rgba(255, 155, 62, 0.4);   color: #ffc48a; }
.shelf-group-pill-video          { background: rgba(96, 165, 250, 0.14);  border-color: rgba(96, 165, 250, 0.45);  color: #a8c8ff; }
.shelf-group-pill-social         { background: rgba(56, 189, 248, 0.12);  border-color: rgba(56, 189, 248, 0.4);   color: #7dd3fc; }
.shelf-group-pill-stock          { background: rgba(250, 204, 21, 0.12);  border-color: rgba(250, 204, 21, 0.4);   color: #fde68a; }
.shelf-group-pill-ai             { background: rgba(99, 102, 241, 0.16);  border-color: rgba(99, 102, 241, 0.45);  color: #a5b4fc; }
.shelf-group-pill-utility        { background: rgba(125, 139, 155, 0.16); border-color: rgba(125, 139, 155, 0.4);  color: #c1cad6; }
.shelf-group-pill-toolbox        { background: rgba(202, 138, 4, 0.14);   border-color: rgba(202, 138, 4, 0.45);   color: #e8c069; }
.shelf-group-pill-inspo          { background: rgba(182, 245, 96, 0.12);  border-color: rgba(182, 245, 96, 0.4);   color: #d8f99c; }

/* Creative Advice reader-intent taxonomy. Distinct slugs from Creative
   Tools; same muted-tint idiom so the admin/settings shelf UI reads as one
   family. Keep these in sync with the 'creative-advice' catalog in
   src/services/shelfGroups.js. */
.shelf-group-pill-make-better-work      { background: rgba(167, 139, 250, 0.14); border-color: rgba(167, 139, 250, 0.4);  color: #c8b6ff; }
.shelf-group-pill-get-unstuck           { background: rgba(255, 155, 62, 0.12);  border-color: rgba(255, 155, 62, 0.4);   color: #ffc48a; }
.shelf-group-pill-build-a-creative-life { background: rgba(80, 216, 144, 0.12);  border-color: rgba(80, 216, 144, 0.4);   color: #8ee9b4; }
.shelf-group-pill-develop-taste         { background: rgba(255, 107, 174, 0.12); border-color: rgba(255, 107, 174, 0.4);  color: #ffb1d4; }
.shelf-group-pill-work-with-technology  { background: rgba(99, 102, 241, 0.16);  border-color: rgba(99, 102, 241, 0.45);  color: #a5b4fc; }
.shelf-group-pill-protect-your-attention { background: rgba(56, 189, 248, 0.12); border-color: rgba(56, 189, 248, 0.4);   color: #7dd3fc; }
.shelf-group-pill-talk-to-people        { background: rgba(250, 204, 21, 0.12);  border-color: rgba(250, 204, 21, 0.4);   color: #fde68a; }
.shelf-group-pill-stay-human            { background: rgba(0, 212, 192, 0.12);   border-color: rgba(0, 212, 192, 0.4);    color: #6fe7d8; }

/* Strategic Thinking editorial/process taxonomy. Distinct slugs from the other
   two shelves; same muted-tint idiom so the admin/settings shelf UI reads as
   one family. Keep these in sync with the 'strategic-thinking' catalog in
   src/services/shelfGroups.js. */
.shelf-group-pill-define-the-problem   { background: rgba(255, 107, 107, 0.12); border-color: rgba(255, 107, 107, 0.4);  color: #ffb1b1; }
.shelf-group-pill-find-the-insight     { background: rgba(250, 204, 21, 0.12);  border-color: rgba(250, 204, 21, 0.4);   color: #fde68a; }
.shelf-group-pill-write-the-brief      { background: rgba(255, 107, 174, 0.12); border-color: rgba(255, 107, 174, 0.4);  color: #ffb1d4; }
.shelf-group-pill-judge-the-work       { background: rgba(202, 138, 4, 0.14);   border-color: rgba(202, 138, 4, 0.45);   color: #e8c069; }
.shelf-group-pill-decisions            { background: rgba(80, 216, 144, 0.12);  border-color: rgba(80, 216, 144, 0.4);   color: #8ee9b4; }
.shelf-group-pill-ai-pressure          { background: rgba(99, 102, 241, 0.16);  border-color: rgba(99, 102, 241, 0.45);  color: #a5b4fc; }
.shelf-group-pill-strategist-identity  { background: rgba(56, 189, 248, 0.12);  border-color: rgba(56, 189, 248, 0.4);   color: #7dd3fc; }
.shelf-group-pill-workshops-process    { background: rgba(0, 212, 192, 0.12);   border-color: rgba(0, 212, 192, 0.4);    color: #6fe7d8; }
.shelf-group-pill-resources            { background: rgba(182, 245, 96, 0.12);  border-color: rgba(182, 245, 96, 0.4);   color: #d8f99c; }

/* Writing is Power writing-craft taxonomy. Distinct slugs from the other
   shelves; same muted-tint idiom so the admin/settings shelf UI reads as one
   family. Keep these in sync with the 'writing-is-power' catalog in
   src/services/shelfGroups.js. */
.shelf-group-pill-craft             { background: rgba(167, 139, 250, 0.14); border-color: rgba(167, 139, 250, 0.4);  color: #c8b6ff; }
.shelf-group-pill-ai-and-voice      { background: rgba(99, 102, 241, 0.16);  border-color: rgba(99, 102, 241, 0.45);  color: #a5b4fc; }
.shelf-group-pill-publishing        { background: rgba(255, 155, 62, 0.12);  border-color: rgba(255, 155, 62, 0.4);   color: #ffc48a; }
.shelf-group-pill-persuasion        { background: rgba(255, 107, 107, 0.12); border-color: rgba(255, 107, 107, 0.4);  color: #ffb1b1; }
.shelf-group-pill-creative-identity { background: rgba(255, 107, 174, 0.12); border-color: rgba(255, 107, 174, 0.4);  color: #ffb1d4; }
.shelf-group-pill-reading-culture   { background: rgba(0, 212, 192, 0.12);   border-color: rgba(0, 212, 192, 0.4);    color: #6fe7d8; }

/* AI Perspectives taxonomy. Distinct slugs from the other shelves; same
   muted-tint idiom so the admin/settings shelf UI reads as one family. Keep
   these in sync with the 'ai-perspectives' catalog in
   src/services/shelfGroups.js. */
.shelf-group-pill-be-more-human     { background: rgba(167, 139, 250, 0.14); border-color: rgba(167, 139, 250, 0.4);  color: #c8b6ff; }
.shelf-group-pill-future-of-work    { background: rgba(255, 155, 62, 0.12);  border-color: rgba(255, 155, 62, 0.4);   color: #ffc48a; }
.shelf-group-pill-ai-and-writing    { background: rgba(99, 102, 241, 0.16);  border-color: rgba(99, 102, 241, 0.45);  color: #a5b4fc; }
.shelf-group-pill-educate-thyself   { background: rgba(250, 204, 21, 0.12);  border-color: rgba(250, 204, 21, 0.4);   color: #fde68a; }
.shelf-group-pill-ai-and-research   { background: rgba(80, 216, 144, 0.12);  border-color: rgba(80, 216, 144, 0.4);   color: #8ee9b4; }
.shelf-group-pill-ai-and-creativity { background: rgba(255, 107, 174, 0.12); border-color: rgba(255, 107, 174, 0.4);  color: #ffb1d4; }
.shelf-group-pill-ai-and-strategy   { background: rgba(255, 107, 107, 0.12); border-color: rgba(255, 107, 107, 0.4);  color: #ffb1b1; }
.shelf-group-pill-ai-economy        { background: rgba(0, 212, 192, 0.12);   border-color: rgba(0, 212, 192, 0.4);    color: #6fe7d8; }
.shelf-group-pill-unsorted       { background: rgba(255, 255, 255, 0.03); border-color: var(--border);             color: var(--text-dim); font-style: italic; }

/* Chip variants borrow the same color rule via class composition — the
   chip carries .shelf-filter-chip + .shelf-group-pill-<slug>, and the
   .is-active state overrides the background so the highlighted chip is
   always readable. */
.shelf-filter-chip[class*="shelf-group-pill-"] { color: inherit; }

/* Admin select + save status ──────────────────────────────────────────── */
.shelf-group-select-label {
  display: inline-block;
  margin-left: 0.45rem;
}
.shelf-group-select {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  padding: 0.18rem 0.4rem;
  background: var(--panel-3);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 3px;
  cursor: pointer;
}
.shelf-group-select:hover { border-color: var(--border-strong); }
.shelf-group-select:focus { outline: 2px solid var(--border-focus); outline-offset: 1px; }
.shelf-group-save-status {
  display: inline-block;
  margin-left: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
  min-width: 3.5rem;
}
.shelf-group-save-status.is-error { color: var(--danger); }

/* Per-row "Remove from this shelf" control (admin shelf-management page).
   A small icon-only button; the destructive intent is reinforced on hover.
   The accessible name + tooltip live on the <button> in the markup. */
.shelf-directory-th-manage { width: 5rem; text-align: center; }
.shelf-directory-cell-manage { width: 5rem; text-align: center; }
.shelf-remove-form { display: inline; margin: 0; }
.shelf-remove-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.28rem 0.4rem;
  background: var(--panel-3);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.85rem;
  line-height: 1;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.shelf-remove-btn:hover {
  color: var(--danger);
  border-color: var(--danger);
  background: var(--panel-2);
}
.shelf-remove-btn:focus { outline: 2px solid var(--border-focus); outline-offset: 1px; }
.shelf-remove-icon { display: inline-block; }

/* Mobile: collapse the table into a stacked card list at narrow widths
   so iPhone-portrait reads cleanly. The thead disappears; each cell
   gets a label from the `data-label` attribute on the rendered row. */
@media (max-width: 720px) {
  .shelf-directory-table-wrap { border: 0; background: transparent; }
  .shelf-directory-table,
  .shelf-directory-table thead,
  .shelf-directory-table tbody,
  .shelf-directory-table tr,
  .shelf-directory-table th,
  .shelf-directory-table td { display: block; width: auto; }
  .shelf-directory-table thead { display: none; }
  .shelf-directory-row {
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--panel);
    margin-bottom: 0.7rem;
    padding: 0.4rem 0.55rem;
  }
  .shelf-directory-row:hover { background: var(--panel); }
  .shelf-directory-cell {
    padding: 0.35rem 0.25rem;
    border-top: 1px dashed rgba(255, 255, 255, 0.04);
  }
  .shelf-directory-cell:first-child { border-top: 0; }
  .shelf-directory-cell::before {
    content: attr(data-label) " · ";
    font-family: var(--font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
    margin-right: 0.4rem;
  }
  .shelf-directory-cell-tool::before { display: none; }
  .shelf-directory-cell-tool { font-size: 1rem; padding-bottom: 0.45rem; }
  .shelf-directory-use { -webkit-line-clamp: 3; }
}

/* ============================================================================
   DELIGHTFUL PUBLIC SHELVES — local layout for the Delightful XTree shell
   ----------------------------------------------------------------------------
   This block holds ONLY page-specific layout + scope rules for the public
   shelves. The shared Delightful OS visual foundation (tokens, typography
   roles, tree/register/pill/select/meta/status primitives) lives in
   `public/delightful-os.css` — loaded BEFORE this stylesheet on the public
   shelf pages (see src/views/publicShelves.js). Canonical source for the
   shared file is the Site Design repo; mirror edits there into Link Saver.

   Everything below is scoped under `.delightful-public-shell` (the <body>
   class) or uses the public-only `.xt-*` layout classes, so the private
   authenticated app is never touched. Reference (read-only): the Site Redesign
   repo's src/components/FileBrowser/*.tsx + src/styles/global.css. The shell is
   a two-/three-column file-manager port of the live Delightful chrome: a
   visitor arriving from thisisdelightful.com should feel they opened a sibling
   drive, D:\DELIGHTS\, in the same operating environment. No animation.
   ========================================================================== */

/* Base canvas + anchor/selection scope. Design tokens (--xt-*) come from
   delightful-os.css (:root). These rules read them and establish the public
   shell's own document base WITHOUT touching the private app. */
.delightful-public-shell {
  margin: 0;
  min-height: 100vh;
  background: var(--xt-bg);
  background-image: none;
  color: var(--xt-white);
  font-family: var(--xt-font-content);
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
.delightful-public-shell ::selection { background: var(--xt-sel-bg); color: var(--xt-sel-fg); }
.delightful-public-shell a { color: var(--xt-cyan); text-decoration: underline; text-underline-offset: 2px; }
.delightful-public-shell a:hover { color: var(--xt-yellow); }

/* ── Black terminal theme (public shelf surface) ──
   The public shelf shell reads as ONE coherent near-black terminal — an old
   BBS index / DOS file register — instead of a bright DOS-blue page with a dark
   table slab pasted in. We get there by re-pointing three GROUND tokens on the
   public-shell scope only:
     • --xt-bg       the single unified surface (panes, shell, register all share it)
     • --xt-bg-deep  a barely-raised chrome band (header / command bar / table-head
                     / tree + chip hover) — subtle horizontal rhythm, NOT a panel
     • --xt-rule     a softer, desaturated cyan-green divider in place of the
                     electric-cyan grid
   Because every public pane, border, header, command bar, table-head, tree
   hover, empty state and the CSS-only mobile MENU overlay already draw from
   these tokens, the whole surface unifies in a single move — no per-element
   background, no inset shadow.
   ACCENTS ARE DELIBERATELY UNTOUCHED: titles stay chartreuse, links/cyan stay
   cyan, the chartreuse selection/active inversion (active VIEW chip + selected
   tree row) still pops, and the magenta Delightful logo + the bright-cyan
   far-left CP437 rail are unchanged (they use --xt-yellow / --xt-cyan, not the
   ground tokens). The OUTER 2px frame keeps its own strong rule
   (--xt-rule-strong), so the framed-machine edge survives.
   Scope: `.delightful-public-shell` (public <body> only) — the private/admin
   app never carries this class and `:root` is untouched, so nothing leaks.
   Reversible: delete this one rule to fall back to the DOS-blue tokens. */
.delightful-public-shell {
  --xt-bg: #0a0b0e;          /* unified near-black ground (was DOS blue #0000aa) */
  --xt-bg-deep: #14171d;     /* barely-raised chrome: header / command / TH / hover */
  --xt-rule: #356b71;        /* softer desaturated cyan-green divider (was #4bc0d9) */

  /* Two INK tokens that are LOCAL to Link Saver's public shell — they are
     deliberately NOT in delightful-os.css, which is a byte-identical mirror of
     the Site Design source and also paints thisisdelightful.com. Do not add
     them there.

     Both values were hardcoded hexes sitting inline in the rules below. They
     are lifted here unchanged (no visual change) so a ground override can
     re-point them: on a light ground neither survives — a soft cyan-green pill
     and a lavender-white quote both need a different ink entirely, and a
     hardcoded hex cannot follow a token override.

     `--xt-quote-ink` is the ink for descriptive text — the shelf-index blurbs
     and, on Bone, the card pull-quotes. Its sibling `--xt-pill-ink` was removed
     with the Type pill it was created for. */
  --xt-quote-ink: #d8dbff;   /* description / pull-quote ink — soft lavender-white */
}

/* ── Outer ground + 2px frame ── */
.xt-app-shell {
  background: var(--xt-bg);
  padding: 3px;
  min-height: 100vh;
}
.xt-frame {
  display: grid;
  grid-template-columns: 280px 1fr 320px;
  grid-template-rows: auto 1fr minmax(130px, 11rem) auto;
  height: calc(100vh - 6px);
  border: 2px solid var(--xt-rule-strong);
}

/* ── Header (row 1) — the Delightful logo + SYSTEM STATUS. The DOS path and
   the public explanation copy have both left: the path restated the tree's own
   selected row, and the copy is now the kicker above each page's title. ── */
.xt-header {
  grid-column: 1 / span 3;
  grid-row: 1;
  display: flex;
  align-items: center;
  gap: 1rem;
  border-bottom: 1px solid var(--xt-rule);
  background: var(--xt-bg-deep);
  padding: 0.6rem 0.85rem;
}
.xt-header-logo { display: block; flex-shrink: 0; line-height: 0; }
.xt-header-logo img { display: block; height: 34px; width: auto; }
.xt-header-id { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; flex: 1 1 auto; }
.xt-pathbar-status { display: flex; align-items: baseline; gap: 0.6rem; flex-shrink: 0; }

/* ── Rotating SYSTEM STATUS adjective ──
   Ported from the main Delightful site's path-bar `SystemStatus` (typing →
   holding → deleting typewriter cycle). The word slot is a fixed-width
   inline-block so the header never reflows as adjectives cycle — min-width is
   the longest adjective ("strumple-cocking", 16) + one cursor cell, in `ch`
   against the mono header face. The block cursor (`█`) blinks ONLY while a
   complete word is held (the inline script toggles `.is-blinking`); during
   typing/deleting it stays solid. No-JS / reduced-motion: the first word renders
   complete with a static block cursor and never changes. Colour + uppercase come
   from the shared `.xt-status-word`. Scoped to `.delightful-public-shell`. */
.delightful-public-shell .xt-status-word {
  display: inline-block;
  min-width: 17ch;
  text-align: left;
  white-space: pre;
}
.delightful-public-shell .xt-status-cursor.is-blinking {
  animation: xt-status-caret-blink 1.1s steps(1) infinite;
}
@keyframes xt-status-caret-blink {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .delightful-public-shell .xt-status-cursor.is-blinking { animation: none; }
}

/* ── Panes ── */
.xt-pane { min-width: 0; min-height: 0; }
.xt-pane-tree {
  grid-column: 1; grid-row: 2;
  position: relative;
  border-right: 1px solid var(--xt-rule);
  overflow-y: auto;
  padding: 0.75rem 0;
}
/* padding:0 here overrides the global `main { padding }`; max-width:none +
   margin:0 override the global `main { max-width: 620px; margin: 0 auto }` that
   would otherwise cap + centre the register into a narrow floating table. The
   pane fills its grid track so the register reads as the primary file pane. */
.xt-pane-main {
  grid-column: 2; grid-row: 2;
  overflow: hidden auto;
  min-width: 0;
  padding: 0;
  max-width: none;
  margin: 0;
}
.xt-commandbar {
  grid-column: 1 / span 3; grid-row: 4;
  border-top: 1px solid var(--xt-rule);
  background: var(--xt-bg-deep);
  padding: 0.5rem 0.75rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  font-size: 0.78rem;
}
/* The status slot. The DIR / COMMANDS / FILE legend that filled it is gone from
   every surface, so it now renders empty and the Bone block hides it via
   `:empty`; the footer credit + subscribe chip sit beside it. */
.xt-cmd-info {
  flex: 1 1 360px;
  min-width: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.25rem 0.75rem;
  align-items: baseline;
}
/* Footer SIGNAL callout (shelf detail) — copy to the LEFT of the subscribe
   chip, pushed to the bottom-right of the footer. */
.xt-footer-signal {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-left: auto;
  flex-wrap: nowrap;
  justify-content: flex-end;
}
.xt-footer-signal-copy { color: var(--xt-gray); font-size: 0.7rem; min-width: 0; }
.xt-footer-signal-key { color: var(--xt-gray); text-transform: uppercase; letter-spacing: 0.08em; }
.xt-footer-signal .xt-signal-chip { flex-shrink: 0; }

/* The layout — now the ONLY one, used by the index, the 404 and every shelf.
   Two columns (tree | main): the left rail carries the logo, the tree and the
   subscribe offer, and its border-right is the single unbroken vertical rule.
   The `--detail` suffix is historical — it named this layout when it belonged
   to the shelf pages alone — and is kept only to avoid churning every selector
   that hangs off it. */
.xt-frame--detail {
  grid-template-columns: 280px 1fr;
  grid-template-rows: auto 1fr auto;
}
.xt-frame--detail .xt-pane-tree { grid-column: 1; grid-row: 2; border-right: 1px solid var(--xt-rule); }
.xt-frame--detail .xt-pane-main { grid-column: 2; grid-row: 2; padding: 0 0.4rem 0 0; }
.xt-frame--detail .xt-commandbar { grid-column: 1 / span 2; grid-row: 3; }

/* Decorative CP437 rail straddling the pane's left border (static). */
.xt-rail {
  position: absolute;
  left: -5px; top: 6px; bottom: 6px;
  width: 8px;
  color: var(--xt-cyan);
  pointer-events: none;
  overflow: hidden;
  z-index: 1;
  /* Continuous CP437 medium-shade (▒) dither — matches the main Delightful
     site's far-left rail, which is a stacked column of ▒ glyphs (see the
     read-only Site Design FileBrowser `Rail`: RAIL_GLYPH '▒' × 300, leading-none
     + whitespace-pre, full cyan). A 50%-coverage checkerboard reproduces that
     dithered strip as a CONTINUOUS pattern. Replaces the old dashed
     `repeating-linear-gradient` (6px on / 3px gap), which rendered as separated
     blue blocks. Full strength like the main site (the ~50% dither carries the
     density, so no opacity reduction). */
  background-image:
    linear-gradient(45deg, currentColor 25%, transparent 25% 75%, currentColor 75%),
    linear-gradient(45deg, currentColor 25%, transparent 25% 75%, currentColor 75%);
  background-size: 2px 2px;
  background-position: 0 0, 1px 1px;
}

/* Tree rows read as terminal/file-manager labels, not underlined web links.
   Scoped under .delightful-public-shell so `text-decoration: none` out-specifies
   the local `.delightful-public-shell a { text-decoration: underline }` base.
   Row structure + colour states live in delightful-os.css (.xt-tree-row). */
.delightful-public-shell .xt-tree-row { text-decoration: none; }

/* Tree colour roles — re-assert ABOVE the public shell's `a { color: cyan }`
   base. Our tree rows are anchors, so the shared `.xt-tree-folder` /
   `.xt-tree-file` (specificity 0,1,0) lose to `.delightful-public-shell a`
   (0,1,1) and labels rendered cyan instead of the canonical chartreuse. These
   scoped rules (0,2,0 / 0,3,0) win, and HOLD the colour through hover so the
   recipe stays "hover = background only" (no cyan/yellow flip from `a:hover`),
   matching the main site. Site Design styles its tree on a <button> with inline
   colour, so it never hit this — the shared file is unchanged. The selected-row
   colour (--xt-sel-fg) is already handled by the shared `.xt-tree-row.is-selected`
   rules, which out-specify the anchor base on their own. */
.delightful-public-shell .xt-tree-folder,
.delightful-public-shell .xt-tree-folder:hover { color: var(--xt-yellow); }
.delightful-public-shell .xt-tree-file,
.delightful-public-shell .xt-tree-file:hover { color: var(--xt-cyan); }

/* Tree typeface — match the main Delightful site, which renders its tree (all
   .xtree-label chrome) in the custom DelightfulRetroChrome face. We load the
   same web font (copied into public/fonts/) and scope it to the tree pane via a
   local --xt-font-label override, so the public shelf tree matches the main
   site glyph-for-glyph while the rest of the shell keeps the system mono stack.
   The shared --xt-font-label token is unchanged, so Site Design is untouched. */
@font-face {
  font-family: 'DelightfulRetroChrome';
  src: url('/fonts/retro-chrome.woff') format('woff');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
.delightful-public-shell .xt-pane-tree {
  --xt-font-label: 'DelightfulRetroChrome', Menlo, Consolas, "Cascadia Mono", "Courier New", monospace;
}

/* MOUNTED VOLUMES — a small, dim chrome label above the tree. It reads as a
   terminal section caption (gray, smaller + wider-tracked than the drive rows),
   NOT a heading or web nav title. It sits inside .xt-pane-tree so it inherits
   the same retro tree typeface; padding-left matches the tree rows (0.75rem) so
   the label aligns with C:\DELIGHTFUL / D:\DELIGHTS below it. */
.delightful-public-shell .xt-tree-heading {
  margin: 0 0 0.4rem;
  padding: 0 0.75rem;
  color: var(--xt-gray);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  line-height: 1.1;
}

/* A small blank spacer line between the two peer mounted volumes
   (C:\DELIGHTFUL / D:\DELIGHTS) — a subtle terminal pause, ~half a row tall.
   Not a divider rule, not interactive; just vertical space so the volumes don't
   run together while staying visually equal. */
.delightful-public-shell .xt-tree-spacer {
  height: 0.5rem;
  pointer-events: none;
}

/* Mobile MENU overlay — CSS-only <details> disclosure that mirrors the main
   site's full-screen MENU drawer without any client script. The tree is rendered
   twice (the Delightful pattern): the desktop copy lives in the tree pane, and
   this overlay carries its own copy for small screens. On desktop the whole
   overlay is hidden and the tree pane shows the tree; under the mobile
   breakpoint the overlay's ☰ MENU chip appears and opening it covers the screen
   with a DOS-blue panel (see the `@media (max-width: 860px)` block below). */
.delightful-public-shell .xt-mobile-nav { display: none; }

/* ── Main pane ── */
/* The main pane has NO outer padding, so a full-bleed body (the reading room's
   card grid, the shelf index's) can run to the pane's chrome borders and set
   its own. The intro text block gets its padding from .xt-main-head. */
.xt-main-inner { padding: 0; max-width: none; text-align: left; }
.xt-main-head { padding: 1.1rem 1rem 0.45rem; }

/* ── Left-rail metadata — the block below the tree. It carried the FILE /
   DESCRIPTION / DATA panels when there was a register to describe; today every
   surface uses it for the standing subscribe offer (.xt-tree-signal). ── */
.xt-rail-meta { padding: 0.85rem 0.75rem 0.5rem; border-top: 1px solid var(--xt-rule); margin-top: 0.5rem; }
.xt-rail-desc { margin: 0; color: var(--xt-gray); font-size: 0.72rem; line-height: 1.5; }

/* ── SUBSCRIBE chip (Substack). The bordered SIGNAL panel that used to pin it
   to the bottom of the inspector column is gone with that column; the chip now
   sits under the tree in the left rail. It is an anchor, so its rest + hover
   look is scoped here. ── */
.xt-signal-chip {
  display: inline-block;
  border: 1px solid var(--xt-rule);
  padding: 0.25rem 0.5rem;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--xt-yellow);
  text-decoration: none;
}
.xt-signal-chip:hover { background: var(--xt-sel-bg); color: var(--xt-sel-fg); }

/* ── Mobile: stack the shell into a single column ── */
@media (max-width: 860px) {
  .xt-app-shell { padding: 0; }
  .xt-frame {
    display: flex;
    flex-direction: column;
    height: auto;
    min-height: 100vh;
    border: 0;
  }
  .xt-rail { display: none; }

  /* ── Mobile sidebar: content-first ──
     The tree pane carries the rail's panels, reordered BELOW the register on
     shelf-detail pages (see the order rules just below). Rail chrome is
     stripped so an index page collapses cleanly with no empty bordered box.
     Whether the TREE inside it shows is decided by the @supports blocks at the
     end of this media query. */
  .xt-pane-tree { max-height: none; border-right: 0; padding: 0; overflow: visible; }
  .xt-rail-meta { margin-top: 0; }
  /* Content-first order on shelf-detail pages: compact header (flex default,
     order 0) → main register (1) → FILE/DATA rail (2) → command/footer bar (3).
     Desktop is a grid, so these flex `order`s don't apply there. */
  .xt-frame--detail .xt-pane-main { padding-right: 0; order: 1; }
  .xt-frame--detail .xt-pane-tree { max-height: none; order: 2; }
  .xt-frame--detail .xt-commandbar { order: 3; }

  /* The overlay is a CSS-only <details>. Closed: a compact bordered ☰ MENU chip
     pinned to the right just below the header (echoing Delightful's top-right
     path-bar chip). Open: the whole element becomes a full-height DOS-blue
     panel with a ▾ MENU / × CLOSE top bar over the mounted-volumes tree. */
  .delightful-public-shell .xt-mobile-nav { display: block; }
  .delightful-public-shell .xt-mobile-nav-summary {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    list-style: none;
    -webkit-tap-highlight-color: transparent;
  }
  .delightful-public-shell .xt-mobile-nav-summary::-webkit-details-marker { display: none; }
  /* The ☰ MENU chip — a full 44px touch target, terminal-styled (chartreuse on
     a fine rule), inverting to the selection colours on hover/focus. */
  .delightful-public-shell .xt-mobile-nav-chip {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.85rem;
    border: 1px solid var(--xt-rule);
    color: var(--xt-yellow);
    text-transform: uppercase;
    letter-spacing: 0.12em;
  }
  .delightful-public-shell .xt-mobile-nav-summary:hover .xt-mobile-nav-chip,
  .delightful-public-shell .xt-mobile-nav-summary:focus-visible .xt-mobile-nav-chip {
    background: var(--xt-sel-bg);
    color: var(--xt-sel-fg);
    border-color: var(--xt-sel-bg);
  }
  /* The open top bar (▾ MENU / × CLOSE) is hidden while closed; the chip is
     hidden once open. */
  .delightful-public-shell .xt-mobile-nav-bar { display: none; }

  /* Open: a fixed top BAR. It used to be the whole full-screen panel, because
     it contained its own copy of the tree; the panel is now the rail below,
     lifted into place by the :has() rules at the end of this block. The bar and
     that panel share `--xt-mobile-bar-h` so they meet exactly rather than
     agreeing by coincidence. */
  .delightful-public-shell { --xt-mobile-bar-h: 3.75rem; }
  .delightful-public-shell .xt-mobile-nav[open] {
    position: fixed;
    inset: 0 0 auto 0;
    height: var(--xt-mobile-bar-h);
    z-index: 60;
    display: flex;
    flex-direction: column;
    background: var(--xt-bg);
  }
  .delightful-public-shell .xt-mobile-nav[open] .xt-mobile-nav-summary {
    height: var(--xt-mobile-bar-h);
    box-sizing: border-box;
  }
  .delightful-public-shell .xt-mobile-nav[open] .xt-mobile-nav-summary {
    justify-content: space-between;
    background: var(--xt-bg-deep);
    border-bottom: 1px solid var(--xt-rule);
  }
  .delightful-public-shell .xt-mobile-nav[open] .xt-mobile-nav-chip { display: none; }
  .delightful-public-shell .xt-mobile-nav[open] .xt-mobile-nav-bar {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: space-between;
    min-height: 44px;
  }
  .delightful-public-shell .xt-mobile-nav-open {
    color: var(--xt-yellow);
    text-transform: uppercase;
    letter-spacing: 0.12em;
  }
  .delightful-public-shell .xt-mobile-nav-x {
    border: 1px solid var(--xt-rule);
    padding: 0.4rem 0.7rem;
    color: var(--xt-yellow);
    text-transform: uppercase;
    letter-spacing: 0.12em;
  }
  /* ── ONE tree, shown two ways ──────────────────────────────────────────
     The tree used to be rendered TWICE: once in the rail for desktop and once
     inside this drawer for mobile. Every volume and shelf name was therefore in
     the document twice — duplicate navigation text in every extraction, and two
     <nav> landmarks sharing the label "Mounted volumes".

     Now it is rendered once, in the rail, and the drawer's open state lifts
     that single copy into a full-screen panel under the bar. `:has()` is what
     makes that possible without JavaScript: the <details> is in the header and
     the rail is its sibling, so the open state has to be read from a common
     ancestor. (:has() is already used elsewhere in this stylesheet.)

     THE FALLBACK MATTERS MORE THAN THE OPTIMISATION. Without :has() the drawer
     would open onto an empty screen and mobile navigation would be gone. So the
     hiding is scoped to browsers that can do the lift, and where they cannot,
     the tree simply stays visible in the stacked rail and the MENU chip — which
     would then toggle nothing — is removed. Less elegant, fully navigable. */
  @supports selector(:has(*)) {
    .delightful-public-shell .xt-tree-block { display: none; }
    .delightful-public-shell .xt-frame:has(.xt-mobile-nav[open]) .xt-tree-block {
      display: block;
    }
    .delightful-public-shell .xt-frame:has(.xt-mobile-nav[open]) .xt-pane-tree {
      position: fixed;
      inset: var(--xt-mobile-bar-h) 0 0 0;
      z-index: 59;
      overflow-y: auto;
      background: var(--xt-bg);
      padding: 0.85rem 0;
    }
    /* Generous touch rhythm for tree rows inside the open panel. */
    .delightful-public-shell .xt-frame:has(.xt-mobile-nav[open]) .xt-tree-row {
      padding-top: 0.4rem;
      padding-bottom: 0.4rem;
    }
  }
  @supports not selector(:has(*)) {
    .delightful-public-shell .xt-mobile-nav { display: none; }
  }
  /* Only the intro text block gets horizontal padding. */
  .xt-main-head { padding: 1.25rem 1rem 0.85rem; }

  /* ── Compact mobile header: logo | MENU in one row ──
     Matches thisisdelightful.com mobile — a small logo and the MENU chip
     top-right on a single short row. SYSTEM STATUS is hidden to keep it
     compact. The empty `.xt-header-id` (flex:1, min-width:0) is what pushes the
     chip to the right edge; it used to hold the path and the blurb, both of
     which have since left the chrome entirely. */
  .xt-header { flex-wrap: nowrap; align-items: center; gap: 0.6rem; padding: 0.4rem 0.6rem; }
  .xt-header-logo img { height: 20px; }
  .xt-pathbar-status { display: none; }            /* SYSTEM STATUS removed on mobile */
  .delightful-public-shell .xt-header .xt-mobile-nav { flex: 0 0 auto; }
  /* Closed chip carries no extra summary padding — the header's own padding +
     gap space it. The open full-screen overlay keeps its top-bar padding (it is
     position: fixed, escaping the header). */
  .delightful-public-shell .xt-header .xt-mobile-nav:not([open]) .xt-mobile-nav-summary { padding: 0; }
}
/* Reduced motion: no animations exist in this pass; guard future additions. */
@media (prefers-reduced-motion: reduce) {
  .delightful-public-shell * { animation: none !important; transition: none !important; }
}

/* ==========================================================================
   "BONE" — the reading-room ground (four shelves only)
   ==========================================================================
   creative-advice, strategic-thinking, ai-perspectives, writing-is-power get a
   warm off-white reading surface instead of the near-black terminal. They are
   the long-form editorial shelves: you arrive to READ, and white-on-near-black
   at 14px is a scanning colourway, not a reading one.

   SCOPE. Every rule below hangs off `.delightful-public-shell--bone`, a second
   <body> class applied by delightfulShell() from the READING_ROOM_SLUGS
   allowlist in src/views/publicShelves.js. The near-black theme is what you get
   by default; Bone is strictly additive. `:root` is untouched, and
   public/delightful-os.css — a byte-identical mirror of the Site Design source
   whose :root also paints thisisdelightful.com — is not edited at all.

   PLACEMENT. This block sits at the END of the stylesheet on purpose. Several
   of the rules it overrides (e.g. `.delightful-public-shell .xt-tree-folder:hover`)
   carry identical specificity, so source order is what decides them. Moving
   this block earlier would silently hand those back to the near-black values.
   ========================================================================== */

/* ── Literata, self-hosted — CURRENTLY UNREFERENCED ───────────────────────
   Nothing resolves to this face any more: the Bone reading token now points at
   Jost, and every public surface is Bone. The @font-face and the two woff2
   files are kept ON PURPOSE rather than deleted — an unused @font-face costs a
   visitor nothing, because a browser only downloads a face a rule actually
   uses, so this is repo weight and not page weight.

   It is kept because one decision is still open: the pull quotes are italic,
   which a serif made natural, and Jost's italic is an oblique. If the quotes go
   back to a true italic this is the face they would use. Delete both files and
   this block if that question closes the other way.
   ──────────────────────────────────────────────────────────────────────────
   The reading face. The shared `--xt-font-reader` stack is Iowan/Charter/
   Palatino — print faces that were not drawn for small screen text, and the
   register's descriptions are the browsing experience. Literata was drawn for
   screen reading at text sizes.

   Self-hosted as woff2 in /public/fonts, exactly like retro-chrome.woff above:
   no Google Fonts <link>, so the page still makes zero third-party requests and
   leaks no visitor to fonts.gstatic.com. Both files are the variable-weight
   LATIN subset — one file covers 400-600, which is why there is no separate
   500 face. `font-display: swap` matches the existing @font-face.

   latin-ext is deliberately NOT shipped: it would add 108 KB against the 134 KB
   here, and across all 183 records on these four shelves there is not a single
   character outside this subset (the only non-Latin-1 characters in the real
   data are curly quotes, en/em dashes and an ellipsis, all of which live in the
   General Punctuation range covered below). Anything outside the range falls
   back down the stack to Charter/Georgia rather than failing to render. If the
   content ever needs it, add the two latin-ext files and a matching pair of
   @font-face rules — nothing else changes. */
@font-face {
  font-family: 'Literata';
  src: url('/fonts/literata.woff2') format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Literata';
  src: url('/fonts/literata-italic.woff2') format('woff2');
  font-weight: 400 600;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── Town 20 Inline Stroke — the wordmark's own face ──────────────────────
   The typeface the DELIGHTS. logotype is drawn in (J Foundry). Shipped as the
   supplied OTF rather than woff2: at 25 KB it is already smaller than the
   Literata woff2 beside it, and there is no woff2 toolchain in this repo, so
   converting would add one for a few KB.

   Self-hosted like every other face here — no third-party request, and the
   "no Google Fonts" rule the tests enforce is untouched.

   `font-display: swap` matches the others: headings render immediately in the
   fallback and reflow when the face lands. The fallback stack is a geometric
   sans, so the swap is a change of face, not of layout scale. */
@font-face {
  font-family: 'Town20Inline';
  src: url('/fonts/town20-inline-stroke.otf') format('opentype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* The BLACK cut of the same family, registered as weight 700 on the same family
   name so `font-weight` selects between them. The two are a matched pair: the
   Stroke cut is this letterform's inline outline with the fill removed, which
   is why they sit together without looking like two typefaces.

   The sibling "Town 31 Dim" cuts supplied alongside this one are deliberately
   NOT shipped. Black L is a solid letter with a hatched drop shadow — too
   decorative to carry section headings down a reading page — and Black LS / S
   are SHADOW LAYERS, drawn to stack underneath another cut. Set on their own
   they are illegible, so they are not text faces at all. */
@font-face {
  font-family: 'Town20Inline';
  src: url('/fonts/town20-inline-black.otf') format('opentype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ── The ground ───────────────────────────────────────────────────────────
   Ten tokens re-pointed, where the near-black theme needed three. Near-black
   could leave the accents alone; on warm off-white they both fail outright —
   --xt-cyan (#76e5fc frosted blue) and --xt-yellow (#ccff33 chartreuse) each
   sit at roughly 1.1:1 against #f5f2ec, which is invisible, not low-contrast.

   Contrast against the #f5f2ec ground, computed not eyeballed:
     ink       #221f1a  14.7:1  — body, cells, headings          (AAA)
     secondary #5c564c   6.5:1  — captions, meta, chrome labels  (AA)
     accent    #33518f   6.7:1  — links, hover, focus            (AA)
     tertiary  #918a7d   3.1:1  — branch connectors only; large/decorative
   `--xt-dim` is the one token that does not clear AA for body text, which is
   why it is left carrying only the tree's `│ ├─ └─` connectors and the © line.
   Do not move real text onto it.

   The chartreuse selection pair is deliberately UNCHANGED. #ccff33 as a FILL
   with #221f1a text is the one brand mark that survives on every ground, and it
   is what keeps the page unmistakably Delightful once the DOS blue is gone. */
.delightful-public-shell--bone {
  --xt-bg: #f5f2ec;            /* ground — warm off-white */
  --xt-bg-deep: #fbf9f5;       /* raised chrome: header / command bar / TH / hover */
  --xt-white: #221f1a;         /* primary ink (the token name is now a lie; the ROLE is "body ink") */
  --xt-gray: #5c564c;          /* secondary — captions, meta keys, chrome labels */
  --xt-dim: #918a7d;           /* tertiary — branch connectors, © line. NOT body text. */
  --xt-side-value: #5c564c;    /* metadata values / record counts */
  --xt-cyan: #33518f;          /* link + accent — cool blue on warm ground, deliberate */
  --xt-yellow: #221f1a;        /* heading ink. The other yellow ROLES are split out below. */
  --xt-rule: #e2dcd1;
  --xt-rule-strong: #e2dcd1;   /* the outer frame drops to the same hairline — a heavy
                                  dusty-grape edge reads as a bezel on paper */
  --xt-sel-bg: #ccff33;        /* unchanged — the surviving brand mark */
  --xt-sel-fg: #221f1a;

  /* Introduced when the two hardcoded hexes were tokenized; the pill half of
     that pair went with the register. */
  --xt-quote-ink: #5c564c;

  /* Bone-only additions. --xt-accent exists because "interactive" and "heading"
     were the same token (--xt-yellow) on the dark ground and cannot be on this
     one. --xt-sunk / --xt-hairline are the tile ground and the inner divider. */
  --xt-accent: #33518f;
  /* The rollover wash. A tint of the accent rather than a grey, so hover reads
     as "this is a link" in the same voice the link colour already speaks. Used
     with a matching box-shadow spread so the highlight extends past the glyphs
     without inline padding reflowing the line. */
  --xt-accent-wash: rgba(51, 81, 143, 0.10);
  --xt-sunk: #ece7dd;
  --xt-hairline: #ebe6dc;

  /* The reading face for every Bone surface — index, 404 and all fourteen
     shelves. It was Literata, a book face; under the Town wordmark that read as
     two projects sharing a page, so it is now Jost, whose Futura lineage shares
     the wordmark's circles-and-lines construction.

     The shared --xt-font-reader token in delightful-os.css is NOT touched: it is
     the byte-identical mirror, and it also paints thisisdelightful.com. This
     re-point is local to the Bone scope. */
  --xt-font-reader: 'Jost', 'Century Gothic', 'Avenir Next', 'Futura', sans-serif;
}

/* ── Splitting the --xt-yellow roles ──────────────────────────────────────
   On the dark ground one chartreuse served every one of these. On Bone it
   cannot: an editorial heading wants primary ink, a folder row wants secondary,
   an interactive state wants the accent, and the brand mark wants to stay
   chartreuse but as a FILL rather than as text. Re-pointing --xt-yellow above
   handles the heading role (the most prominent one); the rest are split here. */

/* Interactive — hover belongs to the accent, not to heading ink. Without this,
   every hover on Bone would be "text turns the colour it already is". */
.delightful-public-shell--bone a:hover,
.delightful-public-shell--bone .xt-issue-link:hover,
.delightful-public-shell--bone .xt-tree-folder:hover {
  color: var(--xt-accent);
}

/* Focus. The shared rules outline in --xt-yellow, which is now ink — legible,
   but a 1px ink hairline is a weak focus ring on paper. Accent at 2px reads
   clearly and still clears AA against the ground. */
.delightful-public-shell--bone .xt-tree-row:focus-visible,
.delightful-public-shell--bone .xt-select:focus-visible {
  outline: 2px solid var(--xt-accent);
  outline-offset: 1px;
}
.delightful-public-shell--bone a:focus-visible,
.delightful-public-shell--bone summary:focus-visible,
.delightful-public-shell--bone button:focus-visible {
  outline: 2px solid var(--xt-accent);
  outline-offset: 2px;
}

/* Folder identity — secondary ink. Every tree row rendering at full heading
   weight would make the rail shout over the register beside it.

   `:not(.is-selected)` matters. `.xt-tree-row.is-selected` in delightful-os.css
   carries the same specificity as this rule, so without the exclusion this one
   wins on source order and repaints the SELECTED row's ink to secondary — on
   top of the chartreuse selection fill. The visible label survives today only
   because `.xt-tree-row.is-selected .xt-tree-label` is a notch more specific
   and happens to wrap the text; anything ever rendered directly in the row
   would come out secondary-on-chartreuse. Excluding it here fixes the cause
   rather than relying on that accident. */
.delightful-public-shell--bone .xt-tree-folder:not(.is-selected) {
  color: var(--xt-gray);
}

/* SYSTEM STATUS sits in plain ink. The chartreuse fill read as a highlighter
   stripe across the header; the chartreuse brand mark still carries on the
   selected tree row, which is where it does real work. */
.delightful-public-shell--bone .xt-status-word {
  color: var(--xt-gray);
}

/* Outbound chips — SUBSCRIBE and the filter chips take the accent. */
.delightful-public-shell--bone .xt-signal-chip {
  color: var(--xt-accent);
  border-color: var(--xt-rule);
}
.delightful-public-shell--bone .xt-signal-chip:hover,
.delightful-public-shell--bone .xt-signal-chip:focus-visible,
.delightful-public-shell--bone .xt-signal-chip:focus-visible {
  color: var(--xt-accent);
  border-color: var(--xt-accent);
  background: var(--xt-bg-deep);
}

/* Mobile MENU overlay. These live inside the 860px breakpoint, so the Bone
   overrides have to as well or they never apply. The overlay inherits the
   ground through --xt-bg / --xt-bg-deep; only the two yellow-roled labels and
   the chip need splitting out. */
@media (max-width: 860px) {
  .delightful-public-shell--bone .xt-mobile-nav-open,
  .delightful-public-shell--bone .xt-mobile-nav-x {
    color: var(--xt-accent);
  }
  .delightful-public-shell--bone .xt-mobile-nav-chip {
    color: var(--xt-accent);
    border-color: var(--xt-rule);
  }
}

/* ── The reading room ─────────────────────────────────────────────────────
   Sectioned editorial layout, Bone shelves only. The markup is only emitted
   for those four slugs, but every rule is scoped anyway so a future caller
   cannot accidentally paint it onto the register.

   Structure: section head (serif title / hairline rule / right-aligned count),
   then a grid. The first card in a section big enough to justify one is a LEAD
   — a wider tile beside a larger headline. See LEAD_MIN_SECTION_SIZE in
   src/views/publicShelves.js for why that is conditional. */

.delightful-public-shell--bone .xt-reading-room {
  padding: 0.5rem 1rem 2.5rem;
}

.delightful-public-shell--bone .xt-sec,
.delightful-public-shell--bone .xt-unfiled {
  margin: 0 0 3.75rem;
}
.delightful-public-shell--bone .xt-sec:last-child,
.delightful-public-shell--bone .xt-unfiled:last-child { margin-bottom: 0; }

.delightful-public-shell--bone .xt-sec-head {
  display: flex;
  align-items: baseline;
  gap: 0.95rem;
  margin-bottom: 1.5rem;
}
.delightful-public-shell--bone .xt-sec-title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 500;
  letter-spacing: -0.014em;
  color: var(--xt-white);
  white-space: nowrap;
}

/* The page title has to move with the section titles or the two compete. The
   shared .xt-title is 1.6rem/700 — only 1.28x the old 1.25rem section head,
   which is why the section head could not hold its position without a rule
   underneath it doing the work. At 2.25rem against 1.5rem the ratio is 1.5 and
   the hierarchy reads from type alone. Scoped to Bone; every other shelf keeps
   the shared 1.6rem/700. */
.delightful-public-shell--bone .xt-title {
  font-size: clamp(1.9rem, 3.4vw, 2.25rem);
  font-weight: 500;
  letter-spacing: -0.018em;
  line-height: 1.06;
}
/* Unsorted is not a category, so its heading is deliberately quieter than a
   real section's — mono chrome rather than editorial serif. */
.delightful-public-shell--bone .xt-sec-title--quiet {
  font-family: var(--xt-font-label);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--xt-gray);
  font-weight: 400;
}
.delightful-public-shell--bone .xt-sec-rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--xt-rule);
}
.delightful-public-shell--bone .xt-sec-count {
  flex: 0 0 auto;
  font-family: var(--xt-font-label);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  color: var(--xt-dim);
  white-space: nowrap;
}

/* ~212px minimum column, per the specimen. auto-fill (not auto-fit) so a
   two-record section leaves its empty tracks EMPTY rather than stretching two
   cards across the full width — a section's cards should be the same size as
   every other section's. */
.delightful-public-shell--bone .xt-sec-grid {
  display: grid;
  gap: 1.6rem 1.35rem;
  grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
}

/* ── Cards ──────────────────────────────────────────────────────────────── */
.delightful-public-shell--bone .xt-read-card {
  min-width: 0;
}
.delightful-public-shell--bone .xt-read-title {
  margin: 0.7rem 0 0;
  font-size: 1.06rem;
  line-height: 1.22;
  font-weight: 500;
  letter-spacing: -0.008em;
}
.delightful-public-shell--bone .xt-read-title a {
  color: var(--xt-white);
  text-decoration: none;
}
.delightful-public-shell--bone .xt-read-title a:hover,
.delightful-public-shell--bone .xt-read-title a:focus-visible {
  color: var(--xt-accent);
  text-decoration: none;
  /* The wash is painted INSET, not as a full-height background. An inline box
     is as tall as the font's content area (25px at this size), which is taller
     than the 1.22 line-height (20.7px) — so on a wrapped headline consecutive
     lines overlap by ~4px and the highlight reads as one solid block. Raising
     the leading enough to clear it would need 1.47, which is a different design.

     Instead the gradient paints only the middle 72% of each line fragment, so
     the bands sit clear of each other with the layout untouched. `clone` is
     required: the default `slice` would stretch one gradient across the whole
     multi-line box rather than repeating it per line.

     The negative margin against equal padding widens the painted area past the
     glyphs without shifting the text — so the headline still aligns with the
     meta line beneath it. */
  background-image: linear-gradient(
    180deg,
    transparent 14%,
    var(--xt-accent-wash) 14%,
    var(--xt-accent-wash) 86%,
    transparent 86%
  );
  margin: 0 -0.22em;
  padding: 0 0.22em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

.delightful-public-shell--bone .xt-read-meta {
  margin: 0.45rem 0 0;
  font-family: var(--xt-font-label);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--xt-dim);
}
.delightful-public-shell--bone .xt-read-pub { color: var(--xt-gray); font-weight: 500; }
/* The record's issue date, on the meta line beside the publication. Set one
   step quieter than the publication: the publication is what a reader scans
   for, the date is what they check. Both inherit the meta line's mono/uppercase
   treatment, so this only has to say "recede". */
.delightful-public-shell--bone .xt-read-date { color: var(--xt-dim); }
/* "Saved" — the qualifier that stops the date being read as the article's own
   publication date. A shade quieter again than the date it introduces: it is
   the same word on every record, so it should be legible and then ignored. */
.delightful-public-shell--bone .xt-date-label { opacity: 0.72; }
/* The separator between meta tokens. A middot rather than a pipe or a slash —
   it is the lightest mark that still reads as a boundary at 0.6rem, and it is
   aria-hidden at every call site so it is never announced as content. */
.delightful-public-shell--bone .xt-meta-sep {
  margin: 0 0.42em;
  /* --xt-dim, not --xt-hairline. The hairline token is tuned for 1px rules
     against the Bone ground and is very nearly the ground itself (~1.1:1), so a
     glyph painted in it is invisible rather than quiet — the meta line read as
     "AEON 13 APR 2026" with an unexplained gap. Matching the dimmer of the two
     values it separates makes it a separator instead of a defect. */
  color: var(--xt-dim);
}

.delightful-public-shell--bone .xt-read-quote {
  margin: 0.55rem 0 0;
  font-size: 0.9rem;
  line-height: 1.45;
  font-style: italic;
  color: var(--xt-quote-ink);
  /* Measured: quotes average ~100 characters and top out at 270. Four lines
     holds the average comfortably and stops the tail from setting one card
     three times the height of its row-mates. The lead card is unclamped. */
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The lead. Spans the full grid row and splits into tile | text once there is
   room for the two to sit side by side. */
.delightful-public-shell--bone .xt-read-card--lead {
  grid-column: 1 / -1;
  padding-bottom: 1.6rem;
  border-bottom: 1px solid var(--xt-hairline);
}
.delightful-public-shell--bone .xt-read-card--lead .xt-read-title {
  margin-top: 0;
  font-size: clamp(1.35rem, 2.4vw, 1.8rem);
  line-height: 1.12;
}
.delightful-public-shell--bone .xt-read-card--lead .xt-read-quote {
  font-size: 1rem;
  margin-top: 0.7rem;
  -webkit-line-clamp: none;
  display: block;
  overflow: visible;
}
.delightful-public-shell--bone .xt-read-card--lead .xt-read-meta { margin-top: 0.7rem; }
@media (min-width: 660px) {
  .delightful-public-shell--bone .xt-read-card--lead {
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    gap: 1.5rem;
    align-items: center;
  }
}

/* ── Preview tiles ──────────────────────────────────────────────────────────
   The generated pattern is ALWAYS painted and the image, when there is one,
   layers on top. An image that 404s or is refused by its host removes itself
   (`onerror`) and reveals the pattern rather than collapsing the tile — which
   is why the aspect-ratio lives on the tile, not on the image.

   The private shelf cards' phosphor scanline recipe is not reused: white
   scanlines and a teal dot-grid are a dark-screen idea and are invisible or
   alien on warm paper. These are printed textures instead — dot screens, fine
   hatch, soft duotone washes — tinted from Bone's own accent. Only the variant
   PICK is shared (pickFallbackVariant), so a record keeps a stable tile
   identity across both surfaces. No animation: the specimen's light-ground
   patterns are static, and a drifting sweep on paper reads as twitchy. */
.delightful-public-shell--bone .xt-tile {
  /* Three tints, all derived from the ground's own palette: tA the accent, tB a
     warm paper shadow, tC the ink. The specimen's values (.16/.10/.06) were
     drawn at card size; on the lead tile, which is several times the area, the
     same alphas spread far enough apart to read as an empty box rather than as
     texture — which defeats the whole point of a fallback. Nudged up so the
     pattern registers as a deliberate printed surface at BOTH sizes. These are
     the first knobs to reach for when tuning the ground. */
  --tA: rgba(51, 81, 143, 0.22);
  --tB: rgba(120, 98, 58, 0.15);
  --tC: rgba(34, 31, 26, 0.09);

  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--xt-sunk);
  border: 1px solid var(--xt-hairline);
}
/* The lead tile is wider than a card's but NOT taller in proportion — 4/3 at
   lead width pushed the headline beside it too far down the page. */
.delightful-public-shell--bone .xt-read-card--lead .xt-tile { aspect-ratio: 3 / 2; }

.delightful-public-shell--bone .xt-tile-pat { position: absolute; inset: 0; }
.delightful-public-shell--bone .xt-tile-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  z-index: 1;
}

/* The issue backlink — the shelf's only route back to the newsletter. It sits
   above both the pattern and the image, and the tile always renders, so it can
   never go missing with an image. */
.delightful-public-shell--bone .xt-tile-badge {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 2;
  font-family: var(--xt-font-label);
  font-size: 0.58rem;
  letter-spacing: 0.1em;
  padding: 0.2rem 0.4rem;
  background: var(--xt-bg-deep);
  color: var(--xt-gray);
  border-top: 1px solid var(--xt-hairline);
  border-right: 1px solid var(--xt-hairline);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.delightful-public-shell--bone a.xt-tile-badge-link:hover,
.delightful-public-shell--bone a.xt-tile-badge-link:focus-visible {
  color: var(--xt-sel-fg);
  background: var(--xt-sel-bg);
}

.delightful-public-shell--bone .xt-tile.variant-0 .xt-tile-pat {
  background:
    radial-gradient(circle at 1px 1px, var(--tA) 1.1px, transparent 1.8px) 0 0/7px 7px,
    linear-gradient(155deg, var(--tB), transparent 66%);
}
.delightful-public-shell--bone .xt-tile.variant-1 .xt-tile-pat {
  background:
    repeating-linear-gradient(45deg, var(--tA) 0 1px, transparent 1px 7px),
    linear-gradient(200deg, var(--tB), transparent 72%);
}
.delightful-public-shell--bone .xt-tile.variant-2 .xt-tile-pat {
  background:
    repeating-radial-gradient(circle at 82% 18%, transparent 0 11px, var(--tC) 11px 12px),
    linear-gradient(180deg, var(--tB), transparent 70%);
}
.delightful-public-shell--bone .xt-tile.variant-3 .xt-tile-pat {
  background:
    repeating-linear-gradient(90deg, var(--tA) 0 1px, transparent 1px 6px, var(--tC) 6px 7px, transparent 7px 15px);
}
.delightful-public-shell--bone .xt-tile.variant-4 .xt-tile-pat {
  background:
    radial-gradient(ellipse at 28% 72%, var(--tA), transparent 62%),
    radial-gradient(ellipse at 78% 26%, var(--tB), transparent 58%);
}
.delightful-public-shell--bone .xt-tile.variant-5 .xt-tile-pat {
  background:
    linear-gradient(118deg, transparent 34%, var(--tA) 50%, transparent 66%),
    linear-gradient(0deg, var(--tC), transparent 55%);
}

/* ── Compact link rows ────────────────────────────────────────────────────
   One row recipe, two uses: the image-less records inside a section, and the
   "Not yet filed" block. Both want the same quiet treatment — the difference
   between them is what the surrounding block says, not how a row looks.

   A row shows exactly what an image-less record HAS: a title, a publication and
   an issue number. That is the point of demoting it out of the grid rather than
   giving it a prettier placeholder. */
/* NO rules and NO tint. The group is named instead — a heading says what these
   records ARE, where a background only said "these are different" and left the
   reader to work out how. Rows are held together by the heading above them and
   their own rhythm. */
.delightful-public-shell--bone .xt-linklist {
  list-style: none;
  margin: 0;
  padding: 0;
}
.delightful-public-shell--bone .xt-more-head {
  margin: 1.9rem 0 0.5rem;
  font-family: var(--xt-font-label);
  font-size: 0.62rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--xt-dim);
}
.delightful-public-shell--bone .xt-linkrow {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.42rem 0;
}
.delightful-public-shell--bone .xt-linkrow-title {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--xt-white);
  text-decoration: none;
  font-size: 0.95rem;
  line-height: 1.35;
}
.delightful-public-shell--bone .xt-linkrow-title:hover,
.delightful-public-shell--bone .xt-linkrow-title:focus-visible {
  color: var(--xt-accent);
  text-decoration: none;
  /* The wash is painted INSET, not as a full-height background. An inline box
     is as tall as the font's content area (25px at this size), which is taller
     than the 1.22 line-height (20.7px) — so on a wrapped headline consecutive
     lines overlap by ~4px and the highlight reads as one solid block. Raising
     the leading enough to clear it would need 1.47, which is a different design.

     Instead the gradient paints only the middle 72% of each line fragment, so
     the bands sit clear of each other with the layout untouched. `clone` is
     required: the default `slice` would stretch one gradient across the whole
     multi-line box rather than repeating it per line.

     The negative margin against equal padding widens the painted area past the
     glyphs without shifting the text — so the headline still aligns with the
     meta line beneath it. */
  background-image: linear-gradient(
    180deg,
    transparent 14%,
    var(--xt-accent-wash) 14%,
    var(--xt-accent-wash) 86%,
    transparent 86%
  );
  margin: 0 -0.22em;
  padding: 0 0.22em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.delightful-public-shell--bone .xt-linkrow-pub {
  flex: 0 0 auto;
  font-family: var(--xt-font-label);
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--xt-gray);
}
/* The date on a compact row. Matches .xt-linkrow-pub's treatment one step
   dimmer, exactly as the card meta line does — the two surfaces show the same
   two facts and must not diverge in how they rank them. */
.delightful-public-shell--bone .xt-linkrow-date {
  flex: 0 0 auto;
  font-family: var(--xt-font-label);
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--xt-dim);
}
.delightful-public-shell--bone .xt-linkrow-issue {
  flex: 0 0 auto;
  font-family: var(--xt-font-label);
  font-size: 0.6rem;
}

@media (max-width: 560px) {
  .delightful-public-shell--bone .xt-reading-room { padding: 0.5rem 0.85rem 2rem; }
  .delightful-public-shell--bone .xt-sec-grid { gap: 1.4rem 1rem; }
  .delightful-public-shell--bone .xt-linkrow { flex-wrap: wrap; gap: 0.25rem 0.6rem; }
  .delightful-public-shell--bone .xt-linkrow-title { flex: 1 0 100%; }
  /* The 288px track already resolves to one column on a phone; this is the
     matching padding/rhythm, kept beside the reading room's so the two
     surfaces stay in step. */
}

/* ── The shelf index (the homepage) ───────────────────────────────────────
   The front door is the reading room one level up: the same ground, the same
   shell, and the same card anatomy applied to SHELVES instead of records.

   Deliberately pictureless. The images on this site are per-ARTICLE og:images —
   on a shelf page the picture belongs to the headline beside it, but one
   article's picture standing in for a shelf of 92 records is a category error.
   Measured against production, borrowing "the first picture on the shelf" would
   also have put the SAME image on two of the fourteen cards (one link sits on
   both Agency futures and Brand strategy and leads both), and would have filled
   four more with website screenshots and stock photography. The markup keeps a
   card's shape, so reinstating a media slot later is additive.

   NO rule per card. Fourteen hairlines would rebuild exactly the grid of lines
   the reading room spent its passes removing; space holds the cards apart
   instead. This block adds zero page-level horizontal rules. */
/* ── The homepage masthead ────────────────────────────────────────────────
   The main pane is a NAMEPLATE: the site name and one line, nothing else. The
   fourteen shelves live in the rail beside it, and listing them again in the
   main pane is what made the page busy.

   Display size is scoped to `.xt-masthead`, which ONLY the index applies, so a
   shelf page keeps the shared .xt-title — a shelf title is one level below the
   site's own name and should not be set at the same size. */
/* The masthead is the DELIGHTS. wordmark as an image, so the type rules that
   used to size it no longer apply — `line-height: 0` stops the inline box
   adding a descender gap under it, and the width cap matches the picture
   below so the two share a left AND right edge down the column. */
.delightful-public-shell--bone .xt-masthead .xt-wordmark {
  margin: 0 0 0.9rem;
  line-height: 0;
}
.delightful-public-shell--bone .xt-masthead .xt-wordmark img {
  display: block;
  width: 100%;
  max-width: 26rem;
  height: auto;
}
/* Held to a narrow measure so it reads as a line under the name rather than as
   a paragraph. `balance` splits it evenly instead of leaving a word alone on
   the second line; browsers without it fall back to the greedy wrap. */
.delightful-public-shell--bone .xt-masthead .xt-lead {
  /* 1.15 → 1.22rem for the same reason the shelf roles moved: Jost's smaller
     x-height makes a matched size read a step down. */
  font-size: 1.22rem;
  max-width: 46ch;
  text-wrap: balance;
}

/* The homepage picture, under the copy.
   Capped rather than run to the pane's full width: the main pane is ~1150px on
   a laptop and the source is square, so full-bleed would put a 1150px photo on
   a page whose entire point is restraint — the picture would become the page.
   At 26rem it reads as an object sitting under the tagline, and it lines up
   with the 46ch measure of the line above it.
   `height: auto` against the intrinsic width/height attributes is what lets the
   browser reserve the right box without the CSS fighting the aspect ratio. */
.delightful-public-shell--bone .xt-home-figure {
  margin: 2.25rem 0 0;
  padding: 0 1rem;
}
.delightful-public-shell--bone .xt-home-figure img {
  display: block;
  width: 100%;
  max-width: 26rem;
  height: auto;
  border: 1px solid var(--xt-hairline);
}

/* ── Jost — the shelf reading face ────────────────────────────────────────
   The body face under the Town headings. Literata is a book face, and against a
   heavy geometric display it read as two projects sharing a page; Jost is
   Futura-lineage, so heading and body come from the same circles-and-lines
   construction and the shelf title stops looking like a logo dropped onto a
   document.

   Self-hosted from the same latin-subset woff2 Google serves, exactly as
   Literata is — the page makes no third-party request and the no-Google-Fonts
   rule the tests enforce is untouched. Variable 400-600 in one file per style,
   which is why there is no separate 500 face.

   THE KNOWN COST: geometric sans are the least comfortable faces for continuous
   reading — the circular o, e and c repeat into a pattern. The pull quotes run
   to 270 characters at the tail, which is where that will be felt if it is felt
   anywhere. Accepted deliberately in exchange for heading and body reading as
   one system. */
@font-face {
  font-family: 'Jost';
  src: url('/fonts/jost.woff2') format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Jost';
  src: url('/fonts/jost-italic.woff2') format('woff2');
  font-weight: 400 600;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Jost sets a smaller x-height than Literata did at the same em, so matched
   sizes read a step smaller. Nudged up across the reading roles to put the
   actual letter height back where it was, rather than leaving the page quietly
   shrunken. These are the shelf's own roles; the homepage standfirst is nudged
   with the masthead it belongs to. */
.delightful-public-shell--bone.xt-shelf-page .xt-read-title { font-size: 1.12rem; }
.delightful-public-shell--bone.xt-shelf-page .xt-read-card--lead .xt-read-title { font-size: clamp(1.4rem, 2.5vw, 1.9rem); }
.delightful-public-shell--bone.xt-shelf-page .xt-read-quote { font-size: 0.95rem; }
.delightful-public-shell--bone.xt-shelf-page .xt-read-card--lead .xt-read-quote { font-size: 1.05rem; }
.delightful-public-shell--bone.xt-shelf-page .xt-lead { font-size: 1.12rem; }
.delightful-public-shell--bone.xt-shelf-page .xt-linkrow-title { font-size: 1rem; }

/* ── Town-face headings (every shelf) ─────────────────────────────────────
   The shelf title and the section headings set in the wordmark's own face,
   scoped to `.xt-shelf-page` — the body class every shelf detail page carries.
   The index and the 404 share the Bone ground but NOT this typography, which is
   what the class separates.

   Uppercase and tracked, because that is what makes it read as the SAME
   treatment as the logotype rather than merely the same font — Town 20 Inline
   is drawn for display, and set in sentence case at body-adjacent sizes it
   loses the wordmark's character entirely.

   The tracking is deliberately looser on the title than on the section heads:
   the title is a logotype-scale object, the section heads are structural and
   sit above content, so they take enough to relate without becoming a texture
   to read through. */
.delightful-public-shell--bone.xt-shelf-page .xt-title {
  font-family: 'Town20Inline', 'Avenir Next', 'Century Gothic', 'Futura', sans-serif;
  /* The Black cut. The hairline Stroke read as LESS important than the record
     headlines beneath it — a section heading out-ranked by its own contents —
     and it was harder to read at heading sizes. Weight is the honest fix;
     making the hairline bigger would only have made a faint thing large. */
  font-weight: 700;
  text-transform: uppercase;
  /* Tracked less than the hairline was. Wide tracking is what stops a thin face
     from collapsing into a grey line; on a black face the same tracking just
     makes it shout. */
  letter-spacing: 0.09em;
  /* Smaller than the hairline needed. Weight carries the emphasis now, so the
     size does not have to — and a black face at the hairline size would have
     overpowered the shelf description sitting under it. */
  font-size: clamp(1.75rem, 3.2vw, 2.25rem);
  line-height: 1.14;
  /* Tracking adds a trailing space after the last letter; pulling it back
     keeps the optical left edge aligned with the copy beneath. */
  margin-right: -0.09em;
}

.delightful-public-shell--bone.xt-shelf-page .xt-sec-title {
  font-family: 'Town20Inline', 'Avenir Next', 'Century Gothic', 'Futura', sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  /* 1.3rem of BLACK against a 1.06rem Literata 400 card headline. The ratio is
     smaller than the hairline needed (1.45rem) because weight is doing the
     ranking now instead of size.

     The inline detail inside the stroke does thin out at this size — the letters
     read closer to solid here than in the title above. That is accepted: the
     alternative is a section heading large enough to compete with the shelf
     title, which is the wrong problem to solve. */
  font-size: 1.3rem;
  margin-right: -0.07em;
}
/* The record headline steps DOWN so the heading above it can step up: at 500 it
   was competing with its own section heading, which is what made the page feel
   inverted. The headline is still the thing you read; it just stops out-ranking
   the label that groups it. */
.delightful-public-shell--bone.xt-shelf-page .xt-read-title {
  font-weight: 400;
}

/* "Not yet filed" keeps its quiet mono label — it is not a real section, and
   giving it the display face would assert a grouping that does not exist. */
.delightful-public-shell--bone.xt-shelf-page .xt-sec-title--quiet {
  font-family: var(--xt-font-label);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
}

/* ── Reading-room chrome ──────────────────────────────────────────────────
   The shell was built as a file manager: a path bar that names the current
   directory, and a command bar legending DIR / COMMANDS / FILE. On a page you
   are meant to READ, both were restating things the page already says — the
   path duplicated the tree's selected row, and the command bar duplicated the
   record count while legending affordances ("CLICK OPEN") a reader does not
   need told. The view omits both for these four shelves; what is left here is
   the layout that follows from their absence. */

/* Header. The path and the explanation line have both left it — the path
   duplicated the tree, and the explanation now sits as a kicker above the shelf
   title. No bottom rule and no flat chrome band: the header fades into the
   ground through a gradient across its own height rather than being fenced off
   from it, which is why the padding is generous — the fade needs somewhere to
   happen.

   The header is a two-column grid matching the frame's own tracks, so the logo
   lands in the LEFT rail with the navigation rather than floating above both
   columns. Its right edge carries the same rule the tree pane does, and because
   the two cells share the 280px boundary the line reads as one stroke running
   from the top of the page down to the footer.

   Scoped above the mobile breakpoint: under 861px the frame stacks into a
   single column and the header becomes a flex row with the MENU overlay, where
   a 280px track would be wrong. */
.delightful-public-shell--bone .xt-header {
  border-bottom: 0;
  background: linear-gradient(180deg, var(--xt-bg-deep) 0%, var(--xt-bg) 100%);
}
/* The left rail spans the header row as well as the content row, so the logo,
   the navigation and the subscribe block live in ONE grid area and can hold
   still together while the page scrolls. That is also what draws the vertical
   rule: one element's border-right, running from the top of the page down to
   the footer, rather than two cells lining up.

   `position: sticky` needs slack in the containing block, which is why this
   restructure was necessary and why making the header sticky was not an option
   — row 1 is exactly as tall as the header, so a sticky header has nowhere to
   travel. The rail's grid area is the full page height, so the inner wrapper
   has somewhere to stay put.

   Scoped above the mobile breakpoint: below it the frame stacks to one column,
   the tree is hidden in favour of the MENU overlay, and none of this applies. */
@media (min-width: 861px) {
  .delightful-public-shell--bone .xt-frame--detail {
    /* The header gradient moves to the frame so it spans BOTH columns. On the
       header alone it would have stopped at the rail's edge. */
    background: linear-gradient(180deg, var(--xt-bg-deep) 0, var(--xt-bg) 4.75rem);
  }
  .delightful-public-shell--bone .xt-frame--detail .xt-header {
    grid-column: 2;
    grid-row: 1;
    background: transparent;
    justify-content: flex-end;
    padding: 1.1rem 1rem 1.5rem;
  }
  .delightful-public-shell--bone .xt-frame--detail .xt-pane-tree {
    grid-column: 1;
    grid-row: 1 / 3;
    border-right: 1px solid var(--xt-rule);
    padding: 0;
  }
  .delightful-public-shell--bone .xt-rail-sticky {
    position: sticky;
    /* 5px = .xt-app-shell's 3px pad + .xt-frame's 2px border, i.e. exactly
       where the wrapper already sits at scroll 0. Sticking at 0 instead would
       make the whole rail lurch up by that much the moment sticky engaged. */
    top: 5px;
    padding: 1.1rem 0 1.5rem;
  }
  /* The header's own logo is the mobile one; on desktop the rail carries it. */
  .delightful-public-shell--bone .xt-frame--detail .xt-header-logo { display: none; }
  .delightful-public-shell--bone .xt-rail-logo {
    display: block;
    /* 0.75rem matches .xt-tree-heading / .xt-tree-row, and the asset is the
       left-trimmed logo, so the ink lines up with MOUNTED VOLUMES rather than
       sitting 7px in behind the original's transparent padding. */
    padding: 0 0.75rem;
    margin-bottom: 1.4rem;
    line-height: 0;
  }
  .delightful-public-shell--bone .xt-rail-logo img {
    display: block;
    height: 34px;
    width: auto;
  }
}
/* Mobile keeps the header logo and never shows the rail one. Scoped to its own
   breakpoint rather than left unscoped: an unscoped rule sits later in the file
   than the desktop block above and would win on equal specificity. */
@media (max-width: 860px) {
  .delightful-public-shell--bone .xt-rail-logo { display: none; }
}

/* The decorative CP437 dither rail stays gone. It drew a second vertical strip
   a few pixels to the left of the real one, which read as a doubled edge. */
.delightful-public-shell--bone .xt-rail {
  display: none;
}

/* With the PUBLIC REGISTER / ARCHIVED LINKS ONLY / N RECORDS caption gone, the
   shelf description follows the title directly, so it closes up against it —
   the old margin was sized to clear a line of chrome that is no longer there. */
.delightful-public-shell--bone .xt-title {
  margin-bottom: 0.4rem;
}
.delightful-public-shell--bone .xt-lead {
  margin-top: 0;
  color: var(--xt-gray);
}

/* The shelf dateline: record count and when the shelf last moved. This is the
   one piece of chrome the reading room takes BACK, and only because the two
   facts it carries are not stated anywhere else on the page — the per-section
   "n RECORDS" captions count sections, not the shelf, and nothing named a date
   at all. It sits under the description as a quiet mono line, so it reads as a
   dateline rather than as the return of the old PUBLIC REGISTER caption. */
.delightful-public-shell--bone .xt-shelf-dateline {
  margin: 0.55rem 0 0;
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--xt-dim);
}

/* The explanation line, now a kicker introducing the shelf title. Smaller than
   it was in the header — it is a lead-in, not a statement in its own right. */
.delightful-public-shell--bone .xt-kicker {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  line-height: 1.4;
  font-style: italic;
  color: var(--xt-dim);
}

/* Footer: static at the end of the document rather than pinned to the viewport.
   The frame stops being a fixed-height grid whose middle row scrolls
   internally, and becomes a normal flow container — so the page scrolls as one
   piece and the footer arrives when the records run out, instead of floating
   over them. min-height keeps the frame filling the viewport on a short shelf.

   The tree column sticks while that happens: without it, a 92-record shelf
   leaves an empty 280px column running the whole length of the page. */
.delightful-public-shell--bone .xt-frame--detail {
  height: auto;
  min-height: calc(100vh - 6px);
  grid-template-rows: auto 1fr auto;
}
.delightful-public-shell--bone .xt-frame--detail .xt-pane-main {
  overflow: visible;
}
.delightful-public-shell--bone .xt-frame--detail .xt-pane-tree {
  overflow: visible;
  align-self: stretch;
}
/* Subscribe, under the tree. `.xt-rail-meta` carries a border-top and margin
   from the register layout; neither is wanted here — the block is separated by
   space, and the vertical rule beside it is the only line in this column. */
.delightful-public-shell--bone .xt-rail-meta {
  border-top: 0;
  margin-top: 2.25rem;
  padding: 0 0.75rem;
}
/* On mobile the frame stacks, so this block is the last thing before the footer
   rule — and it was landing flush against it, with a measured gap of ZERO
   between the subscribe chip and the border. The 1.5rem that separates them on
   desktop comes from `.xt-rail-sticky`, whose padding is declared inside the
   861px block and therefore does not apply down here. Restored explicitly, and
   more generously than desktop: stacked, the chip and the footer read as one
   clump without a clear break between them. */
@media (max-width: 860px) {
  .delightful-public-shell--bone .xt-rail-meta { padding-bottom: 2.75rem; }
}
.delightful-public-shell--bone .xt-tree-signal-copy {
  margin: 0 0 0.7rem;
  font-family: var(--xt-font-reader);
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--xt-gray);
}
.delightful-public-shell--bone .xt-tree-signal .xt-signal-chip {
  display: inline-block;
  font-size: 0.62rem;
  padding: 0.6rem 0.8rem;
  letter-spacing: 0.1em;
}

/* Who publishes this, under the subscribe chip. Set in the reading face at the
   signal copy's size, one step dimmer: the subscribe offer above it is an ASK
   and should stay the louder of the two, while this is context a reader takes
   in once. The rule above it separates the two without adding another line to
   a page that has deliberately few — it is a hairline, and it is the only one
   in the rail below the tree. */
.delightful-public-shell--bone .xt-studio-note {
  margin-top: 1.5rem;
  /* NO horizontal padding. The 0.75rem inset for this column is already on the
     shared parent `.xt-rail-meta`, which is what the subscribe copy above sits
     inside — adding it again here double-inset this block and left it hanging
     12px to the right of the line it should align with. Top padding only, to
     clear the rule. */
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--xt-hairline);
}
.delightful-public-shell--bone .xt-studio-copy {
  margin: 0;
  font-family: var(--xt-font-reader);
  font-size: 0.78rem;
  line-height: 1.5;
  /* --xt-gray, not --xt-dim. This is prose, not chrome: dim measures ~3:1 on
     the Bone ground, which is fine for a separator glyph and not fine for a
     sentence at 12.5px. The hierarchy against the subscribe copy above comes
     from SIZE instead, which costs nothing in legibility. */
  color: var(--xt-gray);
}
/* The links carry the underline; the prose around them does not. In a block
   this small and this quiet, colour alone is not a reliable affordance, and
   six links in four lines need to be separable from the sentence holding
   them. */
.delightful-public-shell--bone .xt-studio-copy a {
  color: var(--xt-white);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
.delightful-public-shell--bone .xt-studio-copy a:hover,
.delightful-public-shell--bone .xt-studio-copy a:focus-visible {
  color: var(--xt-white);
}

/* The command bar now carries only the footer, so it no longer needs the
   DIR/COMMANDS/FILE grid or the empty slot that grid left behind. It also has
   no rule above it — the footer sits on the ground like everything else. */
.delightful-public-shell--bone .xt-cmd-info:empty {
  display: none;
}
.delightful-public-shell--bone .xt-commandbar {
  border-top: 1px solid var(--xt-rule);
  background: transparent;
  padding: 1.35rem 1rem 1.5rem;
  font-size: 0.94rem;
}
.delightful-public-shell--bone .xt-reading-footer {
  flex: 1 1 auto;
  display: flex;
  justify-content: flex-end;
}
.delightful-public-shell--bone .xt-reading-footer-credit {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--xt-gray);
  text-align: right;
}
/* A real button rather than a chrome chip: more room inside it, and the accent
   as a border so it reads as the one action on the page. */
.delightful-public-shell--bone .xt-signal-chip {
  padding: 0.7rem 1.15rem;
  font-size: 0.72rem;
  letter-spacing: 0.13em;
  border-color: var(--xt-accent);
  white-space: nowrap;
}
.delightful-public-shell--bone .xt-signal-chip:hover,
.delightful-public-shell--bone .xt-signal-chip:focus-visible {
  background: var(--xt-accent);
  color: var(--xt-bg-deep);
  border-color: var(--xt-accent);
  box-shadow: none;
}
