/* App-host (studio) styles. Same voice as the public site: Karla for
 * chrome and headings, Petrona for prose, JetBrains Mono for dates and
 * code (fonts.css). Theme follows html[data-theme], stamped pre-paint by
 * theme.js and live-synced across tabs; without a stored choice it
 * defaults to the OS preference. */

:root {
  /* The spacing scale — the only gaps the studio uses. */
  --s0: 0.25rem;
  --s1: 0.5rem;
  --s2: 1rem;
  --s3: 1.5rem;
  --s4: 2.5rem;
  --s5: 4rem;
  --bg: #f9faf9;
  --fg: #1a1e1b;
  /* 4.73:1 on --bg — see the note in public.css. The previous #6f7772 sat
   * at 4.40:1, just under the AA floor, and muted carries blockquote text
   * in the editor's own highlight style. */
  --muted: #6a726d;
  /* The accent: the notebook green — the same ink as the public site. */
  --accent: oklch(0.42 0.09 155);
  --border: #e1e6e2;
  --danger: #dc2626;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --sans: "Karla", system-ui, -apple-system, "Segoe UI", sans-serif;
  --serif: "Petrona", Georgia, "Times New Roman", serif;
}
:root[data-theme="dark"] {
  --bg: #151816;
  --fg: #e5e9e6;
  --muted: #8b938d;
  --accent: oklch(0.72 0.09 155);
  --border: #2b302c;
  --danger: #f87171;
}
* { box-sizing: border-box; }
/* The page itself never scrolls sideways. Anything too wide for the
 * viewport — a long shelf, a wide table — scrolls inside its own box;
 * a document that slides under the thumb on every vertical swipe is the
 * worst reading experience a phone can have. This is the backstop, not
 * the fix: give the wide thing its own overflow-x so it stays reachable
 * rather than clipped. */
html { overflow-x: hidden; }
body {
  margin: 0;
  max-width: 100%;
  overflow-x: hidden;
  font-family: var(--sans);
  background: var(--bg);
  color: var(--fg);
  line-height: 1.6;
}
/* The page frame: no rail to clear, no centered column — each page's
 * deskline and content own the width (the blogs desk narrows itself). */
main { max-width: none; margin: var(--s3) auto; padding: 0 var(--s5); }
h1 { font-size: 1.4rem; }
a { color: var(--accent); }
:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
button {
  font: inherit;
  font-family: var(--sans);
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
}
button:hover { border-color: var(--fg); }
/* Primary buttons ink in the accent; text takes the page background so
 * both themes keep contrast (dark green/near-white, light green/near-black). */
button.primary { background: var(--accent); border-color: var(--accent); color: var(--bg); }
button.subtle { border-color: transparent; color: var(--muted); }
button.subtle:hover { color: var(--fg); border-color: var(--border); }
button.danger { background: transparent; color: var(--danger); border-color: transparent; }
button.danger:hover { border-color: var(--danger); }
form.inline { display: inline; }
textarea, input[type="text"] {
  font: inherit;
  width: 100%;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
}

/* Sign-in page: one centered line, nothing else. */
body.signin { margin: 0; }
main.signin {
  margin: 0;
  max-width: none;
  min-height: 100dvh;
  display: grid;
  place-items: center;
}
main.signin a {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 1.05rem;
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.15rem;
}
main.signin a:hover { border-color: var(--fg); }

/* Studio */
.mono { font-family: var(--mono); }
.muted { color: var(--muted); }
main.wide { max-width: 52rem; }

/* ── The deskline ───────────────────────────────────────────────────
 * The page's one line of chrome. Left: the way back (deeper pages
 * only), then the page's name — said exactly here and nowhere else.
 * Right: this page's controls, at most one in the accent. */
.deskline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s1) var(--s3);
  margin-bottom: var(--s3);
  font-family: var(--sans);
  font-size: 0.85rem;
}
.deskline .back { color: var(--muted); text-decoration: none; white-space: nowrap; }
.deskline .back:hover, .deskline .back:focus-visible { color: var(--fg); }
.deskline .who {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: lowercase;
  color: var(--fg);
}
.deskline .who .members-marker { display: inline-flex; align-items: center; margin-left: 0.3rem; vertical-align: -0.1em; }
.deskline .gap { flex: 1; }
/* Chips (media views): quiet text, the active one wears the accent
 * underline — the app's only "you are here" mark. */
.deskline .chips { display: inline-flex; flex-wrap: wrap; gap: var(--s2); color: var(--muted); }
.deskline .chips a { color: var(--muted); text-decoration: none; white-space: nowrap; }
.deskline .chips a:hover { color: var(--fg); }
.deskline .chips a[aria-current="true"] {
  color: var(--fg);
  font-weight: 600;
  border-bottom: 2px solid var(--accent);
  padding-bottom: 2px;
}
/* find: quiet until focused — no rule until the field is reached for. */
.deskline .find { display: flex; align-items: baseline; gap: var(--s1); width: 11.5rem; border-bottom: 1px solid transparent; }
.deskline .find:focus-within { border-bottom-color: var(--fg); }
.deskline .find input {
  border: none;
  border-radius: 0;
  background: transparent;
  padding: 0;
  font-size: 0.85rem;
  min-width: 0;
  width: 100%;
}
.deskline .find input:focus { outline: none; }
.deskline .find input::placeholder { font-family: var(--serif); font-style: italic; color: var(--muted); }
.deskline .find kbd { font-family: var(--mono); font-size: 0.7rem; color: var(--muted); }
/* The page's one accent action: an icon or a label and a word, never a
 * boxed widget. Covers buttons and the add-files file-input label. */
.deskline .act {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s1);
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--accent);
  text-decoration: none;
  border: none;
  border-radius: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
}
.deskline .act svg { width: 0.85em; height: 0.85em; transform: translateY(1px); }
.deskline button.quiet { font-size: 0.85rem; padding: 0; border-radius: 0; }
.deskline button.quiet:hover { background: transparent; color: var(--fg); }
.deskline .meta { font-family: var(--mono); font-size: 0.7rem; color: var(--muted); white-space: nowrap; }

/* ── The corner ─────────────────────────────────────────────────────
 * The ambient icon cluster at the page's foot: read, theme, sign out —
 * identical set and order on every studio page. Sticky, not fixed: it
 * keeps its place in the document (settling into it at the page end)
 * but rides the viewport bottom meanwhile, so the controls are always
 * in reach without a scroll. The page background masks content passing
 * beneath. */
.corner {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s4);
  padding: var(--s1) 0;
  background: var(--bg);
}
.corner button, .corner a {
  border: none;
  background: transparent;
  color: var(--muted);
  padding: 0;
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  display: block;
}
.corner svg { width: 1rem; height: 1rem; display: block; }
.corner button:hover, .corner a:hover, .corner :focus-visible { color: var(--fg); }

/* ── The drawers ────────────────────────────────────────────────────
 * The desk's other places, one uniform grammar: a mono label and a
 * count. Identical geometry, identical voice. */
.drawers { display: grid; gap: var(--s1); justify-items: start; padding: 0 var(--s3); }
.drawer {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  text-decoration: none;
}
.drawer:hover, .drawer:focus-visible { color: var(--fg); }
/* The studio margin: worktray, then the drawers beneath it. */
.margin-col { display: grid; gap: var(--s3); align-content: start; }
/* De-chipped: one bordered row, everything in it quiet text. */
.notebook-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.6rem;
  align-items: baseline;
  margin-bottom: 2.1rem;
  padding-bottom: 0.8rem;
  border-bottom: 1px solid var(--border);
}
.notebook-bar .bar-label {
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  color: var(--fg);
}
.notebook-bar .bar-gap { flex: 1; }
/* Blog filter: a muted leading word, then quiet links — the active one
 * darkened with an accent underline. Never a boxed widget. */
.notebook-bar .blog-filter {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.95rem;
  align-items: baseline;
  font-size: 0.85rem;
}
.blog-filter a {
  text-decoration: none;
  color: var(--muted);
  white-space: nowrap;
}
.blog-filter a:hover { color: var(--fg); }
.blog-filter a[aria-current="true"] {
  color: var(--fg);
  border-bottom: 2px solid var(--accent);
  padding-bottom: 2px;
}
/* The members-only mark: a small lock beside the blog name, in the
 * label's ink so it reads as a quiet property, not a badge. */
.blog-filter .members-marker {
  display: inline-flex;
  align-items: center;
  margin-left: 0.3rem;
  vertical-align: -0.1em;
}
.lock-icon {
  width: 0.82em;
  height: 0.82em;
  opacity: 0.75;
}
.notebook-bar .new-blog { text-decoration: none; font-size: 0.8rem; color: var(--muted); }
.notebook-bar .new-blog:hover { color: var(--fg); }
/* find a post: an underline, not a box; the / hint says it's a key away. */
.notebook-bar .find {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border-bottom: 1px solid var(--border);
  padding: 0.15rem 0.15rem;
  width: 11.5rem;
  margin-right: 0.6rem;
}
.notebook-bar .find input {
  border: none;
  border-radius: 0;
  padding: 0.1rem 0;
  background: transparent;
  font-size: 0.85rem;
  min-width: 0;
}
.notebook-bar .find input:focus { outline: none; }
.notebook-bar .find:focus-within { border-bottom-color: var(--fg); }
.notebook-bar .find input::placeholder { font-family: var(--serif); font-style: italic; color: var(--muted); }
.notebook-bar .find kbd {
  font-size: 0.7rem;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0 0.3rem;
}
/* The one prominent affordance: writing — the pen and a word in the
 * accent ink, underlined like a promise, not boxed like a widget. */
button.new-post {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  font-weight: 600;
  font-size: 0.95rem;
  padding: 0;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--accent);
  white-space: nowrap;
  flex-shrink: 0;
  cursor: pointer;
}
button.new-post svg { width: 0.85em; height: 0.85em; transform: translateY(1px); }
button.new-post .new-post-word {
  border-bottom: 1px solid var(--accent);
  padding-bottom: 1px;
}
button.new-post:hover { border-color: transparent; }
.notebook-body {
  display: grid;
  grid-template-columns: 17.5rem 1fr;
  gap: var(--s4);
  align-items: start;
}
/* The notebook's dot grammar (shared with the reading surfaces): filled
 * = published, open ring = in progress, dotted = unlisted. */
.notebook li {
  display: flex;
  gap: 0.55rem;
  align-items: baseline;
}
.dot {
  width: 0.44rem;
  height: 0.44rem;
  flex: none;
  border-radius: 50%;
  background: var(--accent);
  transform: translateY(-1px);
}
.dot.draft { background: transparent; border: 1.5px solid var(--accent); }
.dot.unlisted { background: transparent; border: 1.5px dotted var(--muted); }
/* Published with staged edits: the filled dot gains a quiet halo — the
 * state changes the mark, not a label. */
.dot.edits {
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3.5px color-mix(in oklab, var(--accent) 55%, transparent);
}
.entry-text { min-width: 0; }
/* Titles wrap — never ellipsize. */
.notebook .listing-title {
  color: var(--fg);
  text-decoration: none;
  display: block;
}
.notebook .listing-title:hover, .notebook .listing-title:focus-visible { text-decoration: underline; }
.notebook .listing-title.untitled { color: var(--muted); font-style: italic; }
.notebook .pin-mark { color: var(--accent); font-size: 0.7rem; }
.entry-meta { display: block; font-size: 0.75rem; }
/* Meta lines: the blog name IS the filter — data as navigation. */
.entry-meta a { color: inherit; text-decoration: none; }
.entry-meta a:hover, .entry-meta a:focus-visible { color: var(--fg); text-decoration: underline; }
/* The worktray: in-progress on a quiet accent wash — the "pick this
 * back up" zone. Theme-safe: mixed from the theme's own inks. */
.in-progress {
  margin: 0;
  background: color-mix(in oklab, var(--accent) 7%, transparent);
  border-radius: 10px;
  padding: var(--s3);
  align-self: start;
}
.in-progress h2 {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: lowercase;
  color: var(--accent);
  margin: 0 0 var(--s2);
  display: flex;
  align-items: baseline;
}
.in-progress h2 .count { margin-left: auto; }
.in-progress ul { list-style: none; margin: 0; padding: 0; }
.in-progress li { margin: var(--s2) 0; }
/* The timeline: the same month grid the reading surfaces use. */
.notebook .months {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14.5rem, 1fr));
  gap: var(--s4);
  align-content: start;
}
/* Cancel the global section margin so the first month row's header
 * lines up with the in-progress heading beside it (the grid gap owns
 * the spacing between rows). */
.notebook .month { margin: 0; }
/* No rule under the marker — same as the reading surfaces (public.css):
 * the mono voice does the separating, and a full-width line over a short
 * month underlines empty space. The trash grid inherits this. */
.notebook .month h2 {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: lowercase;
  color: var(--muted);
  margin: 0 0 0.65rem;
}
.notebook .month ul { list-style: none; margin: 0; padding: 0; }
.notebook .month li { margin: var(--s2) 0; }
.timeline-foot {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 1.5rem 0;
  font-size: 0.8rem;
}
.timeline-foot a { color: var(--muted); text-decoration: none; }
.timeline-foot a:hover { color: var(--fg); }
.timeline-foot .page-num { font-size: 0.7rem; }
/* Search results: one flat list in the notebook frame. */
.search-results h2 { font-size: 0.75rem; font-weight: 400; letter-spacing: 0.08em; }
.search-results h2 a { color: inherit; }
.search-results ul { list-style: none; margin: 0; padding: 0; max-width: 32rem; }
.search-results li { margin: 0.65rem 0; }
/* Trash: the notebook's month grid, grouped by the month a post was
 * deleted. The hollow grey dot (.trashed) is the shared grammar for a post
 * that left the page. Month header/list styles come from .notebook .month
 * above; only the grid, dot, per-row actions, note, and empty state are
 * particular to the trash. */
/* List beside preview: the months read straight down one narrow column
 * (a long trash scrolls rather than wrapping into a second timeline),
 * with the selected post's source held beside them. */
.trash-body {
  display: grid;
  grid-template-columns: minmax(20rem, 1fr) minmax(18rem, 26rem);
  gap: var(--s4);
  align-items: start;
  max-width: 72rem;
}
.trash-months {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s4);
  align-content: start;
}
.dot.trashed { background: transparent; border: 1.5px solid var(--border); }
/* The title is the preview link, styled as the plain listing text. */
.trash-months .listing-title { color: var(--muted); text-decoration: none; }
.trash-months li:hover .listing-title,
.trash-months li.selected .listing-title { color: var(--fg); }
/* The previewed row: the accent underline says "you are reading this
 * one" — the same mark as an active chip. */
.trash-months li.selected .listing-title {
  border-bottom: 2px solid var(--accent);
  align-self: start;
}
/* Accent verb inside a row (restore): the row's one next action. */
.entry-actions button.accent-word { color: var(--accent); font-weight: 600; }
.entry-actions button.accent-word:hover { background: transparent; color: var(--accent); text-decoration: underline; }

/* The preview pane: the deleted post's source, sticky so it stays put
 * while the list scrolls past it. */
.trash-preview {
  position: sticky;
  top: 1.5rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1.2rem 1.35rem;
  min-height: 18rem;
  max-height: calc(100vh - 6rem);
  overflow-y: auto;
}
.trash-preview .preview-head { margin-bottom: 0.9rem; }
.trash-preview .preview-head h2 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--fg);
}
.trash-preview .preview-head .entry-meta { margin: 0.25rem 0 0; font-size: 0.72rem; }
.trash-preview .preview-source {
  margin: 0;
  font-size: 0.76rem;
  line-height: 1.75;
  color: var(--muted);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.trash-preview .preview-empty { font-size: 0.75rem; margin: 0; }
/* Per-row actions ride at the end of the row, revealed on hover/focus. */
.trash-months .entry-actions {
  margin-left: auto;
  display: inline-flex;
  gap: 0.15rem;
  opacity: 0;
}
.trash-months li:hover .entry-actions,
.trash-months li:focus-within .entry-actions { opacity: 1; }
.trash-months .entry-actions form.inline { display: inline-flex; }
.trash-months .entry-actions button { font-size: 0.72rem; padding: 0 0.3rem; white-space: nowrap; }
.notebook-bar .bar-count { font-size: 0.7rem; }
.notebook-bar button.quiet.danger { font-size: 0.8rem; }
/* Empty state: a dashed hollow circle and a quiet sentence. */
.trash-empty {
  display: grid;
  justify-items: center;
  gap: 0.8rem;
  padding: 5rem 1rem 3rem;
  color: var(--muted);
  text-align: center;
}
.trash-empty .glyph {
  width: 2.4rem;
  height: 2.4rem;
  border: 1.5px dashed var(--border);
  border-radius: 50%;
}
.trash-empty p { margin: 0; font-size: 0.9rem; }
.trash-empty .sub { font-size: 0.7rem; }
/* On narrow screens the hover affordance can't be reached — always show
 * the per-row actions. */
@media (max-width: 48rem) {
  /* No room to hold a preview beside the list — it stacks under it and
   * stops being sticky (nothing to stay beside). */
  .trash-body { grid-template-columns: 1fr; gap: 1.8rem; }
  .trash-preview { position: static; max-height: none; min-height: 0; }
  .trash-months .entry-actions { opacity: 1; }
}
/* The studio-wide ⌘K palette (palette.js) — the same quiet card the
 * editor island draws, for creation and navigation from any studio
 * page. */
.palette-scrim {
  position: fixed;
  inset: 0;
  background: color-mix(in srgb, var(--fg) 30%, transparent);
  z-index: 19;
}
.palette {
  position: fixed;
  left: 50%;
  top: 4.5rem;
  transform: translateX(-50%);
  width: min(35rem, calc(100vw - 2rem));
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 24px 64px color-mix(in srgb, var(--fg) 28%, transparent);
  overflow: hidden;
  z-index: 20;
  font-family: var(--sans);
}
.palette[hidden], .palette-scrim[hidden] { display: none; }
.palette input {
  width: 100%;
  font: inherit;
  font-size: 0.95rem;
  padding: 0.9rem 1.1rem;
  border: none;
  border-radius: 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  color: var(--fg);
}
.palette input:focus-visible { outline: none; }
.palette input::placeholder {
  font-family: var(--serif);
  font-style: italic;
  color: var(--muted);
}
.palette-list {
  padding: 0.5rem;
  max-height: 55vh;
  overflow-y: auto;
  display: grid;
}
.palette-group {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  padding: 0.5rem 0.75rem 0.25rem;
}
.palette-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font: inherit;
  font-size: 0.9rem;
  text-align: left;
  padding: 0.55rem 0.75rem;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
}
.palette-item:hover, .palette-item.hi {
  background: color-mix(in srgb, var(--fg) 6%, transparent);
  border-color: transparent;
}
.palette-hint {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--muted);
}

/* Timeline pin controls: a quiet hover affordance at a row's end. Rows
 * with a home blog can pin in place; unpinning arms first (arm.js). */
.entry-pin { margin-left: auto; opacity: 0; }
.notebook li:hover .entry-pin,
.notebook li:focus-within .entry-pin { opacity: 1; }
.entry-pin .pin-toggle {
  font-size: 0.72rem;
  padding: 0 0.3rem;
  white-space: nowrap;
}
.entry-pin .pin-toggle.armed { color: var(--danger); }

/* ── The pins desk ──────────────────────────────────────────────────
 * The sitewide pinned shelf as a page: one narrow ordered list, each
 * row draggable (pins.js) with nudges + unpin revealed on reach. */
.pins-desk { max-width: 36rem; margin: 0 auto; }
/* The corner keeps the narrow desks' left margin. */
main:has(.pins-desk) > .corner { max-width: 36rem; margin-left: auto; margin-right: auto; }
main:has(.blogs-desk) > .corner { max-width: 46rem; margin-left: auto; margin-right: auto; }
.pin-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); max-width: 32rem; }
.pin-list li { display: flex; gap: 0.55rem; align-items: baseline; cursor: grab; }
.pin-list li.dragging { opacity: 0.75; cursor: grabbing; }
.pin-list .entry-actions {
  margin-left: auto;
  display: inline-flex;
  gap: 0.15rem;
  opacity: 0;
}
.pin-list li:hover .entry-actions,
.pin-list li:focus-within .entry-actions { opacity: 1; }
.pin-list .entry-actions form.inline { display: inline-flex; }
.pin-list .entry-actions button { font-size: 0.72rem; padding: 0 0.3rem; white-space: nowrap; }
@media (max-width: 48rem) {
  .pin-list .entry-actions { opacity: 1; }
}

/* ── The blogs desk ─────────────────────────────────────────────────
 * A narrow centered column: the shelf, then the pulled blog's panel. */
.blogs-desk { max-width: 46rem; margin: 0 auto; }

/* The shelf: blogs as book spines standing on a shared baseline rule.
 * Vertical text here is the point — these are books, not chrome. */
.shelfline {
  display: flex;
  align-items: flex-end;
  gap: var(--s3);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--s4);
}
.shelfline .gap { flex: 1; }
/* Enough blogs outgrow a phone's width. The shelf scrolls sideways on
 * its own — a flex item needs min-width:0 to shrink below its content,
 * or the row pushes the whole page wide and every vertical scroll drags
 * the document sideways. The spines keep their height, so only the
 * shelf moves. */
.shelf {
  display: flex;
  align-items: flex-end;
  gap: var(--s2);
  min-width: 0;
  overflow-x: auto;
  /* The spines' focus ring and the pulled book's shadow would be
   * clipped by overflow-x alone; the padding gives them room. */
  padding: var(--s1) 0;
  overscroll-behavior-x: contain;
  /* The books stand ON the shelfline; a scrollbar drawn across it reads
   * as a second rule cutting the spines off at the foot. Overlay
   * scrollbars (touch) already behave — this quiets the always-visible
   * kind down to the border's own weight. */
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.shelf::-webkit-scrollbar { height: 3px; }
.shelf::-webkit-scrollbar-track { background: transparent; }
.shelf::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 2px;
}
.shelf .book {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  height: 8.5rem;
  width: 2.4rem;
  white-space: nowrap;
  padding: var(--s2) var(--s1);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 6px 6px;
  text-decoration: none;
  color: var(--fg);
  font-family: var(--sans);
  font-size: 0.85rem;
  background: var(--bg);
  overflow: hidden;
}
/* The count stands at the spine's FOOT — with the rotated writing mode
 * the flex flow's start is the visual bottom, so the count is ordered
 * first and its auto margin pushes the name toward the spine's head. */
.shelf .book .n {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 400;
  color: var(--muted);
  order: -1;
  margin-bottom: auto;
}
.shelf .book .lock-icon { width: 0.7rem; height: 0.7rem; opacity: 0.7; flex: none; }
/* The pen marks the caller's default blog — new posts start there. */
.shelf .book .pen-icon { width: 0.7rem; height: 0.7rem; opacity: 0.7; flex: none; }
.shelf .book:hover { border-color: var(--muted); }
/* Horizontal gestures reorder (blogs.js); vertical stays the page's. */
.shelf .book { touch-action: pan-y; }
.shelf .book[aria-current="true"] {
  border-color: var(--accent);
  font-weight: 600;
  box-shadow: 0 6px 18px color-mix(in oklab, var(--accent) 12%, transparent);
}
.shelf .book.ghost { border-style: dashed; color: var(--muted); background: transparent; }
/* Mid-drag (blogs.js): the pulled book floats a little off the shelf. */
.shelf .book.dragging {
  opacity: 0.75;
  border-color: var(--accent);
  box-shadow: 0 6px 18px color-mix(in oklab, var(--accent) 18%, transparent);
  cursor: grabbing;
}

/* The panel: mono keys down the left, values beside them. Inputs look
 * like inputs — a resting rule under every editable value, accent on
 * focus; enter saves per field, so there are no save buttons. */
.panel-grid {
  display: grid;
  grid-template-columns: 4.5rem 1fr;
  gap: var(--s2) var(--s3);
  align-items: baseline;
  font-size: 0.85rem;
}
.panel-grid .k { font-size: 0.7rem; color: var(--muted); text-align: right; }
.panel-grid .v { min-width: 0; }
.panel-grid input {
  border: none;
  border-radius: 0;
  background: transparent;
  border-bottom: 1px solid var(--border);
  padding: 0 0.1rem;
}
.panel-grid input:focus { outline: none; border-bottom-color: var(--accent); }
/* Choices are quiet text — the picked one wears the accent underline,
 * exactly like an active chip. The radio itself is visually hidden. */
.choice-set { display: inline-flex; gap: 0.95rem; align-items: baseline; color: var(--muted); }
.choice-set .choice { cursor: pointer; }
.choice-set input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
.choice-set .choice span { color: var(--muted); white-space: nowrap; }
.choice-set .choice:hover span { color: var(--fg); }
.choice-set input:checked + span {
  color: var(--fg);
  font-weight: 600;
  border-bottom: 2px solid var(--accent);
  padding-bottom: 2px;
}
.choice-set input:focus-visible + span { outline: 2px solid var(--fg); outline-offset: 2px; }
.choice-form .choice-save { margin-left: 0.5rem; padding: 0.1rem 0.3rem; }
.panel-grid input.panel-name { font-family: var(--sans); font-size: 1rem; font-weight: 600; width: 100%; max-width: 24rem; }
.panel-grid input.panel-about { font-family: var(--sans); font-size: 0.85rem; width: 100%; max-width: 30rem; }
.panel-grid .writers, .panel-grid .order {
  display: flex;
  gap: 0.25rem var(--s3);
  align-items: baseline;
  flex-wrap: wrap;
}
.panel-grid .writer { white-space: nowrap; }
.panel-grid .role { font-size: 0.66rem; }
.panel-grid .writers .quiet { padding: 0.1rem 0.3rem; }
/* The remove action discloses on its row's hover/focus — inline words
 * never mean two different things at once. Always visible on touch. */
.writers .writer .inline { opacity: 0; transition: opacity 0.12s; }
.writers .writer:hover .inline, .writers .writer:focus-within .inline { opacity: 1; }
@media (hover: none) {
  .writers .writer .inline { opacity: 1; }
}
.panel-grid .add-writer input[type="email"] {
  font-size: 0.85rem;
  font-family: var(--serif);
  font-style: italic;
  width: 14rem;
  padding: 0 0.15rem;
}
.panel-grid .order .nudge { font-size: 0.8rem; padding: 0.1rem 0.3rem; }
.panel-foot { display: flex; padding-top: var(--s4); }
.panel-foot button.quiet { padding: 0.1rem 0.3rem; }
/* A server refusal (name collision, unknown writer) — inline over the
 * panel, never a bare error page. */
.panel-error {
  margin: 0 0 var(--s2);
  padding: 0.4rem 0.6rem;
  border-radius: 6px;
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  font-size: 0.85rem;
  color: var(--danger);
}
@media (max-width: 40rem) {
  .panel-grid { grid-template-columns: 1fr; gap: 0.3rem; }
  .panel-grid .k { text-align: left; margin-top: 0.6rem; }
}

/* Small screens: the margin folds above the timeline. */
@media (max-width: 48rem) {
  .notebook-body { grid-template-columns: 1fr; gap: 2rem; }
  .notebook-bar .find { order: 5; }
  /* No hover on touch: pin controls stay visible. */
  .entry-pin { opacity: 1; }
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
form.stack { display: grid; gap: 0.9rem; margin: 1rem 0; }
form.stack label { display: grid; gap: 0.3rem; font-size: 0.9rem; }
form.stack button { justify-self: start; }
form.row, .row { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; margin: 0.75rem 0; }
select, input[type="email"], input[type="date"] {
  font: inherit;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
}
textarea { resize: vertical; line-height: 1.5; }
section { margin-top: 2rem; }
section h2 { font-size: 0.95rem; color: var(--muted); font-weight: 400; }

/* Progressive disclosure: quiet <details> blocks for secondary actions. */
details.disclosure { margin-top: 2rem; }
details.disclosure > summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 0.9rem;
  list-style-position: outside;
}
details.disclosure > summary:hover { color: var(--fg); }
details.disclosure[open] > summary { margin-bottom: 0.75rem; }

/* Theme toggle: fixed bottom-left. Only the chrome-free editor (Focus
 * layout) uses this fixed id form — every other studio page renders
 * `.theme-toggle` inside the corner's icon cluster instead. */
#theme-toggle {
  position: fixed;
  left: 1rem;
  bottom: 1rem;
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 1rem;
  cursor: pointer;
  padding: 0.5rem;
  z-index: 5;
}
#theme-toggle:hover { color: var(--fg); }
/* Phones: the floating toggle sits on top of whatever is in the
 * bottom-left corner, so it docks into the fixed top bar instead. It
 * goes on the LEFT, after the sidebar toggle and the way back — the
 * bar's right end belongs to the publish button, which is wide enough
 * on a phone to swallow anything parked there. The button stays in the
 * layout's markup; theme.js binds it by selector either way. */
@media (max-width: 48rem) {
  #theme-toggle {
    top: 0;
    /* Just past the "← studio" link (which ends near 7.2rem at 375px);
       the publish button's left edge starts well right of this. */
    left: 7.5rem;
    bottom: auto;
    /* 2.9rem is the island's --bar-h; that variable lives on .editor,
       which is not an ancestor of this body-level button. */
    height: 2.9rem;
    display: flex;
    align-items: center;
    z-index: 16;
  }
}

/* Quiet text buttons (same treatment as the editor island's chrome). */
button.quiet {
  border: none;
  background: transparent;
  color: var(--muted);
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
}
button.quiet:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 7%, transparent); }
button.quiet.danger:hover, button.quiet.danger.armed { color: var(--danger); background: transparent; }

/* Media inbox: a month timeline where photos, galleries, loose files,
 * and bundles sit side by side — the same voice as the notebook. */
input[type="file"] { font: inherit; font-size: 0.85rem; color: var(--muted); }
.add-files { display: inline-flex; align-items: baseline; gap: 0.5rem; }
.add-files-label {
  color: var(--accent);
  font-weight: 600;
  font-size: 0.95rem;
  border-bottom: 1px solid var(--accent);
  padding-bottom: 1px;
  cursor: pointer;
  white-space: nowrap;
}
.notebook-bar .bar-label a { color: var(--muted); text-decoration: none; }
.notebook-bar .bar-label a:hover { color: var(--fg); }
.notebook-bar .bar-note { font-size: 0.7rem; }
.media-month { margin: 0 0 var(--s3); }
/* No rule under the month marker — the quiet mono voice separates. */
.media-month h2 {
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: lowercase;
  color: var(--muted);
  margin: 0 0 var(--s2);
}
.inbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: var(--s3);
}
.inbox-card { display: grid; gap: var(--s1); align-content: start; }
.inbox-card.collection-card { grid-column: span 2; }
.photo-tile img, .photo-tile video, .gallery-cell img, .gallery-cell video, .mosaic img, .mosaic video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* One tile height across photo, file, and mosaic tiles, so every card's
 * name shares a baseline with its neighbors'. */
.photo-tile {
  display: block;
  height: 12rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  overflow: hidden;
}
/* A gallery card: a small mosaic — one lead image, two side tiles. No
 * "+N more" overlay: the kind tag's count already says how many. */
.mosaic {
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 3px;
  height: 12rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  overflow: hidden;
  text-decoration: none;
}
.mosaic > *:first-child { grid-row: span 2; }
/* A bundle card: the file list itself is the thumbnail. */
.bundle-list {
  display: grid;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg);
  text-decoration: none;
  color: var(--fg);
}
.bundle-row {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.45rem 0.85rem;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
  font-size: 0.72rem;
  min-width: 0;
}
.bundle-list .bundle-row:last-child { border-bottom: none; }
.bundle-row .ext { color: var(--accent); flex: none; }
.bundle-row .fname {
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.bundle-row .fsize { margin-left: auto; flex: none; }
.file-tile {
  display: grid;
  gap: 0.25rem;
  justify-items: center;
  align-content: center;
  height: 12rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
}
/* Stitch selection (media.js): loose tiles become selectable; a chosen
 * card wears the accent ring, and the sticky stitch bar collects the
 * batch into a new gallery or bundle. */
.stitchable { cursor: pointer; }
.inbox-card.stitch-selected .photo-tile,
.inbox-card.stitch-selected .file-tile {
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* The card a ⌘-click from the editor landed on (#m-{id}) wears the
 * same accent ring as a stitch selection — "this one". */
.inbox-card:target .photo-tile,
.inbox-card:target .file-tile,
.gallery-item:target .gallery-cell,
.bundle-file:target .bundle-row {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.stitch-bar[hidden] { display: none; }
.stitch-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 1rem;
  padding: 0.5rem 0.25rem;
  background: var(--bg);
  border-top: 1px solid var(--border);
  font-family: Karla, system-ui, sans-serif;
  font-size: 0.85rem;
}
.stitch-bar .stitch-count { font-size: 0.7rem; }
.stitch-bar .stitch-spacer { flex: 1; }
.stitch-bar .stitch-make {
  font: inherit;
  font-weight: 600;
  padding: 0.35rem 1rem;
  border: none;
  border-radius: 8px;
  background: var(--accent);
  color: var(--bg);
  cursor: pointer;
  white-space: nowrap;
}
.file-tile .ext { font-size: 1rem; color: var(--accent); }
.file-tile .fname, .file-tile .fsize {
  font-size: 0.66rem;
  max-width: 90%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Name and kind share one line: the name yields, the kind never wraps. */
.card-title { display: flex; align-items: baseline; gap: 0.5rem; font-size: 0.85rem; min-width: 0; }
/* A collection card's name edits in place, in the title's weight. */
.card-title form { flex: 1; min-width: 0; display: flex; }
.card-title input.media-name { width: 100%; min-width: 0; }
input.media-name.collection-name { font-weight: 600; }
.card-title .kind-tag { font-size: 0.7rem; color: var(--accent); flex: none; white-space: nowrap; }
.card-meta { font-size: 0.7rem; overflow-wrap: anywhere; }
/* The name reads as a caption (Karla chrome, the card's own ink), not a
 * form field: the base input box is stripped so it sits inline like a
 * label until you hover or focus, when a bottom rule invites the edit.
 * The `input.` prefix beats the global input[type=text] box rule. */
input.media-name {
  width: 100%;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--fg);
  border: none;
  border-radius: 0;
  padding: 0.1rem 0;
  background: transparent;
  border-bottom: 1px solid transparent;
}
.media-name::placeholder { color: var(--muted); font-style: italic; }
.media-name:hover, .media-name:focus {
  border-bottom-color: var(--border);
  outline: none;
}
.media-name:focus { border-bottom-color: var(--accent); }
/* Progressive disclosure: a card at rest is tile + name + meta. Its
 * actions appear when reached for (hover/focus); always on touch. */
.card-actions { display: flex; align-items: center; gap: 0.25rem 0.5rem; flex-wrap: wrap; min-width: 0; }
.inbox-card .card-actions, .gallery-item .card-actions { opacity: 0; }
.inbox-card:hover .card-actions, .inbox-card:focus-within .card-actions,
.gallery-item:hover .card-actions, .gallery-item:focus-within .card-actions { opacity: 1; }
@media (hover: none) {
  .inbox-card .card-actions, .gallery-item .card-actions { opacity: 1; }
}
.card-actions button, .card-actions summary { font-size: 0.75rem; padding: 0.2rem 0.4rem; white-space: nowrap; }
/* The bundle… menu: a small disclosure popover. */
.bundle-menu { position: relative; }
.bundle-menu > summary {
  list-style: none;
  cursor: pointer;
  color: var(--muted);
  font-size: 0.75rem;
  padding: 0.2rem 0.4rem;
  border-radius: 6px;
  white-space: nowrap;
}
.bundle-menu > summary:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 7%, transparent); }
.bundle-menu[open] > summary { color: var(--fg); }
.bundle-pop {
  position: absolute;
  right: 0;
  top: calc(100% + 0.3rem);
  z-index: 10;
  width: 15rem;
  display: grid;
  gap: 0.15rem;
  padding: 0.5rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 10px 28px color-mix(in srgb, var(--fg) 16%, transparent);
}
.bundle-pop .mono { font-size: 0.62rem; letter-spacing: 0.08em; padding: 0.15rem 0.4rem; }
.bundle-pop button { text-align: left; width: 100%; font-size: 0.8rem; }
.bundle-pop input[type="text"] { font-size: 0.85rem; padding: 0.35rem 0.5rem; }
/* The "add to…" submenu: a typeahead over the existing collections
 * (media.js caps the list to the five most recent until you type). */
.add-to > summary {
  list-style: none;
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--muted);
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
}
.add-to > summary:hover { color: var(--fg); background: color-mix(in srgb, var(--fg) 7%, transparent); }
.add-to[open] > summary { color: var(--fg); }
.bundle-pop .add-to input.addto-filter {
  font-size: 0.8rem;
  border: none;
  border-radius: 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  padding: 0.2rem 0.15rem;
  margin: 0.15rem 0 0.25rem;
}
.add-to .addto-filter:focus { outline: none; border-bottom-color: var(--accent); }
.add-to .addto-list { display: grid; gap: 0.1rem; max-height: 11rem; overflow-y: auto; }
.addto-item { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A collection open: the gallery grid or the bundle's file table. Each
 * gallery item is a photo plus the loose-item affordances beneath it
 * (name, embed reference, remove). */
.gallery-open {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: 1.4rem 1.25rem;
}
.gallery-item { display: grid; gap: var(--s1); align-content: start; }
.gallery-cell {
  display: block;
  overflow: hidden;
  height: 12rem;
  border-radius: 8px;
  border: 1px solid var(--border);
}
.bundle-open {
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg);
  max-width: 60rem;
}
.bundle-open .bundle-row { font-size: 0.78rem; padding: 0.6rem 1rem; }
.bundle-open .bundle-file:last-child .bundle-row { border-bottom: none; }
.bundle-row .row-links { margin-left: auto; display: inline-flex; gap: 0.9rem; align-items: baseline; }
.bundle-row .row-links a { color: var(--muted); text-decoration: none; font-family: var(--sans); font-size: 0.75rem; }
.bundle-row .row-links a:hover { color: var(--fg); }
/* When action links take the right edge, sizes sit beside the name. */
.bundle-open .bundle-row .fsize { margin-left: 0; }
.csv-head {
  border-collapse: collapse;
  font-size: 0.68rem;
  color: var(--muted);
  margin: 0 1rem 0.4rem;
}
.csv-head th {
  text-align: left;
  font-weight: 400;
  color: var(--fg);
  padding: 0.1rem 1.1rem 0.25rem 0;
  border-bottom: 1px solid var(--border);
}
.csv-head td { padding: 0.18rem 1.1rem 0.18rem 0; }
.csv-note { display: block; font-size: 0.62rem; padding: 0 1rem 0.6rem; }
/* The collection deskline's rename-in-place: the name input sized to
 * the name it holds, in the caption idiom (input.media-name). */
.deskline input.deskline-name { width: 12rem; font-size: 1rem; }
.bundle-row .row-links button.quiet { font-size: 0.75rem; padding: 0 0.3rem; }

/* Small screens: tighter chrome. */
@media (max-width: 40rem) {
  nav { padding: 0.6rem 1rem; gap: 0.75rem; }
  main { margin: 1.25rem auto; padding: 0 1rem; }
}

/* Focus shell (the editor): no nav, the page is the writing surface.
 * The island's --measure decides the text column; main stays fluid.
 * The island draws its own fixed top bar and offsets itself below it. */
main.focus {
  max-width: 64rem;
  margin: 0 auto;
  padding: 0 1.5rem;
}
/* Wide and full measures outgrow the shell's reading column: let the
 * manuscript's own max-width (or the page, for "full") take over. */
main.focus:has(.editor[data-width="wide"]),
main.focus:has(.editor[data-width="full"]) {
  max-width: none;
}
/* The way back sits out of the text column on wide screens (80rem
 * clears even the "full" measure)… */
/* The no-JS fallback's way back (the island's top bar replaces it on
 * hydration). */
.editor-back {
  font-family: var(--sans);
  font-size: 0.85rem;
  text-decoration: none;
  display: inline-block;
  margin: 1.1rem 0 0.75rem;
}
.editor-back:hover { color: var(--fg); }
details.post-options { margin-top: 3rem; }
