/* base.css — theme-neutral structural rules, layout, components, infrastructure */
/* Theme-specific overrides live in island.css and academia.css */

/* =========================================================================
   styles.css — extracted verbatim from the original inline <style> blocks.
   Block 1 (base app styles) + Block 2 (profile tab extensions), concatenated
   in source order. Block 0 (inlined Google Fonts @font-face rules with
   bundler UUID URLs) is intentionally omitted; fonts are now loaded via
   a normal <link rel="stylesheet"> to fonts.googleapis.com in index.html.
   ========================================================================= */

/* ============================================================
   Couplet — Character Profile Application
   A small fog-threaded island. Earned tenderness under pressure.
   ============================================================ */

/* =========================================================================
   CANONICAL BREAKPOINTS — I-076 (+ Phase 18 semantic tiers)
   All media queries in this project must use one of these values.
   Adding a new breakpoint requires updating this list.

   480px  — registry filter sheet trigger
   560px  — narrow phone: hide topbar date / weather-label; likes-grid single col
   640px  — small phone: reduce padding, typography, admin layout
   720px  — medium phone: hide wordmark subtitle/separator; topbar widgets cluster
   768px  — topbar nav icons hidden (topbar collapses to title + bell + menu)
   880px  — guidebook / timeline: collapse two-column to single-column
   900px  — topbar nav-item text labels hidden (icons-only nav)
   980px  — forum: collapse thread-row and post-body grid layouts
   1024px — tablet landscape / small desktop: profile and shipping collapse
   1180px — topbar nav-label: already hidden at 900px (see N1 block)

   Semantic tiers (Phase 18) — use these for NEW responsive work; map onto the
   values above. Existing ad-hoc rules are NOT rewritten (additive convention).
     phone        ≤ 480px
     large-phone  ≤ 600px
     tablet       ≤ 768px
     tablet+      ≤ 1024px
   Phase 18 introduced 600px (large-phone) as a sanctioned value alongside the set above.
   ========================================================================= */

:root {
  /* Cross-theme typography. The font stack is identical in every theme;
     theme files only redefine paper/ink/accent/shadow tokens. */
  /* Cormorant dropped 2026-06-14 (F1, I-051): not loaded anymore; Bubble overrides --font-display to Fraunces (theme.css). */
  --font-display: 'Noto Serif KR', serif;
  --font-body:    'Hanken Grotesk', 'Noto Sans KR', sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, monospace;
  --font-korean:  'Noto Serif KR', serif;
}

/* ── A11y: visible focus baseline. Keyboard focus must always be visible. Uses
   :focus-visible so mouse clicks don't draw rings, only keyboard nav. --accent
   is a theme token (defined in foundation), so this stays theme-neutral. ── */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background-color 300ms cubic-bezier(.2,.7,.3,1);
}

#root { position: relative; z-index: 1; }

.btn-link {
  background: none; border: none; padding: 0;
  font-family: var(--font-body); font-size: 12px;
  letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
  color: var(--ink-mute); cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
  transition: color 180ms ease;
}
.btn-link:hover { color: var(--ink); }

/* ============================================================
   APP SHELL
   ============================================================ */
.app-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  position: relative;
}
/* ============================================================
   DASHBOARD
   ============================================================ */
.dashboard {
  max-width: var(--page-w-contained);
  margin: 0 auto;
  padding: 40px 48px 96px;
  width: 100%;
  position: relative;
}

.char-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 28px;
}
.char-card-accent {
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--card-accent, var(--accent));
}
.char-card-name {
  font-family: var(--font-display);
  font-size: 28px;
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 4px;
}
.char-card-name--no-alt { margin-bottom: calc(4px + 10px); }
.char-card-hangul {
  font-family: var(--font-korean);
  font-size: 13px;
  color: var(--ink-mute);
  margin-bottom: 12px;
}
.char-card-meta {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  display: flex; flex-direction: column; gap: 4px;
}
.char-card-meta .row { display: flex; justify-content: space-between; }
/* Quick-stats field contents on character cards (registry). */
.char-card-stat-value { font-variation-settings: "opsz" 72, "SOFT" 40, "WONK" 0; padding-right: 0.25em; }
/* Tone dot nudge (task: registry tone footer). Renders only under tone-enabled themes. */
.char-card-tone-dot { margin-left: 3px; }
@keyframes linkCopiedFade {
  0%   { opacity: 0; transform: translateY(-4px); }
  12%  { opacity: 1; transform: translateY(0); }
  72%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-2px); }
}
.char-card-new .label {
  color: var(--ink-mute);
}
.char-card-new .quiet {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-faint);
}

/* ── Registry filter bar (rf-) ─────────────────────────── */
.rf-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 10px 0;
}
/* Registry filter dock (I-350 / Band A S3.2) — the filter/sort bar floats at the
   sticky bottom over the scrolling cards; on narrow screens the filters collapse to
   a trigger + sheet, with count + sort as a slim pill. All themes (token palette). */
.rfd-dock {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  z-index: 40;
  margin-top: 28px;
  /* Issue 1 (polish v1.15.x): full-viewport-width bar, no floating-pill chrome.
     Side padding matches .dashboard (48px); collapses to 20px at <=640px below. */
  padding: 8px 48px 12px;
}
.rfd-sheet-trigger { display: none; }   /* wide screens: filters shown inline */
@media (max-width: 480px) {
  .rfd-dock { gap: 10px; flex-wrap: nowrap; }
  .rfd-sheet-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    padding: 7px 12px;
    background: transparent;
    border: 1px solid var(--rule);
    border-radius: 9px;
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 13px;
    cursor: pointer;
  }
  .rfd-dock .rf-bar-left { display: none; }   /* hidden until the sheet opens */
  .rfd-dock.rfd-sheet-open .rf-bar-left {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: calc(100% + 8px);
    padding: 14px;
    background: var(--paper-soft);
    border: 1px solid var(--rule);
    border-radius: 12px;
    box-shadow: 0 -10px 30px -12px rgba(0, 0, 0, 0.5);
  }
}
.rf-bar-left {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  min-width: 0;
  flex: 1;
}
.rf-bar-right {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  margin-left: auto;
}

/* dropdown trigger — dashed underline, key:value layout */
.rf-select {
  position: relative;
}
.rf-select-trigger {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  background: transparent;
  border: none;
  padding: 3px 16px 3px 0;
  cursor: pointer;
  font-family: var(--font-body);
}
.rf-select-trigger:hover {
  border-bottom-color: var(--accent);
}
.rf-select-trigger .rf-key {
  font-size: 9px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.rf-select-trigger .rf-val {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 15px;
  color: var(--ink);
}
.rf-select-trigger.dirty .rf-val {
  color: var(--accent-deep);
}
.rf-caret {
  position: absolute;
  right: 2px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--ink-faint);
  pointer-events: none;
}

/* dropdown menu */
.rf-menu {
  position: absolute;
  /* Issue 4 (polish v1.15.x): dock is now fixed to the viewport bottom, so the
     menu opens upward (above the trigger) instead of off-screen below. */
  bottom: calc(100% + 6px);
  top: auto;
  left: 0;
  z-index: 20;
  min-width: 150px;
  background: var(--paper-soft);
  border: 1px solid var(--rule);
  box-shadow: var(--shadow-card);
  padding: 5px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  animation: rfPop 160ms cubic-bezier(.2,.7,.3,1) both;
}
@keyframes rfPop {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}
.rf-menu-item {
  appearance: none;
  text-align: left;
  background: transparent;
  border: none;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 15px;
  color: var(--ink-soft);
  padding: 6px 10px;
  cursor: pointer;
  transition: background 140ms, color 140ms;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}
.rf-menu-item:hover {
  background: var(--paper-warm);
  color: var(--ink);
}
.rf-menu-item.on {
  color: var(--accent-deep);
}
.rf-menu-item .rf-tick {
  font-size: 12px;
  opacity: 0;
}
.rf-menu-item.on .rf-tick {
  opacity: 1;
}

.registry-empty-message {
  text-align: center;
  color: var(--ink-faint);
  font-family: var(--font-display);
  font-style: italic;
  padding: 48px 0;
  font-size: 15px;
}

.empty-state {
  text-align: center;
  padding: 80px 40px;
  max-width: 480px;
  margin: 0 auto;
}
.empty-state h2 {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 32px;
  color: var(--ink);
  margin: 0 0 12px;
}
.empty-state p {
  color: var(--ink-mute);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 17px;
  line-height: 1.6;
  margin: 0 0 28px;
}

/* ============================================================
   PROFILE SCREEN
   ============================================================ */
.profile {
  max-width: var(--page-w-contained);
  margin: 0 auto;
  padding: 32px 48px 96px;
  width: 100%;
}

/* two-column body */
.profile-body {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 56px;
  align-items: start;
}

/* ---------- sidebar ---------- */
.sidebar {
  /* Sidebar scrolls in-flow with the main profile content so the whole
     profile reads as one unified column — no sticky pin, no offset. */
  display: flex; flex-direction: column;
  gap: 24px;
}
.portrait-image {
  /* Force the portrait to the 3:4 frame and crop the photo (a tall/wide image used
     to stretch the frame to its own ratio because height:100% resolved to auto). */
  width: 100%; height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  display: block;
  filter: contrast(0.97) saturate(0.9);
}
.sidebar-name .it { font-style: italic; }
/* char profile: trim the 24px .sidebar flex-gap above the Share/Report/Message
   footer. -14px lands the footer's 11px top padding exactly one stat-row rhythm
   (11px) below the last quickstat, so the gap above the buttons matches the
   spacing between stat rows (I-011). */
.sidebar .dir-card-foot { margin-top: -14px; }
.quickstat .v {
  font-family: var(--font-display);
  font-size: 16px;
  font-style: italic;
  color: var(--ink);
  text-align: right;
}
.theme-picker .label { margin-bottom: 12px; display: block; }
.swatch-row { display: flex; gap: 9px; flex-wrap: wrap; margin: 15px 0; }
.swatch:hover { transform: scale(1.08); }
.swatch-label {
  margin-top: 10px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-soft);
}

/* gallery thumbs */
.gallery {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.gallery-thumb {
  width: 100px;
  height: 100px;
  background: var(--paper-warm);
  border: 1px solid var(--rule-soft);
  padding: 4px;
  position: relative;
  flex-shrink: 0;
}
.gallery-thumb img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(0.55) contrast(0.97);
}
.gallery-thumb.empty {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  color: var(--ink-faint);
  text-transform: uppercase;
}

/* ============================================================
   MAIN CONTENT
   ============================================================ */
.section-title-block {
  display: flex; align-items: baseline; gap: 8px;
  margin-bottom: 28px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--rule);
}
.section-title .it { font-style: italic; }
.section-title-en {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 17px;
  color: var(--ink-mute);
}
/* shallows grid */
.short-answer-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 14px;
  margin-bottom: 64px;
}
.field-card.span-3 { grid-column: span 3; }
.field-card.span-4 { grid-column: span 4; }
.field-card.span-6 { grid-column: span 6; }
.field-card.span-12 { grid-column: span 12; }
.field-card-value {
  font-family: var(--font-display);
  font-size: 19px;
  line-height: 1.3;
  font-weight: 500;
  color: var(--ink);
  word-break: break-word;
}
.field-card-value.korean {
  font-family: var(--font-korean);
  font-weight: 400;
}
.field-card-value.empty {
  font-style: italic;
  color: var(--ink-faint);
  font-weight: 400;
}

/* section divider — one element, skinned per theme via CSS */
.section-divider {
  display: flex; align-items: center; gap: 16px;
  margin: 24px 0 40px;
  color: var(--accent);
  opacity: 0.7;
}
.section-divider svg { flex-shrink: 0; }
.section-divider .line {
  flex: 1; height: 1px;
  background: linear-gradient(to right, transparent, var(--accent-soft) 30%, var(--accent-soft) 70%, transparent);
}

/* depths */
.long-answer-section { margin-top: 56px; }
.long-answer { display: flex; flex-direction: column; gap: 48px; }
/* Dividers inside flex-gap containers participate in the gap rhythm; zero their
   own vertical margin so the spacing stays consistent on both sides. */
.long-answer > .section-divider,
.biography-doc > .section-divider { margin: 0; }
.depth-block {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 40px;
  align-items: start;
}
.depth-head {
  position: sticky;
  top: 92px;
}
.depth-head-num .waves {
  display: inline-flex; gap: 2px;
  color: var(--accent);
}
.depth-head-title .it { font-style: italic; }
.depth-body p { margin: 0 0 1em; }
.depth-body p:last-child { margin-bottom: 0; }

/* personality bars */
.long-answer > .traits-block { margin-top: 0; }
.traits-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 22px;
}
.traits-title {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 22px;
  font-weight: 500;
  color: var(--ink);
  margin: 0;
}
/* connections */
/* connections — collapsible drawer cards */
.connections {
  margin-top: 48px;
}
.connections-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  align-items: start;
  gap: 12px 18px;
}

.connection-summary {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  outline: none;
}
.connection-portrait-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: contrast(0.97) saturate(0.9);
}
.connection-portrait-fallback {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 22px;
  color: var(--ink-mute);
  line-height: 1;
}

.connection-summary-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.connection-summary .connection-name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 18px;
  color: var(--ink);
  line-height: 1.15;
  letter-spacing: -0.005em;
}
.connection-summary .connection-descriptor {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-mute);
  line-height: 1.3;
  letter-spacing: 0.01em;
}

.connection-chevron {
  flex-shrink: 0;
  width: 20px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 16px;
  line-height: 1;
  color: var(--ink-faint);
  transition: color 200ms ease;
  user-select: none;
}
.connection-card.open .connection-chevron { color: var(--accent-deep); }

/* drawer animation — grid-template-rows 0fr → 1fr animates to content height */
.connection-drawer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 320ms cubic-bezier(.2,.7,.3,1);
}
.connection-card.open .connection-drawer {
  grid-template-rows: 1fr;
}
.connection-drawer-inner {
  overflow: hidden;
  min-height: 0;
}

.connection-notes {
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-soft);
}
.connection-notes p { margin: 0 0 0.85em; }
.connection-notes p:last-child { margin-bottom: 0; }
.connection-notes.empty {
  font-family: var(--font-display);
  font-style: italic;
  color: var(--ink-faint);
  font-size: 14.5px;
}
.connection-edit-grid {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.connection-edit-label {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-mute);
  display: block;
  margin-top: 10px;
}
.connection-edit-label:first-child { margin-top: 0; }
.connection-notes-input { min-height: 90px; }

.connection-summary .remove-btn { flex-shrink: 0; }
.modal h3 {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 28px;
  font-weight: 500;
  margin: 0 0 12px;
  color: var(--ink);
}
.modal p {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 17px;
  color: var(--ink-soft);
  line-height: 1.55;
  margin: 0 0 28px;
}
.modal-actions {
  display: flex; gap: 12px; justify-content: flex-end;
}

/* ---------- 2E-3: Login modal polish ----------
   LoginModal now carries email + password .field-input fields. These
   rules only tighten spacing/rhythm inside the modal so the form reads
   as considered and calm — they reuse the shared .modal / .field-input
   / .btn language rather than introducing a new modal aesthetic. */
.modal .field-input + .field-input { margin-top: 12px; }
.modal .field-input {
  display: flex;
  align-items: center;
  padding: 11px 13px;
}
.modal .modal-actions {
  margin-top: 26px;
  padding-top: 20px;
  border-top: 1px dashed var(--rule-soft);
}
/* full-width, centred primary/secondary action pair for the login form */
.modal .modal-actions .btn { justify-content: center; }
.modal .modal-actions .btn-accent { letter-spacing: 0.16em; }
/* error sits quietly between the fields and the actions */
.modal .lock-error {
  margin: 6px 0 -6px;
  padding: 6px 0 0;
  font-size: 13px;
}

/* ---------- 14.2 (I-153): SignInForm inline-style port ----------
   `.modal-signin` is a size modifier on the modal — double-classed so
   it beats the base `.modal { max-width: 440px }` regardless of source
   order. Scoped overrides under it tighten the action row and add a
   10px gap between sibling .sync-fields. `.modal-helper-text` is the
   small lead-in <p> below the modal h3 (font-family + italic still
   inherit from the more specific .modal p rule above; only size,
   color, and the negative top margin are overridden here). */
.modal.modal-signin { max-width: 420px; }
.modal .modal-helper-text {
  font-size: 13px;
  color: var(--ink-mute);
  margin-top: -4px;
}
.modal-signin .sync-field + .sync-field { margin-top: 10px; }
.modal-signin .modal-actions { margin-top: 18px; }

/* ---------- D4 (Phase 27.4): auth modal tab strip ----------
   Two-tab strip (Sign In / Register) inside .modal-signin.
   Sits below the h3, above the form body. Reuses .btn-link colours;
   active tab gains a 2px accent underline matching .tab.active::after. */
.modal-tabs {
  display: flex;
  gap: 20px;
  margin: 0 0 22px;
  border-bottom-width: 1px;
  border-bottom-style: solid;
  border-bottom-color: var(--rule-soft);
  padding-bottom: 0;
}
.modal-tab {
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: 15px;
  padding: 0 0 10px;
  color: var(--ink-soft);
  position: relative;
  transition: color 160ms ease;
}
.modal-tab:hover { color: var(--ink); }
.modal-tab.active {
  color: var(--ink);
}
.modal-tab.active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--accent);
}

/* not-found */
.notfound {
  text-align: center;
  padding: 120px 40px;
  max-width: 520px;
  margin: 0 auto;
}
.notfound h2 {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 44px;
  font-weight: 500;
  margin: 0 0 16px;
}
.notfound p {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 18px;
  color: var(--ink-mute);
  line-height: 1.6;
  margin: 0 0 32px;
}

.sync-field .label { margin-bottom: 2px; }

/* ============================================================
   ANIMATIONS
   ============================================================ */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalIn { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes toastIn { from { opacity: 0; transform: translate(-50%, 10px); } to { opacity: 1; transform: translate(-50%, 0); } }
@keyframes cardFadeIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes stagger { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.stagger { animation: stagger 700ms cubic-bezier(.2,.7,.3,1) both; }
.stagger-1 { animation-delay: 60ms; }
.stagger-2 { animation-delay: 140ms; }
.stagger-3 { animation-delay: 220ms; }
.stagger-4 { animation-delay: 300ms; }
.stagger-5 { animation-delay: 380ms; }
.stagger-6 { animation-delay: 460ms; }

/* page transition */
.page-enter {
  animation: pageEnter 500ms cubic-bezier(.2,.7,.3,1);
}
@keyframes pageEnter {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* responsive */
@media (max-width: 1024px) {
  .profile-body { grid-template-columns: 1fr; gap: 40px; }
  .sidebar { position: static; }
  .depth-block { grid-template-columns: 1fr; gap: 16px; }
  .depth-head { position: static; }
  .connections-list { grid-template-columns: 1fr; }
  .short-answer-grid { grid-template-columns: repeat(6, 1fr); }
  .field-card, .field-card.span-3, .field-card.span-4 { grid-column: span 3; }
  .field-card.span-6, .field-card.span-12 { grid-column: span 6; }
}
@media (max-width: 640px) {
  .app-topbar, .dashboard, .profile, .rfd-dock { padding-left: 14px; padding-right: 14px; }
  .section-title { font-size: 30px; }
  .short-answer-grid { grid-template-columns: repeat(2, 1fr); }
  .field-card, .field-card.span-3, .field-card.span-4, .field-card.span-6, .field-card.span-12 { grid-column: span 2; }
  /* Mobile topbar: collapse to brand + bell + menu. Top-level nav lives in the
     drawer (it carries every route); the widget cluster + feature nav are hidden
     so they can't overflow and force horizontal scroll (which also pushed
     right-aligned controls like the edit-mode Save button off-screen). */
  .topbar-nav { display: none !important; }
  .topbar-widgets-cluster { display: none !important; }
  /* Profile photo: the sidebar goes full-width on phones, which blew the 3:4
     portrait up to ~screen width. Cap + center it. */
  .portrait-frame { max-width: 190px; margin-left: auto; margin-right: auto; }
  /* Edit-mode secondary bar: allow wrap so Save/Delete can't be pushed off-screen. */
  .pf-secondary { flex-wrap: wrap; }
  /* Guard against content-driven horizontal overflow in single-column profile
     blocks — grid/flex children default to min-width:auto and won't shrink below
     their content (this is what makes sliders/cards spill past the viewport). */
  .ship-slider, .field-card, .connection-card, .depth-block > * { min-width: 0; }
}
/* Phase 18 (tiny-phone ≤400): two field-card columns get cramped; go single. */
@media (max-width: 400px) {
  .short-answer-grid { grid-template-columns: 1fr; }
  .field-card,
  .field-card.span-3, .field-card.span-4,
  .field-card.span-6, .field-card.span-12 { grid-column: span 1; }
}

/* ---- tab-level Save / Edit action group (flush right of tabs) ---- */
.tab-bar-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  margin-right: 0;
  flex: 0 0 auto;
}
.tab-action-btn {
  /* Visually recessive: small square, icon-only, fades in on hover.
     Sits flush with the tab strip without competing for attention. */
  padding: 0;
  width: 26px;
  height: 26px;
  justify-content: center;
  gap: 0;
  font-size: 11px;
  opacity: 0.38;
  border-color: transparent;
  background: transparent;
  transition: opacity 180ms ease, color 180ms ease, background 180ms ease, border-color 180ms ease;
}
.tab-action-btn i,
.tab-action-btn svg { font-size: 12px; width: 12px; height: 12px; }
.tab-action-btn:hover { opacity: 0.82; border-color: var(--rule-soft); background: var(--paper-warm); }
/* Narrow viewports — base rules already give us an icon-only square,
   nothing else to do here. */
@media (max-width: 720px) {
  .tab-bar-actions { margin-left: auto; margin-right: 0; }
}

/* (Band A S6.2 / I-363: dead .save-pill* removed — superseded by .pf-pill in S3.3.) */

/* ============================================================
   PROFILE HEADER STACK (redesign §A) — tab bar + edit-only
   secondary action bar as one sticky, opaque header. Supersedes
   the PROFILE use of .tab-bar-actions / .save-pill; .tab-action-btn
   stays (guidebook/plotting/timeline still use it). I-342, I-343.
   ============================================================ */
.pf-headerstack {
  position: relative;
  z-index: 30;
  background: var(--paper);
  margin-top: -8px;
  margin-bottom: 24px; /* restores the gap the old inner .tab-bar margin-bottom:32px gave; on the wrapper so it holds in all 3 states */
}
/* Profile tabs are NOT sticky (per request) — the header stack scrolls with the
   page. (.pf-headerstack keeps position:relative from above.) */
.pf-headerstack .tab-bar {
  position: static;
  margin: 0;
  padding-top: 14px;
}

/* icon-only action group — replaces the outlined .tab-bar-actions box */
.pf-actions {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: auto;
  flex: 0 0 auto;
}
.pf-iconbtn {
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  border: none;
  background: transparent;
  color: var(--ink-mute);
  border-radius: 8px;
  cursor: pointer;
  transition: color 150ms ease, background 150ms ease;
  position: relative;
}
.pf-iconbtn i { font-size: 17px; line-height: 1; }
.pf-iconbtn:hover { color: var(--ink); background: color-mix(in oklab, var(--ink) 8%, transparent); }
.pf-iconbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* active "done" affordance — the pencil's editing state, in place */
.pf-iconbtn.is-done {
  color: var(--accent-deep);
  background: color-mix(in oklab, var(--accent) 16%, transparent);
}

/* edit-only secondary bar — full-width row directly below the tab bar */
.pf-secondary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 0 12px;
  border-top: 1px solid var(--rule-soft);
  background: var(--paper);
  animation: pfSecondaryIn 240ms cubic-bezier(.2,.7,.3,1);
}
@keyframes pfSecondaryIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
.pf-spacer { flex: 1; }

/* status pill — dirty vs saved (text still carries saving/error) */
.pf-pill {
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.06em;
  padding: 4px 11px;
  border-radius: var(--radius-field);
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid transparent;
}
.pf-pill::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}
.pf-pill-dirty {
  color: var(--danger, #b9606e);
  background: color-mix(in oklab, var(--danger, #b9606e) 12%, transparent);
  border-color: color-mix(in oklab, var(--danger, #b9606e) 28%, transparent);
}
.pf-pill-saved {
  color: var(--ink-mute);
  background: color-mix(in oklab, var(--ink) 6%, transparent);
}
.pf-pill-saved::before { background: var(--ok, #5f9e78); }

/* secondary-bar actions — text+icon. Save primary, Delete cautious */
.pf-act {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: all 160ms ease;
}
.pf-act i { font-size: 13px; }
.pf-act:hover { border-color: var(--ink-soft); color: var(--ink); }
/* Save: the primary action — FILLED with the theme accent (same look as the admin
   panel's active-theme button), so it's obviously where to click. Per-theme accent
   via tokens; Bubble swaps in its candy gradient. */
.pf-act-primary {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
  color: var(--paper-soft);
}
.pf-act-primary:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper-soft);
}
.pf-act-danger { color: var(--danger, #b9606e); border-color: color-mix(in oklab, var(--danger, #b9606e) 32%, var(--rule)); }
.pf-act-danger:hover { color: #fff; background: var(--danger-deep, #8a3a44); border-color: var(--danger-deep, #8a3a44); }
.pf-act-approve { color: var(--ok, #5f9e78); border-color: color-mix(in oklab, var(--ok, #5f9e78) 40%, var(--rule)); }
.pf-act-approve:hover { color: #fff; background: var(--ok, #5f9e78); border-color: var(--ok, #5f9e78); }
.pf-act-disabled { cursor: default; opacity: 0.65; pointer-events: none; }
.pf-denial-banner {
  padding: 9px 14px;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.45;
  border-bottom: 1px solid var(--warn-border);
  background: var(--warn-wash);
  color: var(--warn);
  width: 100%;
  box-sizing: border-box;
}

/* ============================================================
   NETWORK TAB
   ============================================================ */
.network-tab { animation: pageEnter 360ms cubic-bezier(.2,.7,.3,1); }

.network-canvas-wrap {
  margin: 16px auto 24px;
  display: flex;
  justify-content: center;
}
.network-canvas {
  position: relative;
  margin: 0 auto;
}
.network-lines {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 0;
}

.network-center,
.network-node {
  position: absolute;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 50px;
}
.network-center-frame img,
.network-node-frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  filter: contrast(0.97) saturate(0.9);
}
.network-line-icon svg { display: block; }

.network-center-label,
.network-node-name {
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--ink);
  text-align: center;
  line-height: 1.2;
  width: 130px;
  margin: 0 -15px;
  word-break: break-word;
}
.network-center-label { font-style: italic; }
.network-node-name { font-style: italic; }
.network-node-name .muted { color: var(--ink-faint); }
.network-node-link { color: inherit; text-decoration: none; cursor: pointer; }
button.network-node-link { background: none; border: none; padding: 0; font: inherit; }
.network-node-link:hover .network-node-frame {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
}

.network-node-editor {
  position: absolute;
  top: 88px;
  left: 50%;
  transform: translateX(-50%);
  width: 240px;
  background: var(--paper-soft);
  border: 1px solid var(--rule);
  padding: 12px 14px;
  box-shadow: var(--shadow-card);
  z-index: 4;
  display: flex; flex-direction: column; gap: 4px;
  animation: stagger 200ms ease both;
}
/* The family-tree "Add a family member" form REUSES .network-node-editor, but unlike
   the canvas node-popover it's an INLINE panel inside the family-tree editor. Without
   this reset it inherits position:absolute; top:88px and pins itself to the TOP OF THE
   PAGE whenever the family-tree section is editable (owner bug 2026-06-17). */
.network-node-editor.family-tree-add-panel {
  position: static;
  top: auto;
  left: auto;
  transform: none;
  width: 100%;
  max-width: 360px;
  animation: none;
}
.network-node-editor .label {
  font-size: 9.5px;
  letter-spacing: 0.18em;
  color: var(--ink-mute);
}
.network-node-editor-actions {
  display: flex; justify-content: space-between;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--rule-soft);
}

.network-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  margin: 4px auto 18px;
  padding: 0 16px;
  max-width: 60ch;
}

/* ---- Network cap banners (soft / hard) ----
   Render directly above the add button. Warm amber for soft cap, muted
   red/accent-deep for hard cap. Consistent rounded corners and small icon. */
.network-add-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}
.network-cap-banner {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 3px;
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.35;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  max-width: 100%;
}
/* Neutral amber caution callout — first consumer of --warn (Stage 10 CW block).
   Intentionally NOT theme-accented; amber reads as advisory on all themes. */
.warn-callout {
  display: inline-flex; align-items: flex-start; gap: 8px;
  padding: 10px 14px; border-radius: var(--radius-card, 6px);
  font-family: var(--font-body); font-size: 13px; line-height: 1.5;
  border: 1px solid var(--warn-border); background: var(--warn-wash);
  color: var(--warn); width: 100%; box-sizing: border-box;
}
/* ---- Family Tree — structural layout (theme-neutral) ----------------------
   Appearance lives in theme.css (body.theme-bubble .family-tree-*).
   Three generation rows stacked vertically; connector lines between them.
   Node cards reuse .network-node-frame / .network-node-name / .network-node-editor. */

.family-tree-section {
  margin-top: 40px;
}

.family-tree-rows {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  margin: 20px auto;
  max-width: 680px;
}

.family-tree-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}

.family-tree-connector {
  width: 2px;
  height: 28px;
  background: var(--rule);
  margin: 0 auto;
  opacity: 0.55;
  flex-shrink: 0;
}

.family-tree-row-inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20px;
  padding: 6px 8px;
  width: 100%;
}

.family-tree-row-empty {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12px;
  color: var(--ink-faint);
  padding: 8px 0;
  text-align: center;
  width: 100%;
}

.family-tree-node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.family-tree-node-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 68px;
  cursor: default;
}

.family-tree-node-card--link,
.family-tree-node-card--edit {
  cursor: pointer;
}

.family-tree-node-card--link:hover .network-node-frame,
.family-tree-node-card--edit:hover .network-node-frame {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
}

.family-tree-node-frame {
  /* Inherits size/shape from .network-node-frame; border color set inline */
  border-color: var(--accent);
}

.family-tree-node-name {
  /* Inherits font/style from .network-node-name; narrower width for tree layout */
  width: 80px;
  font-size: 12px;
  margin: 0;
}

.family-tree-node-label {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-mute);
  text-align: center;
  line-height: 1.2;
  margin-top: 2px;
}

.family-tree-node-editor {
  /* Inherits positioning & sizing from .network-node-editor */
  position: absolute;
  top: 110px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
}

.family-tree-editor-wrap {
  margin: 20px auto 0;
  max-width: 280px;
}

.family-tree-add-panel {
  /* Inherits card/border from .network-node-editor */
}

.family-tree-cap-notice {
  font-family: var(--font-body);
  font-size: 12px;
  color: var(--ink-mute);
  padding: 6px 0;
}

.warn-callout strong { font-weight: 600; color: var(--warn); }
/* Full-mode two-line layout: icon + bold label on line 1, CW text on line 2. */
.warn-callout__body { display: flex; flex-direction: column; gap: 2px; }
.warn-callout__text { display: block; }
/* Compact chip variant — inline next to titles; detail text on hover via title attr. */
.warn-callout--compact {
  display: inline-flex; align-items: center; gap: 4px;
  width: auto; padding: 3px 8px; font-size: 11px; line-height: 1.4;
  cursor: default;
}
.warn-callout--compact strong { font-size: 11px; }

/* CW symbol badge — round white circle with pink warning icon; hover reveals CW text via title attr.
   Reusable across timeline, threads list, etc. Place inline next to a label or character name. */
.cw-symbol {
  display: inline-flex; align-items: center; justify-content: center;
  width: 23px; height: 23px; border-radius: 50%; padding: 2px;
  background: #fff; border: 1.5px solid #f0c0cc;
  color: #d45c7a; flex-shrink: 0;
  cursor: default; vertical-align: middle;
}

/* Retired from per-theme accent tints (island/academia/eclipse) to one neutral
   amber rule shared with .warn-callout. */
.network-cap-banner--hard { background: var(--warn-wash); color: var(--warn); border-color: var(--warn-border); }
.add-btn.is-disabled,
.add-btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
.network-empty p {
  margin: 0;
  font-family: var(--font-body);
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
  line-height: 1.6;
}

.network-foot {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--rule-soft);
  gap: 16px;
  flex-wrap: wrap;
}
.network-legend {
  display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center;
}
.legend-empty {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-faint);
}

.network-legend-full {
  margin-top: 14px;
  font-family: var(--font-body);
  font-size: 12px;
  color: var(--ink-mute);
}
.network-legend-full summary::before {
  content: "+ ";
  color: var(--accent);
}
.network-legend-full[open] summary::before { content: "− "; }
.legend-blocks {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px 22px;
  padding: 14px 0 6px;
}
.legend-block {
  display: flex; flex-direction: column; gap: 8px;
}
.legend-block-types {
  display: flex; flex-direction: column; gap: 5px;
}
.legend-type {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  color: inherit;
}
.legend-type svg { flex-shrink: 0; }

/* ============================================================
   SHIPPING TAB
   ============================================================ */
.shipping-tab { animation: pageEnter 360ms cubic-bezier(.2,.7,.3,1); }

.ship-list { display: flex; flex-direction: column; gap: 36px; }

.ship-header {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 28px;
  align-items: center;
  margin: 26px 0;
}
.ship-portrait {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.ship-portrait-frame img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: contrast(0.97) saturate(0.9);
}
.ship-edit-urls { display: flex; flex-direction: column; gap: 14px; }
.ship-edit-urls .label {
  display: block;
  margin-bottom: 4px;
}

/* Status as a caption beneath the relationship glyph */
.ship-link-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
  min-width: 0;
}
.ship-status-caption {
  text-align: center;
  max-width: 240px;
  font-size: 16px;
  line-height: 1.35;
}
input.ship-status-caption {
  width: 220px;
}

/* Relationship-type picker (between portraits) */
.ship-rel-display {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.ship-rel-picker {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 6px;
  max-width: 260px;
}
.ship-rel-btn.active:focus-visible,
.ship-rel-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--paper-soft), 0 0 0 3px var(--accent);
}

.ship-quote-block { margin-top: 30px; margin-bottom: 26px; }
.ship-quote {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 19px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0 auto;
  padding: 6px 22px;
  text-align: center;
  position: relative;
  max-width: 64ch;
}
.ship-quote-block textarea.prose-input {
  text-align: center;
  padding-left: 22px;
  padding-right: 22px;
}

.ship-sliders {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 22px 40px;
}
.traits-sliders {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 22px 40px;
}
.ship-slider {
  display: flex; flex-direction: column; gap: 8px;
  position: relative;
}
.ship-slider-head {
  display: flex; justify-content: space-between; align-items: baseline;
}
.ship-slider-controls { display: flex; align-items: center; gap: 12px; } /* Band A S6.1 (I-273) */
.v-empty { color: var(--ink-faint); } /* Band A S6.1 — shared muted "—" placeholder (was repeated inline) */
.ship-slider-label {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
  color: var(--ink);
  letter-spacing: 0.01em;
  text-transform: lowercase;
}
/* sidebar quickstat icon */
.quickstat .k .icon {
  display: inline-flex;
  align-items: center;
  margin-right: 7px;
  color: var(--ink-mute);
  vertical-align: middle;
  opacity: 0.85;
}
.quickstat-sub .k {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 12px;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--ink-faint);
}

/* ============================================================
   PLOTTING TAB
   ============================================================ */
.plotting-tab { animation: pageEnter 360ms cubic-bezier(.2,.7,.3,1); }

.plot-subhead {
  display: flex; align-items: baseline; gap: 16px;
  margin: 8px 0 18px;
}
.plot-subtitle .it { font-style: italic; }
.plot-subnote {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-faint);
}

/* timeline */
.timeline {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex; flex-direction: column;
  gap: 0;
}
.timeline-entry {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 20px;
  padding: 10px 0 16px;
  position: relative;
  cursor: default;
  transition: transform 200ms ease, background 200ms ease;
}
.timeline-entry[draggable="true"] { cursor: grab; }
.timeline-entry[draggable="true"]:active { cursor: grabbing; }
.timeline-entry.dragging { opacity: 0.4; }


.timeline-rail {
  display: flex; flex-direction: column; align-items: center;
  position: relative;
  padding-top: 6px;
}
.timeline-body { padding-top: 0; }
.timeline-meta {
  display: flex; align-items: baseline; gap: 14px;
  flex-wrap: wrap;
}
.timeline-desc {
  font-family: var(--font-body);
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--ink-soft);
  margin: 6px 0 0;
  max-width: 64ch;
}
.timeline-edit { display: flex; flex-direction: column; gap: 10px; }
.timeline-edit-head {
  display: grid;
  grid-template-columns: 130px 1fr 32px;
  gap: 10px;
  align-items: center;
}

/* ideas grid */
.ideas-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 14px;
}
.idea-card-text {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0;
}
.idea-card-title-input { width: 100%; font-weight: 600; margin-bottom: 6px; }
.idea-card-title { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.idea-card-remove {
  position: absolute;
  top: 6px; right: 8px;
  background: none;
  border: none;
  color: var(--ink-faint);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  opacity: 0;
  transition: opacity 180ms ease, color 180ms ease;
}
.idea-card:hover .idea-card-remove { opacity: 1; }
.idea-card-remove:hover { color: var(--danger-deep); }

/* loose-idea tags */
.idea-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px dashed var(--rule-soft);
}
.idea-tags-edit {
  border-top-style: dashed;
}

/* ============================================================
   DEVELOPMENT TAB
   ============================================================ */
.development-tab { animation: pageEnter 360ms cubic-bezier(.2,.7,.3,1); }

.dev-stack {
  display: flex; flex-direction: column;
  gap: 28px;
}
/* Guidebook + Faceclaims (both use .guidebook-stack): half the inter-block gap. */
.dev-stack.guidebook-stack { gap: 7px; }
.dev-block[draggable="true"] { cursor: grab; }
.dev-block[draggable="true"]:active { cursor: grabbing; }

.dev-block-controls {
  display: flex; gap: 4px;
  justify-content: flex-end;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--rule-soft);
}
.dev-ctrl:disabled { opacity: 0.3; cursor: default; }

/* ---------- Add-block picker ---------- */
.dev-add-area {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--rule-soft);
}
.dev-add-picker-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 14px;
}
.dev-add-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
}

/* ---------- Images (triple square) ---------- */
.images-block { }
.images-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}
.images-slot { display: flex; flex-direction: column; gap: 8px; }
.images-square {
  width: 100%;
  aspect-ratio: 1 / 1;
  background: var(--paper-warm);
  border: 1px solid var(--rule-soft);
  overflow: hidden;
  position: relative;
}
.images-square img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  filter: contrast(0.97) saturate(0.92);
}

/* ---------- Banner (full-width decorative) ---------- */
.banner-block { }
.banner-image {
  width: 100%;
  aspect-ratio: 3 / 1;
  background: var(--paper-warm);
  border: 1px solid var(--rule-soft);
  overflow: hidden;
  position: relative;
}
.banner-image img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  filter: contrast(0.97) saturate(0.92);
}
.placeholder-dim {
  font-size: 10px;
  color: var(--ink-faint);
  letter-spacing: 0.22em;
}

/* =====================================================================
   styles.css — REPLACE existing rules at lines 2990–3016
   (the old .quote-block / .quote-display / .quote-input rules) with
   everything below. The new section covers L1 (quote styles), L2
   (Instagram), and L3 (Tinder) view-mode visuals.

   Everything uses shared theme tokens (--ink, --accent, --rule, --paper-*,
   font vars) which both Island and Academia re-map, so a single set of
   rules works for both themes. Academia-specific overrides (deeper
   shadows, lighter hatch fills) are appended at the bottom of each
   section.
   ===================================================================== */


/* ---------- Quote (pull quote, four visual styles) — L1 ----------
   Per-style classes control font, size, alignment, decoration, and color.
   Both Island (light paper) and Academia (dark) themes pick up the right
   ink/accent automatically via the shared --ink / --accent / --ink-faint
   variables, which each theme re-maps to its own palette.

   Layout note: .quote-block is full-width column; .quote-display-wrap is
   the per-style container that owns alignment/padding (so the textarea in
   edit mode and the blockquote in view mode share one geometry per style). */
.quote-block {
  display: block;
  width: 100%;
}
.quote-display-wrap {
  position: relative;
  width: 100%;
}
.quote-display {
  margin: 0;
  font-weight: 400;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.quote-input {
  width: 100%;
  background: transparent;
  border: 1px dashed var(--rule-soft);
  resize: vertical;
}

/* === CLASSIC ===========================================================
   Large italic display serif, left-aligned, left accent rule, oversized
   muted opening-quote glyph hanging in the gutter. Generous breathing room. */
.quote-style-classic .quote-display-wrap {
  padding: 18px 24px 18px 44px;
  border-left: 2px solid var(--accent);
  text-align: left;
}
.quote-style-classic .quote-display-wrap::before {
  content: "\201C";
  position: absolute;
  left: 14px;
  top: -6px;
  font-family: var(--font-display);
  font-size: 64px;
  line-height: 1;
  color: var(--accent);
  opacity: 0.35;
  pointer-events: none;
  font-style: italic;
}
.quote-style-classic .quote-display,
.quote-style-classic .quote-input {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 26px;
  line-height: 1.4;
  text-align: left;
  color: var(--ink);
  letter-spacing: 0;
}
.quote-style-classic .quote-input {
  padding: 4px 0;
}

/* === BOLD ==============================================================
   Heavy tracked uppercase sans, centered, accent-colored, no quote mark.
   Reads like a chapter epigraph or a headline. */
.quote-style-bold .quote-display-wrap {
  padding: 14px 12px;
  text-align: center;
}
.quote-style-bold .quote-display,
.quote-style-bold .quote-input {
  font-family: var(--font-body);
  font-weight: 700;
  font-style: normal;
  text-transform: uppercase;
  font-size: 22px;
  line-height: 1.25;
  letter-spacing: 0.18em;
  text-align: center;
  color: var(--accent);
}
.quote-style-bold .quote-input {
  padding: 4px 0;
}

/* === EDITORIAL =========================================================
   Smaller, tighter, mono-flavored, left-aligned, muted. Magazine
   pull-quote register — the kind set in the margin of a column. */
.quote-style-editorial .quote-display-wrap {
  padding: 8px 0 8px 14px;
  border-left: 1px solid var(--rule);
  text-align: left;
}
.quote-style-editorial .quote-display,
.quote-style-editorial .quote-input {
  font-family: var(--font-mono);
  font-style: normal;
  font-weight: 400;
  font-size: 13px;
  line-height: 1.55;
  letter-spacing: 0.02em;
  text-align: left;
  color: var(--ink-soft);
  opacity: 0.85;
}
.quote-style-editorial .quote-input {
  padding: 4px 0;
}

/* === FADED =============================================================
   Watermark: very large display italic at low opacity, full-width centered,
   no rules, no quotation marks. Sits behind the eye, not in front of it. */
.quote-style-faded .quote-display-wrap {
  padding: 18px 8px;
  text-align: center;
}
.quote-style-faded .quote-display,
.quote-style-faded .quote-input {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 48px;
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-align: center;
  color: var(--ink);
  opacity: 0.18;
}
.quote-style-faded .quote-input {
  padding: 4px 0;
  opacity: 0.35; /* slightly more visible while typing so the user can see what they're writing */
}

/* ---------- Quote style picker (mini previews) ----------
   Each button is a small live-rendered preview of its style. The active
   button gets a stronger border + accent ring; hover lifts subtly. */
.quote-style-picker {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-top: 14px;
}
.quote-style-btn.quote-style-preview {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  padding: 6px 8px;
  background: var(--paper-warm);
  border: 1px solid var(--rule-soft);
  cursor: pointer;
  overflow: hidden;
  transition: border-color 160ms ease, background 160ms ease, transform 160ms ease;
}
.quote-style-btn.quote-style-preview:hover {
  border-color: color-mix(in oklab, var(--accent) 50%, var(--rule));
  transform: translateY(-1px);
}
.quote-style-btn-active.quote-style-preview {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
  background: var(--accent-wash);
}

/* Preview glyph — the per-style class above already sets font/weight/etc.
   These rules just clamp the size to fit the 56px button. */
.quote-preview-glyph {
  display: inline-block;
  line-height: 1;
  pointer-events: none;
}
.quote-style-preview.quote-style-classic .quote-preview-glyph {
  font-size: 36px;
  color: var(--accent);
  opacity: 0.55;
}
.quote-style-preview.quote-style-bold .quote-preview-glyph {
  font-size: 12px;
  letter-spacing: 0.22em;
  color: var(--accent);
}
.quote-style-preview.quote-style-editorial .quote-preview-glyph {
  font-size: 11px;
  letter-spacing: 0.04em;
  opacity: 0.75;
}
.quote-style-preview.quote-style-faded .quote-preview-glyph {
  font-size: 28px;
  opacity: 0.28;
}


/* ---------- Instagram block (view mode) — L2 ----------
   Aesthetic impression of a profile, not literal IG UI. Floating images
   (no card box around the grid), drop-shadow per cell. Uses shared
   theme tokens so both Island (light paper) and Academia (dark) read
   correctly without per-theme overrides. */
.ig-block {
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 100%;
}

/* --- header: avatar + username + bio --- */
.ig-header {
  display: flex;
  align-items: center;
  gap: 16px;
}
.ig-avatar {
  flex: 0 0 60px;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--paper-warm);
  border: 1px solid var(--rule-soft);
  display: flex;
  align-items: center;
  justify-content: center;
}
.ig-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.ig-avatar-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  color: var(--ink-faint);
}
.ig-id {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.ig-username {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.01em;
  color: var(--ink);
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.ig-bio {
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-mute);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* --- stats row --- */
.ig-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: end;
  padding: 8px 0 12px;
  border-bottom: 1px solid var(--rule-soft);
}
.ig-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.ig-stat-num {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1.1;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.005em;
  overflow-wrap: anywhere;
}
.ig-stat-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* --- 3×2 image grid, floating (no surrounding card) --- */
.ig-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  padding: 2px;
}
.ig-cell img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* --- photo: compact phone-card portrait (4:5). At the new ~340px
   card width this is far shorter in absolute height than the old
   full-width 16:9 banner. --- */
.tinder-photo {
  width: 100%;
  aspect-ratio: 4 / 5;
  background: var(--paper-soft);
  overflow: hidden;
  position: relative;
}
.tinder-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* --- content below photo --- */
.tinder-body {
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* name + distance share a row; distance is a small tag aligned baseline */
.tinder-id-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 10px;
}
.tinder-name {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.12;
  color: var(--ink);
  letter-spacing: -0.005em;
  overflow-wrap: anywhere;
}
.tinder-distance {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding: 3px 8px;
  border: 1px solid var(--rule-soft);
  border-radius: 999px;
  white-space: nowrap;
  max-width: 50%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* bio — clamped to 3 lines so a long bio doesn't blow out the card */
.tinder-bio {
  margin: 0;
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-soft);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  white-space: pre-wrap;
}

/* interest pills — accent at low opacity, soft pill shape */
.tinder-interests {
  display: flex;
  flex-wrap: wrap;
  gap: 5px 6px;
  margin-top: 2px;
}

/* ── Task 5: Instagram dev block — edit-mode internal structure ──────────────
   Padding, vertical rhythm, stats-row flex, and mono URL inputs consistent
   with other dev blocks. Applied to the .ig-block wrapper when it appears
   inside a .dev-block (edit context). */
.dev-block .ig-block {
  gap: 14px;
}
.dev-block .ig-header {
  gap: 12px;
}
.dev-block .ig-stats {
  display: flex;
  flex-direction: row;
  gap: 0;
  justify-content: space-between;
  padding: 10px 0 12px;
  border-bottom: 1px solid var(--rule-soft);
}
.dev-block .ig-stat {
  flex: 1;
  align-items: center;
}
/* Grid images section consistent spacing */
.dev-block .ig-grid {
  gap: 8px;
  padding: 4px 0 0;
}
/* ── Task 5: Tinder dev block — edit-mode internal structure ─────────────────
   Padding, vertical rhythm for Age/Distance row, clear interest list
   separation, and mono photo URL input. */
/* The Tinder edit form renders as .text-block (see TinderBlock in
   development.js), NOT .tinder-block, so the original edit-mode override
   here never applied to edit mode — it only ever hit the VIEW-mode card
   that renders inside .dev-block, where `max-width: none` blew the 4/5
   photo up to full container width (the "400 × 500" runaway). The
   override is removed so the view card keeps its designed 340px
   phone-card constraint, centering, and 14px radius from the base rule. */
/* Age/Distance row — flex row with equal column spacing */
.dev-block .tinder-id-row {
  display: flex;
  flex-direction: row;
  gap: 12px;
  align-items: center;
  flex-wrap: nowrap;
}
/* Edit-mode body padding rhythm */
.dev-block .tinder-body {
  gap: 12px;
  padding: 16px 18px 18px;
}
/* Interest list items clear separation */
.dev-block .tinder-interests {
  gap: 6px 8px;
  padding-top: 4px;
  border-top: 1px dashed var(--rule-soft);
  margin-top: 4px;
}
/* ---------- 3A-2: Tracker tab ----------
   A clean session / thread log. Each entry is a card in the .ship-card
   visual family (paper-soft fill, hairline border, soft shadow, accent
   top-edge) laid out as: thumbnail left, title + description + link
   stacked right. Edit-mode inputs reuse shared field classes so only
   the read layout needs bespoke rules here. */
.tracker-section { animation: pageEnter 360ms cubic-bezier(.2,.7,.3,1); }

.tracker-list {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.tracker-thumb-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: contrast(0.97) saturate(0.9);
}

/* content column */
.tracker-entry-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.tracker-entry-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.tracker-entry-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 21px;
  line-height: 1.2;
  color: var(--ink);
  letter-spacing: -0.005em;
  word-break: break-word;
}
.tracker-entry-link:hover { opacity: 1; color: var(--accent); }
.tracker-entry-desc {
  margin: 0;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink-soft);
  white-space: pre-wrap;
}

/* edit-mode reorder / remove controls — quiet inline row */
.tracker-entry-controls {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

/* ---------- Playlist (clean tracklist) ---------- */
.playlist-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}
.playlist-header .dev-block-heading { flex: 1; min-width: 0; margin-bottom: 0; }
.playlist-block {
  width: 100%;
  display: block;
}
    .playlist-tracks {
  list-style: none;
  margin: 0; padding: 0;
  width: 100%;
  min-width: 0;
  display: flex; flex-direction: column;
  gap: 0;
}
.playlist-track:last-child { border-bottom: none; }
.playlist-track-main {
  display: flex;
  align-items: baseline;
  gap: 14px;
  min-width: 0;
}
.playlist-track-num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--ink-mute);
  flex: 0 0 auto;
}
.playlist-track-body {
  display: flex; align-items: baseline; gap: 8px;
  flex-wrap: wrap;
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.3;
}
.playlist-track-title {
  color: var(--ink);
  font-style: italic;
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.3;
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
.playlist-track-artist {
  color: var(--ink-mute);
  font-style: italic;
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 1.3;
  min-width: 0;
  overflow-wrap: anywhere;
}
.playlist-track-artist-cell {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
.playlist-dot { color: var(--ink-faint); }
.playlist-track-artist-input { font-size: 14px; color: var(--ink-soft); }
.playlist-track-remove {
  background: transparent;
  border: none;
  color: var(--ink-faint);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  margin-left: auto;
}
.playlist-track-remove:hover { color: var(--danger); }
/* .playlist-link now styled via .tracker-entry-link — no text-link styles needed. */
.playlist-url-row { margin-top: 14px; }

/* ---------- Likes / Dislikes (two columns) ---------- */
.likes-block { }
.likes-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 32px;
}
@media (max-width: 560px) {
  .likes-grid { grid-template-columns: minmax(0, 1fr); gap: 20px; }
}
.likes-col { display: flex; flex-direction: column; min-width: 0; }
.likes-list {
  list-style: none;
  margin: 0; padding: 0;
  display: flex; flex-direction: column;
  gap: 2px;
}
.likes-item {
  display: flex; align-items: baseline; gap: 8px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
  color: var(--ink);
  padding: 4px 0;
}
.likes-item.empty { color: var(--ink-faint); font-style: italic; }
.likes-bullet { color: var(--ink-faint); flex-shrink: 0; }
.likes-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.likes-remove {
  background: transparent;
  border: none;
  color: var(--ink-faint);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  transition: color 160ms ease;
  flex-shrink: 0;
}
.likes-remove:hover { color: var(--danger); }
.likes-add-row {
  display: flex; gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--rule-soft);
}
.likes-add-input::placeholder { color: var(--ink-faint); }

/* ---------- Freeform Text ---------- */
.text-block-body {
  font-family: var(--font-body);
  font-size: 15.5px;
  line-height: 1.75;
  color: var(--ink-soft);
  padding: 0;
  width: 100%;
  overflow-wrap: anywhere;
}
.text-block-body p { margin: 0 0 1em; }
.text-block-body p:last-child { margin-bottom: 0; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1024px) {
  .tab-bar { top: 64px; padding-top: 4px; }
  .ship-header { grid-template-columns: 1fr; gap: 18px; justify-items: center; }
  .ship-link-col { gap: 16px; }
  .ship-edit-urls { grid-template-columns: 1fr; }
  .ship-sliders { grid-template-columns: 1fr; gap: 18px; }
  .traits-sliders { grid-template-columns: 1fr; gap: 18px; }
}
@media (max-width: 640px) {
  .tab-bar { gap: 18px; }
  .tab { font-size: 17px; padding: 12px 2px 14px; }
  .ship-card { padding: 22px 18px; }
  .timeline-entry { grid-template-columns: 44px 1fr; }
  .timeline-edit-head { grid-template-columns: 100px 1fr 32px; }
}
.wordmark-group {
  display: flex;
  align-items: baseline;
  gap: 0;
  cursor: pointer;
  user-select: none;
  outline: none;
}
.wordmark-group:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}
.wordmark-script {
  font-family: var(--font-korean);
  font-style: normal;
  font-weight: 400;
  color: var(--ink-mute);
  font-size: 16px;
  margin-left: 2px;
}
/* Band A S5.4 (I-335): motto mode — petite small-caps body serif, muted-gold, with
   a subtle accent glow. (0,3,0) out-specifies body.theme-eclipse .wordmark-script
   (italic, 0,2,1) and the base rule above (0,1,0). Token-only → resolves per-theme. */
.wordmark-group .wordmark-script.motto {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  font-variant: all-small-caps;
  font-feature-settings: "smcp" 1, "c2sc" 1;
  letter-spacing: 0.14em;
  font-size: 15px;
  color: var(--accent-deep);
  text-shadow: 0 0 10px color-mix(in oklab, var(--accent) 24%, transparent);
  margin-left: 14px;
}
/* 720px wordmark sep/subtitle: also handled in the N1 responsive block above. */

/* N1 (#43): Responsive topbar collapse — three stages.
   900px  — hide nav-item text labels (icons remain).
   720px  — hide widgets cluster (date/weather/music); also wordmark subtitle.
   768px  — hide the full nav icon row.
   560px  — hide topbar date + weather-label (see block below).
   Leave always visible: site title (wordmark) · bell · menu (hamburger). */
@media (max-width: 1700px) {
  /* v1.44: full nav-item text + three widget labels fit down to ~1670px (with
     the 20px topbar padding). Below ~1700 collapse the nav to icons so the
     centered nav never collides with the date/widget cluster. Icons stay
     distinct (address-book, bookmark, calendar, notebook, book-open, chats). */
  .topbar-nav-label { display: none; }
}
@media (max-width: 820px) {
  /* widgets go before the nav icons so the collapse order is
     labels (900) -> widgets (820) -> nav (768) -> title/bell/menu only */
  .topbar-widgets-cluster { display: none; }
  .topbar-date { display: none; }
  .wordmark-sep, .wordmark-subtitle { display: none; }
}
@media (max-width: 768px) {
  .topbar-nav { display: none; }
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  min-width: 0;
  justify-content: flex-start;
}
/* v1.45: single flow row holding every NON-wordmark item (6 nav icons, date,
   widgets, bell) evenly spaced. The wordmark anchors the left edge and the menu
   is pinned at the right edge (.topbar-menu-pinned); this flow fills the span
   between them and distributes its children with equal space around each. */
.topbar-flow {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
  justify-content: flex-start;
  gap: 10px;
}
.topbar-menu-pinned {
  flex: 0 0 auto;
  margin-left: 10px;
}
/* v1.46: ONLY the nav is flex-spaced — it grows to fill the row and spreads its
   feature icons evenly. The date, widgets cluster, and bell sit as a right-aligned
   group (pushed there by the nav's flex:1) with even 15px gaps from .topbar-flow. */
.topbar-nav {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
  justify-content: space-evenly;
}
.topbar-widgets-cluster {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 1;
  min-width: 0;
}
/* v1.44: 3-widget topbar must shrink, not overflow into the centered nav.
   Allow each widget to shrink and ellipsis its label; below 1180px drop the
   widget text (icon-only) before the 820px full-cluster hide. */
.topbar-widgets-cluster .topbar-weather { min-width: 0; flex-shrink: 1; }
.topbar-widgets-cluster .topbar-weather-label {
  white-space: nowrap;
}
.topbar-date { flex-shrink: 0; }
.topbar-music { max-width: 156px; }
.topbar-music .np-meta { min-width: 0; }
.topbar-music .np-title,
.topbar-music .np-artist {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 112px;
}
@media (max-width: 1180px) {
  .topbar-weather-label,
  .topbar-music .np-meta { display: none; }
  /* v1.45 — collapsed widgets go icon-only: weather shows its weather symbol,
     moon shows its phase glyph. Match the bell's box (7px 10px pad, 18px icon)
     so all collapsed controls share one height. The moon temp row is empty, so
     hide it too; the glyph alone remains. */
  .topbar-moon .topbar-weather-temp { display: none; }
  .topbar-widgets-cluster .topbar-weather,
  .topbar-widgets-cluster .topbar-moon {
    padding: 7px 10px;
    line-height: 1;
    gap: 0;
    column-gap: 0;
    min-width: 38px;
    justify-content: center;
  }
  /* Icon bumped to match the bell's 18px so all round controls read the same size */
  .topbar-widgets-cluster .topbar-weather i,
  .topbar-widgets-cluster .topbar-moon i {
    font-size: 18px;
  }
}
.topbar-weather i {
  font-size: 16px;
  color: var(--accent-deep);
  line-height: 1;
}
.topbar-weather-temp {
  color: var(--ink-faint);
  margin-left: 2px;
}
@media (max-width: 560px) {
  .topbar-weather-label { display: none; }
}
/* Moon phase glyph — Weather Icons SVG rendered via CSS mask so it inherits
   currentColor tinting from whatever the widget sets on its container. */
.moon-svg-glyph {
  display: inline-block;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  /* width/height are set inline by MoonGlyph (defaults 15px). */
  flex-shrink: 0;
}
/* In the collapsed (<=1180px) icon-only state the old MoonGlyph SVG was
   constrained by the container; bump the mask span to match the 18px
   font-size used for Phosphor icon peers. */
@media (max-width: 1180px) {
  .topbar-widgets-cluster .topbar-moon .moon-svg-glyph {
    width: 18px !important;
    height: 18px !important;
  }
}

/* v1.45 — moon phase name: never truncate. Render on up to 2 lines (the phase
   names are two words, e.g. "Waxing Crescent"); a single-word name ("New"-less
   set stays 1 line via .is-single-word). Vertically centered against the glyph.
   This overrides the cluster's nowrap/ellipsis/max-width for the moon only.
   Root-cause fix (#7): drop max-width from the label — the Bubble theme's
   1fr grid column already constrains width naturally. Padding-right lives on
   the label so words never touch the container's right edge regardless of how
   narrow the 1fr cell becomes. min-width:0 lets the cell compress without overflow. */
.topbar-moon .topbar-moon-label {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  line-height: 1.12;
  align-self: center;
  -webkit-line-clamp: none;
  padding-right: 10px;
  /* bound the 1fr grid track: inline-grid grows a 1fr column to the label's
     max-content (one line) without a cap, making the widget too wide. Cap it so
     the two-word phase names wrap to 2 lines. box-sizing keeps padding-right
     inside the cap so text never crashes the right edge (#7). */
  max-width: 9ch;
  box-sizing: border-box;
  min-width: 0;
}
.topbar-moon .topbar-moon-label.is-single-word {
  white-space: nowrap;
}
.topbar-hamburger i {
  font-size: 18px;
  line-height: 1;
  display: inline-block;
  vertical-align: middle;
}
.drawer-head {
  display: flex;
  justify-content: flex-end;
  padding: 14px 18px 6px;
}
.drawer-close {
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
  color: var(--ink-mute);
  padding: 4px 10px;
  font-family: var(--font-display);
}
.drawer-close:hover { color: var(--ink); }
.drawer-section:last-child { border-bottom: none; }
.drawer-item {
  width: 100%;
  background: transparent;
  border: none;
  /* #68: hairline divider between entries */
  border-bottom: 1px solid var(--rule-soft);
  /* #69: tighter vertical padding (was 12px) */
  padding: 8px 12px;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 14px;
  /* #70: sans-serif ALL-CAPS, smaller, dark grey — matches icon color */
  font-family: var(--font-body);
  font-style: normal;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mute);
  transition: background 140ms ease, color 140ms ease;
  border-radius: 0;
  text-decoration: none;
}
.drawer-item:last-child {
  border-bottom: none;
}
.drawer-item i {
  font-size: 16px;
  color: var(--ink-mute);
  flex-shrink: 0;
  width: 18px;
  text-align: center;
  line-height: 1;
  transition: color 140ms ease;
}

/* ============================================================
   SIDEBAR — new identity stack + file-stamp
   ============================================================ */
.sidebar-identity {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-bottom: 6px;
}
.sidebar-identity input + label { margin-top: 14px; }
.sidebar-alt-script {
  font-family: var(--font-korean);
  font-size: 17px;
  color: var(--ink-mute);
  margin-top: 2px;
  line-height: 1.2;
}

/* ==========================================================================
   Error boundary + global toast (error.js)
   ========================================================================== */

/* ── Full-screen error boundary overlay ─────────────────────────────────── */

.error-boundary-screen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in oklab, var(--paper) 80%, transparent);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  padding: 24px;
}

.error-boundary-box h2 {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  margin: 0 0 10px;
  color: var(--ink);
}

.error-boundary-box p {
  font-size: 14px;
  color: var(--ink-mute);
  margin: 0 0 20px;
}

.error-boundary-box details {
  margin-bottom: 24px;
}

.error-boundary-box summary {
  font-size: 13px;
  color: var(--ink-faint);
  cursor: pointer;
  user-select: none;
  margin-bottom: 8px;
}

.error-boundary-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 4px;
}

.error-boundary-reload,
.error-boundary-home {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 20px;
  border-radius: 6px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: opacity 0.15s ease;
}

/* Return home is the primary path; Reload is secondary (outlined). */
.error-boundary-reload {
  background: transparent;
  color: var(--accent);
}

.error-boundary-reload:hover,
.error-boundary-home:hover {
  opacity: 0.85;
}

/* ── Global error toast (bottom-right) ───────────────────────────────────── */

.global-error-toast {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 9998;
  max-width: 420px;
  background: #fdf0d5;
  border: 1px solid #e8c97a;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(80, 55, 10, 0.14);
  padding: 12px 16px;
  font-family: var(--font-body);
  font-size: 13px;
  color: #5a3e10;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  animation: toast-slide-in 0.2s ease;
}

@keyframes toast-slide-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0);   }
}

.global-error-toast strong {
  font-weight: 600;
}

.toast-dismiss {
  flex-shrink: 0;
  margin-left: auto;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  color: #8a6020;
  padding: 0 0 0 8px;
  font-family: var(--font-body);
  transition: color 0.1s ease;
}

.toast-dismiss:hover {
  color: #5a3e10;
}

/* Toast keeps amber palette in Academia — it's an alert, not a surface */

/* ============================================================
   ADMIN PANEL  [S8 — adm2-shell]
   Structural shell now uses .adm2-shell (3-zone layout).
   .admin-page / .admin-tab-body / .admin-subtabs were the old
   shell — removed when the adm2 re-shell landed.
   ============================================================ */
/* Saved flash — fixed viewport toast; visible regardless of scroll position */
.admin-saved-flash {
  position: fixed;
  top: 12px;
  right: 16px;
  z-index: 200;
  font-size: 12px;
  color: var(--accent);
  font-style: italic;
  pointer-events: none;
  animation: adminSavedFade 2.2s ease forwards;
}
@keyframes adminSavedFade {
  0%   { opacity: 1; }
  70%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Top tab strip — inherits .tab-bar / .tab. The modifier sits flush
   beneath the Topbar (64px) now that admin-page-head is removed. */
.admin-tab-bar {
  position: static;
  margin-top: 0;
  margin-bottom: 28px;
  padding-left: 48px;
  padding-right: 48px;
}
.admin-save-row {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  padding-top: 22px;
  margin-top: 8px;
}

/* Thread-categories explicit-save footer (D-043) */
.adm-tcat-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
}
.adm-tcat-footer-note {
  font-size: 12px;
  font-style: italic;
  color: var(--ink-faint);
}
.adm-tcat-footer-actions {
  display: flex;
  gap: 12px;
}

.admin-toggle input[type="checkbox"] {
  width: auto;
  flex: 0 0 auto;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.admin-toggle span { line-height: 1; }

@media (max-width: 640px) {
  .tab-bar.admin-tab-bar { padding-left: 20px; padding-right: 20px; }
}
@media (max-width: 640px) {
  .dev-stack.guidebook-stack { gap: 7px; }
}

/* ============================================================
   L6 — SHORT ANSWER SECTION BREAK
   Shallows/Cover field grid splits into three visual buckets so
   the reader can scan compact identity facts, an academic
   sub-record, and full-width prose-style fields as three
   distinct movements.

   profile.js partitions resolved fields into three .short-answer-grid
   sub-grids (compact / academic / full-width). The break between
   compact and full-width sits *after Languages Spoken* by design
   (Languages is the last span-3 entry in SHALLOWS_FIELDS); the
   academic sub-block is rendered with its own subhead in
   Academia mode.

   Also includes the .traits-flourish ornament that fills the
   white space to the right of the Personality header.
   ============================================================ */

/* When grids are chained (compact → academic → fullWidth), the
   base .short-answer-grid { margin-bottom: 64px } would compound. The
   .short-answer-group modifier neutralises that — the trailing margin
   to Depths now belongs to the *last* group only. */
.short-answer-grid.short-answer-group { margin-bottom: 0; }
.short-answer-grid.short-answer-group--full { margin-bottom: 64px; }

/* The break between compact and full-width groups. Pure CSS so it
   stays out of the React render tree's responsibility. Reads as a
   wide, quiet hairline with a centred mark — the "after Languages
   Spoken" beat that signals a register shift from facts to prose. */
.short-answer-break {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 36px 0 28px;
  position: relative;
}

/* Centered subhead for the academic block — eyebrow type flanked
   by hairline rules. Echoes section-marker in tone (small caps,
   wide letterspacing, accent-deep) but reads as a sub-heading
   rather than a section label. */
.short-answer-subhead {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 18px;
}
.short-answer-subhead-label .ph,
.short-answer-subhead-label [class^="ph-"],
.short-answer-subhead-label [class*=" ph-"] {
  color: var(--accent);
  opacity: 0.85;
}

/* ---- Personality header flourish (right-hand white space filler) ----
   Sits opposite the .traits-title. A hairline tide line that fades
   in from the title side, anchored on the right by a small filled
   mark. Theme-neutral by default; the Academia override below swaps
   it for a typographic ornament. */
.traits-flourish {
  flex: 1;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-left: 24px;
  min-width: 0;
}

/* Narrow viewports — flourish hides so the title doesn't crowd it. */
@media (max-width: 640px) {
  .traits-flourish { display: none; }
  .short-answer-academic { padding: 18px 14px 4px; }
  .short-answer-break { margin: 28px 0 22px; }
}

/* ============================================================
   M2 — ADMIN VISUAL STANDARDIZATION
   Tightens the admin panel into a coherent visual system in
   both themes. Five goals:
     1. Consistent section spacing + heading hierarchy
     2. Tab bar background anchored to page floor (B6-BugA/B fix
        for the Island side; Academia was already pinned in the
        existing .tab-bar.admin-tab-bar override above)
     3. Polished form elements (inputs, selects, toggles)
     4. Danger zone — clearly destructive, visually quarantined
     5. Debug tab reads as a developer tool, not a primary feature

   Most admin form styling was already inline in admin.js. The
   refactor in admin.js moves those inline styles into the classes
   below so theme variants can target them cleanly.
   ============================================================ */

/* Shared danger token. Falls back to a desaturated oxblood that
   reads correctly on both paper (--paper) and dark (--theme-bg-base)
   backgrounds. Defined on :root so it's available in both themes
   and inline-style fallbacks (var(--danger, …)) still resolve. */
:root {
  --danger:        #a04545;
  --danger-soft:   #c87575;
  --danger-muted:  #b67c7c;
  --danger-wash:   rgba(160, 69, 69, 0.06);
  --danger-deep:   #7a3030;

  /* Advisory / caution state — NOT destructive (distinct from --danger).
     First consumer (v1.17.0): the Triggers / Content-Warning callout on the
     user profile page. Like --danger, lives on :root so it reads on both the
     light (--paper) and dark (--theme-bg-base) grounds; a theme may retone it. */
  --warn:          #b07a2e;
  --warn-wash:     color-mix(in oklab, var(--warn) 12%, transparent);
  --warn-border:   color-mix(in oklab, var(--warn) 40%, transparent);

  /* Corner-radius scale — tokenized so every theme rounds by default (these are
     Eclipse's values, promoted to the shared default; a future sharp theme
     overrides them). No consumers yet — selector groups adopt var(--radius-*)
     in a later stage, so introducing these is a zero visual change. */
  --radius-field:  8px;   /* inputs, buttons, pills, selects */
  --radius-image:  11px;  /* portrait / image wells, thumbs, card images */
  --radius-card:   13px;  /* section / field / dev / ship / tracker cards */
  --radius-modal:  16px;  /* modals, roster cards, posts, large surfaces */
}

/* TOKEN CONTRACT — --accent-deep is a CONTRAST-ROLE token, not a lightness one:
   it is the highest-emphasis accent that stays legible as a FOREGROUND against
   the page ground (--paper). On light themes that is a DARKER accent; on dark
   themes a LIGHTER one. Both are correct — do not "fix" the light/dark difference.
   (Full token contract lives in docs/THEME-SPEC.md, authored later this phase.) */

/* adm-tcat — admin thread categories tab swatches */
.adm-tcat-swatch-row { display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0; }
.adm-tcat-swatch {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
  transition: border-color 120ms;
}
.adm-tcat-swatch:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
.adm-tcat-swatch.is-selected { outline: 2px solid var(--ink); outline-offset: 2px; }
/* I-348: custom hex picker swatch — a round <input type=color> matching the tones. */
.adm-tcat-hex {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 2px solid var(--rule);
  border-radius: 50%;
  cursor: pointer;
  flex-shrink: 0;
  background: var(--paper-warm);
  -webkit-appearance: none;
  appearance: none;
  transition: border-color 120ms;
}
.adm-tcat-hex::-webkit-color-swatch-wrapper { padding: 0; }
.adm-tcat-hex::-webkit-color-swatch { border: none; border-radius: 50%; }
.adm-tcat-hex::-moz-color-swatch { border: none; border-radius: 50%; }
.adm-tcat-hex:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
.adm-tcat-hex.is-selected { outline: 2px solid var(--ink); outline-offset: 2px; }

/* ---- Admin list column header utilities (replaces per-element inline styles) ---- */
.adm-col-act  { width: 28px; flex-shrink: 0; }
.adm-col-icon { width: 20px; flex-shrink: 0; }
/* thread-categories table */
.adm-col-tc-name  { flex: 2 1 80px; }
.adm-col-tc-desc  { flex: 3 1 100px; }
.adm-col-tc-color { flex-shrink: 0; min-width: 198px; }
/* short-answer field editor */
.adm-col-fe-label { flex: 2 1 120px; min-width: 110px; }
.adm-col-fe-icon  { flex: 1 1 80px; min-width: 70px; font-family: var(--font-mono); }
.adm-col-fe-size  { flex: 0 0 110px; }
.adm-col-fe-num   { flex: 0 0 64px; }
/* depth / biography section editor */
.adm-col-de-title { flex: 2; min-width: 100px; }
.adm-col-de-desc  { flex: 3; min-width: 120px; }
.adm-col-de-max   { flex: 0 0 64px; }
/* registry character groups */
.adm-col-rg-label { flex: 2; }
.adm-col-rg-id    { flex: 1; }
/* slot editor */
.adm-col-se-label { flex: 1 1 120px; min-width: 100px; }
.adm-col-se-icon  { flex: 1 1 90px; min-width: 80px; font-family: var(--font-mono); }
.adm-col-se-field { flex: 1 1 130px; min-width: 120px; }

@keyframes adminDevPulse {
  0%, 100% { opacity: 0.35; transform: scale(1); }
  50%      { opacity: 0.85; transform: scale(1.25); }
}
.admin-danger-zone-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.admin-danger-zone-icon {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--danger);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  line-height: 1;
  border-radius: 50%;
  user-select: none;
}
.admin-danger-zone-confirm { margin-top: 12px; }
.admin-danger-zone-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
}
.admin-danger-zone-input {
  flex: 1 1 200px;
  min-width: 180px;
  font-family: var(--font-mono);
  letter-spacing: 0.04em;
}
.admin-danger-zone-input:focus-within {
  border-color: var(--danger) !important;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--danger) 18%, transparent) !important;
}
.debug-panel-head.is-open { margin-bottom: 10px; }
.debug-panel-section-label:first-child { margin-top: 2px; }
.debug-panel-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 0;
}

/* =========================================================================
   MERGED FROM styles-additions.css (Session — Timeline)
   styles-additions.css is retired after this session; its rules are folded
   in below verbatim. No rules were dropped: none were exact duplicates of
   rules already present in this file (all gb-* selectors are new).
   ========================================================================= */

/* =========================================================================
   GUIDEBOOK (Session 8) — inline section editor.
   The live guidebook renders .gbx-layout / .gbx-nav / .gbx-content (see
   guidebook.js); the rules below style that surface and its inline
   section-title editors. The dead .guidebook-page / .timeline-page
   profile-mirror wrappers (and gb-sidebar-* rules) were removed in I-363.
   ========================================================================= */

/* ---- Section-title-block inside the guidebook -------------------------- */
/* The profile .section-title rules already apply via the cascade. The two
   inline editors need to look like the elements they replace, not like
   .field-input. */
.gbx-content .gb-section-title-input {
  background: transparent;
  border: none;
  border-bottom: 1px dashed var(--rule);
  padding: 2px 0;
  width: 100%;
  font-family: var(--font-display);
  font-size: 44px;
  font-weight: 400;
  letter-spacing: -0.02em;
  color: var(--ink);
  line-height: 1;
}
.gbx-content .gb-section-title-input:focus {
  outline: none;
  border-bottom-color: var(--accent);
}

.gbx-content .gb-section-subtitle-input {
  background: transparent;
  border: none;
  border-bottom: 1px dashed var(--rule);
  padding: 2px 0;
  flex: 1;
  min-width: 200px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 17px;
  color: var(--ink-mute);
}
.gbx-content .gb-section-subtitle-input:focus {
  outline: none;
  border-bottom-color: var(--accent);
}


/* ---- Edit mode: secondary action bar ----------------------------------- */
.gbx-content .pf-secondary {
  border-top: none;
  border-bottom: 1px solid var(--rule-soft);
  margin-bottom: 20px;
}

/* ---- Edit mode: title/subtitle bottom-aligned -------------------------- */
.gbx-content .section-title-block.is-editing { align-items: flex-end; }


/* ---- Narrow viewports — collapse to a single column the way the profile
   already does. The profile's media queries handle .profile-body, but its
   breakpoint lives elsewhere; mirror it here scoped to the guidebook so
   the sidebar stacks above the main column on narrow screens. */
@media (max-width: 880px) {
  .gbx-content .gb-section-title-input {
    font-size: 32px;
  }
}



/* =========================================================================
   Phase 4 carryover sweep — structural extracts
   Structural declarations split out of body.theme-island rules in
   island.css so every theme (Island, Academia, future) inherits the
   theme-agnostic layout. Visual declarations remain theme-scoped.
   ========================================================================= */
.label {
  font-family: var(--font-body);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
}
.btn {
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  padding: 10px 18px;
  border-radius: 1px;
  border-width: 1px;
  border-style: solid;
  cursor: pointer;
  transition: all 220ms ease;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}
.btn-ghost {
  border-width: 1px;
  border-style: solid;
}
.btn-ghost.danger {
  color: var(--danger, #a04545);
  border-color: color-mix(in oklab, var(--danger, #a04545) 32%, var(--rule));
}
.btn-ghost.danger:hover {
  background: var(--danger-wash, rgba(160,69,69,0.06));
}
.btn-accent {
  border-width: 1px;
  border-style: solid;
}
.btn-icon {
  width: 36px;
  height: 36px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-width: 1px;
  border-style: solid;
  border-radius: 50%;
  cursor: pointer;
  transition: all 220ms ease;
}
input, textarea, select {
  font-family: var(--font-body);
  font-size: 14px;
  border-style: none;
  width: 100%;
}
.field-input {
  border-width: 1px;
  border-style: solid;
  border-radius: 1px;
  padding: 8px 10px;
}
textarea.prose-input {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.7;
  min-height: 140px;
  padding: 14px 16px;
  border-width: 1px;
  border-style: solid;
  border-radius: 1px;
  resize: vertical;
}
/* thin themed scrollbars on scrollable field and panel elements */
.field-input,
.prose-input,
.activity-drawer-body,
.drawer-backdrop {
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
}
.field-input::-webkit-scrollbar,
.prose-input::-webkit-scrollbar,
.activity-drawer-body::-webkit-scrollbar,
.drawer-backdrop::-webkit-scrollbar { width: 6px; }
.field-input::-webkit-scrollbar-track,
.prose-input::-webkit-scrollbar-track,
.activity-drawer-body::-webkit-scrollbar-track,
.drawer-backdrop::-webkit-scrollbar-track { background: transparent; }
.field-input::-webkit-scrollbar-thumb,
.prose-input::-webkit-scrollbar-thumb,
.activity-drawer-body::-webkit-scrollbar-thumb,
.drawer-backdrop::-webkit-scrollbar-thumb {
  background: var(--rule);
  border-radius: 3px;
}
.app-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px 20px;
  border-bottom-width: 1px;
  border-bottom-style: solid;
  position: sticky;
  top: 0;
  z-index: 50;
}
.char-card {
  border-width: 1px;
  border-style: solid;
  cursor: pointer;
  transition: transform 280ms cubic-bezier(.2,.7,.3,1);
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}
.char-card:hover {
  transform: translateY(-4px);
}
.char-card-image {
  aspect-ratio: 3 / 4;
  position: relative;
}
/* The portrait fills the 3:4 box and crops. Absolutely positioned so the in-flow
   image height can't override the container's aspect-ratio (it used to take the
   photo's own ratio when a tall/wide image loaded). */
.char-card-image > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.char-card-image::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.char-card-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-family: var(--font-display);
  font-size: 64px;
}
.char-card-body {
  padding: 20px 22px 22px;
  border-top-width: 1px;
  border-top-style: solid;
}
.char-card-status {
  position: absolute;
  top: 14px;
  right: 14px;
  font-size: 9.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 4px 9px;
  border-width: 1px;
  border-style: solid;
}
.card-action {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border-width: 1px;
  border-style: solid;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.link-copied {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 4;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  border-width: 1px;
  border-style: solid;
  padding: 4px 9px;
  pointer-events: none;
  animation: linkCopiedFade 2000ms ease-in-out forwards;
}
.char-card-new {
  border-width: 1px;
  border-style: dashed;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  /* max(): match taller siblings via grid-stretch, but keep a card-like floor
     (~real card height) when this is the only card on its row. */
  min-height: max(100%, 460px);
  transition: all 280ms ease;
  flex-direction: column;
  gap: 14px;
  padding: 48px 20px;
  text-align: center;
}
.char-card-new .plus {
  font-family: var(--font-display);
  font-size: 56px;
  font-weight: 300;
  line-height: 1;
}
/* At-cap state: non-actionable, dimmed, shows prohibit icon instead of "+". */
.char-card-new--disabled {
  cursor: default;
  opacity: 0.55;
  pointer-events: none;
}
.char-card-new--disabled .prohibit-icon {
  font-size: 36px;
}
.portrait-frame {
  position: relative;
  aspect-ratio: 3 / 4;
  border-width: 1px;
  border-style: solid;
  padding: 8px;
}
.portrait-frame::after {
  content: "";
  position: absolute;
  inset: 8px;
  pointer-events: none;
}
.portrait-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  padding: 20px;
}
.portrait-image-input {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 6px 8px;
  border-width: 1px;
  border-style: solid;
}
.portrait-tape {
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%) rotate(-2deg);
  width: 80px;
  height: 18px;
  border-width: 1px;
  border-style: solid;
}
.sidebar-name {
  font-family: var(--font-display);
  font-size: 36px;
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.01em;
  margin: 0;
}
.sidebar-quote {
  padding: 14px 0;
  margin: 0;
  border-left-style: none;
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.5;
  text-indent: 0;
}
.sidebar-quote::before {
  content: "\201C";
  font-size: 36px;
  font-family: var(--font-display);
  line-height: 0;
  vertical-align: -0.2em;
  margin-right: 2px;
  display: inline;
  position: static;
}
.quickstats {
  display: flex;
  flex-direction: column;
  gap: 0;
  border-top-width: 1px;
  border-top-style: solid;
}
/* Character profile sidebar: tighten the gap below quickstats.
   The .sidebar flex gap is 24px which reads oversized after the stat rows;
   pull back to ~14px net (24px gap − 10px negative margin). */
.sidebar .quickstats { margin-bottom: -10px; }
.quickstat {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 12px;
  padding: 11px 0;
  border-bottom-width: 1px;
  border-bottom-style: solid;
}
.quickstat:last-child { border-bottom: none; }
.quickstat .k {
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.theme-picker {
  padding: 16px 0;
  border-top-width: 1px;
  border-top-style: solid;
}
.swatch {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border-width: 1px;
  border-style: solid;
  cursor: pointer;
  position: relative;
  transition: transform 180ms ease;
}
.swatch.active {
  border-width: 1px;
  border-style: solid;
}
.gallery-input {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  padding: 8px 10px;   /* Band A S2.3 (I-365): match sibling sidebar inputs (was 4px 6px) */
  border-width: 1px;
  border-style: solid;
  width: 100%;
}
.gallery-edit-block { display: flex; flex-direction: column; gap: 6px; margin-top: 20px; }
/* ---- Sidebar edit-mode inputs (I-033 / I-081 / I-118) ----
   Replaces per-element inline styles so themes can restyle these fields.
   Mirrors the I-051 sidebar-input pattern (timeline.js, .sidebar-year-input):
   the border palette rides the theme-routed var(--rule) token, defined per
   theme in island.css / academia.css — so theme switching updates it. */
.sidebar-url-input { font-family: var(--font-mono); font-size: 11px; }
.sidebar-user-link { text-decoration: none; color: inherit; cursor: pointer; }
.contact-handle-input {
  background: transparent;
  border-bottom: 1px dashed var(--rule);
  padding: 2px 0;
}
.sidebar-name-input,
.contact-handle-input {
  font-family: inherit;
  font-size: inherit;
  font-style: normal;   /* Band A S3.4 (I-366 E6): name/handle inputs non-italic */
}
.sidebar-alt-script-input { font-family: var(--font-korean); font-size: 14px; }
.sidebar .label { margin-bottom: 3px; display: block; }
.contact-handle-input {
  flex: 1;
  min-width: 0;
  text-align: right;
  color: inherit;
}
/* keeps .quickstat .v palette (color/text-align/font-style); overrides only the box */
.quickstat .v.quickstat-value-input {
  background: transparent;
  border-width: 1px;
  border-style: solid;
  border-radius: var(--radius-field);
  padding: 4px 8px;
  font-family: inherit;
  font-size: inherit;
  align-self: center;
  transition: border-color 180ms ease, background 180ms ease, box-shadow 180ms ease;
}
/* profile-specific: own name (not the timeline-scoped .sidebar-quote-input,
   which is inert under textarea.prose-input). Compound beats textarea.prose-input. */
.prose-input.profile-quote-input {
  min-height: 80px;
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.5;
  font-style: italic;
}
.section-title {
  font-family: var(--font-display);
  font-size: 44px;
  line-height: 1;
  font-weight: 400;
  letter-spacing: -0.02em;
  margin: 0;
}
.field-card {
  grid-column: span 4;
  padding: 16px 18px;
  border-width: 1px;
  border-style: solid;
  position: relative;
  transition: transform 220ms ease, grid-column 280ms ease;
}
.field-card:hover {
  transform: translateY(-2px);
}
.field-card-label {
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 7px;
}
.field-card-label .icon {
  display: inline-flex;
}
.depth-head-num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.depth-head-num .waves span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}
.depth-head-title {
  font-family: var(--font-display);
  font-size: 28px;
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.01em;
  margin: 0 0 8px;
}
.depth-head-desc {
  font-family: var(--font-display);
  font-size: 16px;   /* Band A S3.4 (I-366 E5): was 14px */
  line-height: 1.45;
  margin: 0;
}
.depth-body {
  font-family: var(--font-body);
  font-size: 15.5px;
  line-height: 1.75;
  max-width: 64ch;
}
.depth-body.empty {
  font-family: var(--font-display);
  font-size: 17px;
  border-left-width: 2px;
  border-left-style: solid;
  padding-left: 18px;
}
.traits-block {
  margin-top: 28px;
  padding: 28px 30px;
  border-width: 1px;
  border-style: solid;
  border-top-width: 2px;
  border-top-style: solid;
}
.connection-card {
  border-width: 1px;
  border-style: solid;
  border-radius: 1px;
}
.connection-portrait {
  flex-shrink: 0;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border-width: 2px;
  border-style: solid;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.connection-card.open .connection-drawer-inner {
  padding: 14px 16px 18px;
  border-top-width: 1px;
  border-top-style: dashed;
}
.connection-name-input {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 18px;
  border-style: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 2px 0;
  letter-spacing: -0.005em;
}
.connection-descriptor-input {
  font-family: var(--font-display);
  font-size: 13px;
  border-style: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 2px 0;
  letter-spacing: 0.01em;
}
.connection-url-input {
  font-family: var(--font-mono);
  font-size: 11px;
  border-width: 1px;
  border-style: solid;
  border-radius: 1px;
  padding: 7px 10px;
}
.remove-btn {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border-width: 1px;
  border-style: solid;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: all 180ms ease;
  font-size: 14px;
  line-height: 1;
}
.reorder-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  width: 22px;
  height: 22px;
  border-width: 1px;
  border-style: solid;
  border-radius: 1px;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  transition: color 180ms ease, border-color 180ms ease, background 180ms ease;
}
.add-btn {
  margin-top: 16px;
  font-family: var(--font-display);
  font-size: 14px;
  border-width: 1px;
  border-style: dashed;
  padding: 8px 14px;
  cursor: pointer;
  transition: all 200ms ease;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: fadeIn 200ms ease;
}
.modal {
  border-width: 1px;
  border-style: solid;
  padding: 40px 44px;
  max-width: 440px;
  width: calc(100% - 40px);
  animation: modalIn 280ms cubic-bezier(.2,.7,.3,1);
}
/* (Legacy centered `.toast` rule removed — it was hijacking the position of the
   ToastHost cards, forcing them to screen-center. The live toast layout is the
   `.toast` rule further down + the lower-right `.toast-host` container.) */
.scroll-top-btn {
  position: fixed;
  right: 28px;
  bottom: 64px;  /* clears the ~45px fixed registry filter dock so they don't overlap */
  z-index: 55;
  width: 40px;
  height: 40px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border-width: 1px;
  border-style: solid;
  cursor: pointer;
  transition: transform 220ms ease;
  animation: fadeIn 240ms ease;
}
.scroll-top-btn:hover {
  transform: translateY(-2px);
}
.dark-mode-toggle {
  position: fixed;
  left: 28px;
  bottom: 64px;
  z-index: 55;
  width: 40px;
  height: 40px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  transition: transform 220ms ease;
}
.dark-mode-toggle:hover {
  transform: scale(1.08);
}
.tab-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 21px;
  align-items: center;
  border-bottom-width: 1px;
  border-bottom-style: solid;
  margin-bottom: 32px;
  margin-top: -8px;
  position: sticky;
  top: 64px;
  padding: 8px 0 0;
  z-index: 30;
}
.tab {
  border-style: none;
  padding: 14px 2px 16px;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  position: relative;
  letter-spacing: 0.005em;
  white-space: nowrap;
}
.tab.active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
}
.network-center-frame, .network-node-frame {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  overflow: hidden;
  border-width: 2px;
  border-style: solid;
  position: relative;
  flex-shrink: 0;
  transition: transform 220ms ease;
}
.network-center-frame {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  border-width: 2px;
}
.network-node-frame {
  cursor: pointer;
}
.network-node-frame:hover {
  transform: translateY(-2px);
}
.network-photo-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 20px;
}
.network-line-icon {
  position: absolute;
  pointer-events: none;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border-width: 1.5px;
  border-style: solid;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  pointer-events: none;
  animation: stagger 600ms cubic-bezier(.2,.7,.3,1) both;
}
.network-node-name-input {
  text-align: center;
  border-style: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  font-family: inherit;
  font-size: inherit;
  padding: 2px 4px;
  width: 130px;
}
.legend-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  letter-spacing: 0.06em;
  padding: 3px 0;
}
.legend-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
}
.network-legend-full summary {
  cursor: pointer;
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 6px 0;
  list-style: none;
}
.legend-block-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 6px;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
}
.legend-block-name {
  font-family: var(--font-body);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
}
.ship-card {
  border-width: 1px;
  border-style: solid;
  border-top-width: 2px;
  border-top-style: solid;
  padding: 32px 36px;
  position: relative;
}
.ship-portrait-frame {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  overflow: hidden;
  border-width: 2px;
  border-style: solid;
}
.ship-photo-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 38px;
}
.ship-portrait-name {
  font-family: var(--font-display);
  font-size: 18px;
  text-align: center;
  max-width: 180px;
  line-height: 1.25;
}
.ship-partner-link {
  text-decoration: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
}
.ship-status {
  font-family: var(--font-display);
  font-size: 18px;
}
.ship-status-input {
  font-family: var(--font-display);
  font-size: 18px;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 4px 0;
  width: 100%;
}
.ship-rel-btn {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-width: 1px;
  border-style: solid;
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  transition: transform 180ms ease;
}
.ship-rel-btn.active {
  transform: scale(1.12);
}
.ship-slider-pct {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
  line-height: 1;
}
.ship-slider-label-input {
  font-family: var(--font-display);
  font-size: 16px;
  border-style: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 2px 0;
  letter-spacing: 0.01em;
  text-transform: lowercase;
  flex: 1;
  min-width: 0;
  margin-right: 12px;
}
.ship-slider-pct-input {
  font-family: var(--font-mono);
  font-size: 13px;
  text-align: center;
  border-width: 1px;
  border-style: solid;
  border-radius: var(--radius-field);
  padding: 4px 6px;
  width: 54px;
}
.ship-slider-track {
  height: 1px;
  position: relative;
}
.ship-slider-fill {
  position: absolute;
  left: 0;
  top: -1px;
  height: 3px;
  border-radius: 0;
  transition: width 900ms cubic-bezier(.2,.7,.3,1);
}
.ship-slider-tick {
  position: absolute;
  top: -3px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  transform: translateX(-50%);
  transition: left 900ms cubic-bezier(.2,.7,.3,1);
}
.quickstat-sub {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: baseline;
  gap: 12px;
  padding: 2px 0 11px;
  border-bottom-width: 1px;
  border-bottom-style: solid;
  margin-top: -4px;
}
.quickstat-sub input {
  text-align: right;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 2px 0;
  font-family: var(--font-display);
  font-size: 14px;
}
.ship-card-foot {
  margin-top: 22px;
  padding-top: 16px;
  border-top-width: 1px;
  border-top-style: dashed;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.plot-subtitle {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 500;
  letter-spacing: -0.005em;
  margin: 0;
}
.timeline-entry.drag-over {
  border-radius: 2px;
}
.timeline-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  position: relative;
  z-index: 1;
}
.timeline-line {
  /* absolute within .timeline-rail so the connector runs flush from the
     bottom of this dot to the top of the NEXT entry's dot — spanning the
     entry's own bottom padding (16px) + next entry's top padding (10px) +
     next rail's padding-top (6px). Was a flex item with margin-top, which
     left visible gaps at both ends. */
  position: absolute;
  top: 16px;            /* rail padding-top (6) + dot height (10) */
  bottom: -32px;        /* 16 + 10 + 6 — lands on the next dot's top edge */
  left: 50%;
  transform: translateX(-50%);
  width: 1px;
  background: var(--rule);
}
.timeline-date {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.timeline-title {
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 500;
  letter-spacing: -0.005em;
  margin: 0;
}
.timeline-date-input {
  font-family: var(--font-mono);
  font-size: 12px;
  border-width: 1px;
  border-style: solid;
  padding: 7px 9px;
  letter-spacing: 0.08em;
}
.timeline-title-input {
  font-family: var(--font-display);
  font-size: 18px;
  border-width: 1px;
  border-style: solid;
  padding: 7px 9px;
}
.idea-card {
  border-width: 1px;
  border-style: solid;
  padding: 16px 18px;
  position: relative;
  transition: transform 200ms ease;
}
.idea-card:hover {
  transform: translateY(-2px);
}
.idea-card[draggable="true"] { cursor: grab; }
.idea-card[draggable="true"]:active { cursor: grabbing; }
.idea-card.dragging { opacity: 0.4; }
.idea-card.drag-over { outline: 2px solid var(--accent); outline-offset: 2px; }
.connection-card[draggable="true"] { cursor: grab; }
.connection-card[draggable="true"]:active { cursor: grabbing; }
.connection-card.dragging { opacity: 0.4; }
.connection-card.drag-over { outline: 2px solid var(--accent); outline-offset: 2px; }
.idea-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-width: 1px;
  border-style: solid;
  border-radius: 999px;
  padding: 3px 9px 3px 10px;
  line-height: 1.4;
}
.idea-tag-remove {
  border-style: none;
  padding: 0 0 0 2px;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.idea-tag-input {
  flex: 1;
  min-width: 100px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border-width: 1px;
  border-style: dashed;
  border-radius: 999px;
  padding: 4px 10px;
}
.idea-tag-input::placeholder {
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.dev-block {
  border-width: 1px;
  border-style: solid;
  padding: 22px 26px;
  position: relative;
  transition: transform 200ms ease;
}
.dev-ctrl {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-width: 1px;
  border-style: solid;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.dev-block-heading {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  margin-bottom: 14px;
  overflow-wrap: anywhere;
}
.text-block-heading-input {
  font-family: var(--font-body);
  font-size: 13px;
  border-style: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 4px 0;
  width: 100%;
  margin-bottom: 14px;
}
/* #5 — heading inputs placed inside .dev-block-heading inherit its
   letter-spacing / transform / weight so edit and display look identical. */
.dev-block-heading .text-block-heading-input,
.dev-block-heading .gb-heading-in-display,
.dev-block-heading .dev-heading-in-display {
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  font-weight: inherit;
  margin-bottom: 0;
  width: 100%;
}
.dev-add-picker {
  border-width: 1px;
  border-style: solid;
  padding: 18px 20px;
}
.dev-add-option {
  text-align: left;
  border-width: 1px;
  border-style: solid;
  padding: 12px 14px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.dev-add-option-label {
  font-family: var(--font-display);
  font-size: 17px;
}
.dev-add-option-hint {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.placeholder-fill {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  padding: 16px;
}
.ig-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  transition: transform 200ms ease;
  /* 10% size reduction: each cell is 90% of its grid track */
  transform: scale(0.9);
  transform-origin: center;
}
.ig-cell:hover {
  transform: scale(0.9) translateY(-2px);
}
.tinder-block {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 340px;
  margin: 28px auto;
  border-radius: 14px;
  overflow: hidden;
  border-width: 1px;
  border-style: solid;
}
.tinder-photo-empty {
  position: absolute;
  inset: 0;
}
.tinder-pill {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.03em;
  padding: 4px 9px;
  border-radius: 999px;
  border-width: 1px;
  border-style: solid;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tracker-entry {
  display: grid;
  grid-template-columns: 92px 1fr;
  gap: 22px;
  align-items: start;
  border-width: 1px;
  border-style: solid;
  border-left-width: 2px;
  border-left-style: solid;
  padding: 20px 24px;
}
a.tracker-entry { text-decoration: none; color: inherit; }
.tracker-thumb {
  width: 92px;
  height: 92px;
  border-width: 1px;
  border-style: solid;
  overflow: hidden;
  position: relative;
}
/* Type icon: a round accent-filled circle with a white glyph (replaces the photo). */
.tracker-thumb:has(.tracker-thumb-icon) { border: none; border-radius: 50%; }
.tracker-thumb-icon {
  width: 100%; height: 100%;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent);
  color: #fff;
}
.tracker-thumb-icon i { font-size: 26px; line-height: 1; }
.tracker-thumb-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.tracker-entry-link {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  margin-left: auto;
}
.playlist-track {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: 14px;
  align-items: baseline;
  padding: 8px 0;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  min-width: 0;
}
.playlist-track-input {
  font-family: var(--font-display);
  font-size: 16px;
  border-style: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 2px 0;
  min-width: 60px;
  flex: 1 1 auto;
}
.likes-add-input {
  flex: 1;
  border-style: none;
  border-bottom-width: 1px;
  border-bottom-style: dashed;
  padding: 4px 0;
  font-family: var(--font-display);
  font-size: 15px;
}
.wordmark-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  letter-spacing: -0.01em;
}
.wordmark-sep {
  font-size: 12px;
  margin: 0;
  line-height: 1;
}
.wordmark-subtitle {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 500;
  padding: 0 0 0 15px;
}
.topbar-weather {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
/* Now-playing music widget (a top-bar widget option). Baseline look; themes may
   re-skin via .topbar-music / .np-*. Decorative: no audio, just a faked live state. */
a.topbar-music { text-decoration: none; color: inherit; cursor: pointer; }
.topbar-music {
  gap: 8px;
  text-transform: none;
  letter-spacing: normal;
  max-width: 220px;
}
.np-art {
  flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent-soft, var(--rule)); color: var(--accent-deep, var(--accent));
  font-size: 12px;
}
.np-meta {
  display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0;
  line-height: 1.2;
  transition: opacity 240ms ease, transform 240ms ease;
}
.topbar-music.is-swapping .np-meta { opacity: 0; transform: translateY(3px); }
.np-title {
  font-size: 12px; font-weight: 600; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.np-artist {
  font-size: 10px; font-weight: 500; color: var(--ink-mute);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.np-eq {
  flex: 0 0 auto; display: inline-flex; align-items: flex-end; gap: 2px; height: 13px;
}
.np-eq i {
  width: 2.5px; border-radius: 2px; background: var(--accent);
  animation: npEq 900ms ease-in-out infinite;
}
.np-eq i:nth-child(1) { height: 50%;  animation-delay: -200ms; }
.np-eq i:nth-child(2) { height: 100%; animation-delay: -500ms; }
.np-eq i:nth-child(3) { height: 65%;  animation-delay: -100ms; }
.np-eq i:nth-child(4) { height: 85%;  animation-delay: -350ms; }
@keyframes npEq { 0%, 100% { transform: scaleY(0.35); } 50% { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) {
  .np-eq i { animation: none; }
  .topbar-music.is-swapping .np-meta { opacity: 1; transform: none; }
}
.topbar-date {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
}
/* Date renders inline by default ("Weekday, Month Day"); a theme may stack the two
   parts (Bubble). The comma joins them inline and is hidden when stacked. */
.topbar-date-sub::before { content: ", "; white-space: pre; }
/* Month + day-number gets mono so the digits are tabular and stable */
.topbar-date-sub { font-family: var(--font-mono); }
.topbar-hamburger {
  border-width: 1px;
  border-style: solid;
  padding: 7px 10px;
  cursor: pointer;
  line-height: 1;
}
.topbar-bell {
  border-width: 1px;
  border-style: solid;
  padding: 7px 10px;
  cursor: pointer;
  line-height: 1;
  position: relative;
}
.topbar-bell i {
  font-size: 18px;
  line-height: 1;
  display: inline-block;
  vertical-align: middle;
}
/* Phase 18 (tablet↓ ≤768): ensure primary tap targets meet ~44px on touch. */
@media (max-width: 768px) {
  .topbar-hamburger,
  .topbar-bell {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}
@media (max-width: 480px) {
  .bottombar-login,
  .bottombar-logout,
  .bottombar-actions .dir-foot-btn { min-height: 40px; }
}
/* Theme-scoped .topbar-bell / .topbar-discord box appearance lives in the
   theme files (D-053, I-290): island.css, academia.css, eclipse.css, bubble.css.
   The structural rules above (.topbar-bell box, icon) and the shared dot below
   stay here. */

/* ── Mobile topbar (≤640) — placed AFTER the base topbar rules so it wins the
   cascade (equal specificity → later source wins; tech-debt #7). The earlier
   ≤640 block hides .topbar-nav / .topbar-widgets-cluster (with !important); this
   block owns the layout the base .app-topbar / .topbar-date / .topbar-flow rules
   would otherwise override. Result: wordmark · (bell) · menu, with the bell
   pinned right next to the menu and symmetric 14px gutters. ── */
@media (max-width: 640px) {
  .app-topbar { padding-left: 14px; padding-right: 14px; }
  .topbar-date { display: none; }                 /* phones: drop the date */
  .topbar-flow { justify-content: flex-end; gap: 0; } /* push the bell to the right edge */
  .topbar-menu-pinned { margin-left: 14px; }      /* bell↔menu gap == menu↔edge gutter (14px) */
}
/* Shared dot badge — used by bell (unread notifications) and Messages (unread DMs). */
.topbar-icon-dot {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-deep);
  box-shadow: 0 0 0 2px var(--paper);
  pointer-events: none;
}
/* Top-bar nav icon row */
.topbar-nav-icon {
  display: inline-flex;
  align-items: center;
  border-width: 1px;
  border-style: solid;
  padding: 7px 10px;
  cursor: pointer;
  line-height: 1;
  position: relative;
}
.topbar-nav-icon i {
  font-size: 18px;
  line-height: 1;
  display: inline-block;
  vertical-align: middle;
}
/* word label beside the feature icons — wide-tracked caps micro-label */
.topbar-nav-label {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
}
/* #43: nav-item text labels are now hidden at ≤900px (see N1 block above). */
.topbar-nav-icon.is-active {
  color: var(--accent);
  border-color: var(--accent);
}
/* Feature-nav center group: flat buttons, no pill/circle, active = underline */
.topbar-nav .topbar-nav-icon {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  position: relative;
}
.topbar-nav .topbar-nav-icon.is-active {
  color: var(--ink);
  border-color: transparent;
}
.topbar-nav .topbar-nav-icon.is-active::after {
  content: "";
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: -6px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
}
.drawer-backdrop {
  position: fixed;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  z-index: 90;
}
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 340px;
  max-width: 92vw;
  border-left-width: 1px;
  border-left-style: solid;
  transform: translateX(110%);
  transition: transform 280ms cubic-bezier(.2,.7,.3,1);
  z-index: 100;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
html .drawer-backdrop.open {
  opacity: 1;
  pointer-events: auto;
}
html .drawer.open {
  transform: translateX(0);
}
.drawer-section {
  padding: 8px 14px 18px;
  border-bottom-width: 1px;
  border-bottom-style: solid;
}
.drawer-section-label {
  font-family: var(--font-body);
  font-size: 9px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  padding: 8px 12px;
  margin-bottom: 4px;
  font-weight: 600;
}
/* N2: four unlabelled groups separated by a hairline rule */
.drawer-group {
  padding: 8px 14px;
  border-bottom-width: 1px;
  border-bottom-style: solid;
  border-bottom-color: var(--rule);
}
.drawer-groups > .drawer-group:last-child {
  border-bottom: none;
}
/* Explicit hairline dividers between drawer groups — these survive theme overrides
   that zero out .drawer-group border-bottom (e.g. bubble.css). Low-opacity border
   token keeps them subtle but visible. */
.drawer-hr {
  border: none;
  border-top: 1px solid var(--rule);
  opacity: 0.6;
  margin: 0 14px;
}
.quickstat-select {
  border-width: 1px;
  border-style: solid;
  border-color: var(--rule);
  border-radius: 1px;
  padding: 6px 8px;
  font-family: var(--font-display);
  font-size: 16px;
  cursor: pointer;
}
.error-boundary-box {
  border-width: 1px;
  border-style: solid;
  border-radius: 10px;
  padding: 32px 36px;
  max-width: 540px;
  width: 100%;
  font-family: var(--font-body);
}
.error-boundary-box pre {
  font-family: var(--font-mono);
  font-size: 11px;
  border-width: 1px;
  border-style: solid;
  border-radius: 5px;
  padding: 10px 12px;
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 6px 0 0;
}
.admin-subtab {
  border-width: 1px;
  border-style: solid;
  border-radius: 1px;
  padding: 7px 14px;
  font-family: var(--font-body);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
  cursor: pointer;
  transition: all 180ms ease;
}
.icon-link-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border-width: 1px;
  border-style: solid;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
.admin-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px;
  user-select: none;
}
.short-answer-break::before, .short-answer-break::after {
  content: "";
  flex: 1;
  height: 1px;
}
.short-answer-break-mark {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.short-answer-academic {
  margin: 32px 0 0;

}

.short-answer-subhead-rule {
  flex: 1;
  height: 1px;
}
.short-answer-subhead-label {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  font-weight: 600;
}
.traits-flourish-rule {
  flex: 1;
  height: 1px;
  min-width: 40px;
}
.traits-flourish-mark {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  position: relative;
}
.traits-flourish-heart { display: none; }
.traits-flourish-mark::before, .traits-flourish-mark::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  transform: translateY(-50%);
}
.traits-flourish-mark::before {
  right: 11px;
}
.traits-flourish-mark::after {
  right: 20px;
  width: 3px;
  height: 3px;
}
.tab-bar.admin-tab-bar {
  border-bottom-width: 1px;
  border-bottom-style: solid;
  max-width: 760px;
  margin: 8px auto;
}
.admin-dev-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  margin-bottom: 8px;
}
.admin-dev-eyebrow-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  /* slow pulse so it reads as "live" — this is a live readout panel */
  animation: adminDevPulse 2.4s ease-in-out infinite;
}
.admin-danger-zone {
  margin-top: 32px;
  margin-bottom: 34px;
  padding: 18px 20px 16px;
  border-width: 1px;
  border-style: dashed;
  border-radius: 4px;
  position: relative;
}
.admin-danger-zone-head h3 {
  /* sectionHeading() output inside the danger zone — flush with the icon */
  margin: 0 !important;
}
.admin-danger-zone-label {
  display: block;
  margin-bottom: 6px;
}
.admin-danger-zone-keyword {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  padding: 1px 6px;
  border-radius: 2px;
}
.admin-danger-zone-btn {
  flex-shrink: 0;
  cursor: not-allowed;
  letter-spacing: 0.16em;
}
.admin-danger-zone-btn.is-armed {
  cursor: pointer;
}
.debug-panel {
  margin: 12px 0 16px;
  padding: 12px 14px;
  border-width: 1px;
  border-style: solid;
  border-left-width: 3px;
  border-left-style: solid;
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.55;
}
.debug-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
  user-select: none;
  margin-bottom: 0;
}
.debug-panel-toggle {
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1;
}
.debug-panel-section-label {
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
  margin: 14px 0 5px;
  padding-bottom: 2px;
  border-bottom-width: 1px;
  border-bottom-style: dotted;
}
.debug-panel-key {
  flex-shrink: 0;
}
.debug-panel-val {
  text-align: right;
  word-break: break-all;
}
.debug-panel-row--total {
  margin-top: 6px;
  padding-top: 6px;
  border-top-width: 1px;
  border-top-style: dashed;
  font-weight: 600;
}
.debug-panel-empty {
  padding: 2px 0;
}

/* ============================================================
   ADMIN KIT — adm-* (Stage 17.5.2)
   Structural + layout vocabulary for admin tab restyle stages.
   Theme-neutral; per-theme pieces live in island.css / academia.css.
   ============================================================ */
.adm-section { margin-bottom: 44px; }
.adm-section:last-child { margin-bottom: 0; }
.adm-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px 16px; margin-bottom: 6px; flex-wrap: wrap; }
.adm-section-title { font-family: var(--font-display); font-style: italic; font-size: 22px; font-weight: 500; color: var(--ink); margin: 0; line-height: 1.1; flex: 1 1 auto; min-width: 0; }
.adm-section-note { font-size: 12px; color: var(--ink-faint); font-style: italic; margin: 16px 0 0; line-height: 1.55; max-width: 64ch; }
.adm-eyebrow { font-family: var(--font-body); font-size: 9px; letter-spacing: .2em; text-transform: uppercase; font-weight: 600; color: var(--ink-faint); }
.adm-field { margin-bottom: 16px; }
.adm-radio { display: flex; align-items: center; gap: 8px; cursor: pointer; margin: 2px 0; }
.tracker-entry[role="link"] { cursor: pointer; }
.tracker-entry[role="link"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.adm-field-row { display: flex; gap: 14px; flex-wrap: wrap; background: transparent; }
.adm-field-row > .adm-field { flex: 1; min-width: 160px; margin-bottom: 0; }
.adm-field-row:hover { background: transparent; }
.adm-panel { background: var(--paper-soft); border: 1px solid var(--rule-soft); border-radius: 2px; padding: 18px 20px; }
.adm-panel + .adm-panel { margin-top: 10px; }
.adm-list { display: flex; flex-direction: column; border: 1px solid var(--rule-soft); border-radius: 2px; overflow: hidden; }
.adm-list-row { display: flex; align-items: center; gap: 12px; padding: 11px 14px; background: var(--paper-soft); }
.adm-list-row + .adm-list-row { border-top: 1px solid var(--rule-soft); }
.adm-list-row:hover { background: var(--paper-warm); }
.adm-handle { cursor: grab; color: var(--ink-faint); font-size: 15px; line-height: 1; user-select: none; flex-shrink: 0; display: inline-flex; }
.adm-handle:active { cursor: grabbing; }
.adm-arrows { display: flex; flex-direction: column; gap: 1px; flex-shrink: 0; }
.adm-arrow { appearance: none; background: none; border: none; cursor: pointer; color: var(--ink-faint); font-size: 9px; line-height: 1; padding: 1px 3px; transition: color 160ms; }
.adm-arrow:hover { color: var(--accent-deep); }
.adm-arrow:disabled { opacity: .3; cursor: default; }
.adm-ord { font-family: var(--font-mono); font-size: 12px; color: var(--accent-deep); flex-shrink: 0; min-width: 18px; text-align: center; }
.adm-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.adm-table th { text-align: left; font-family: var(--font-body); font-size: 9px; letter-spacing: .16em; text-transform: uppercase; font-weight: 600; color: var(--ink-faint); padding: 0 14px 9px; border-bottom: 1px solid var(--rule); white-space: nowrap; }
.adm-table th.num, .adm-table td.num { text-align: right; }
.adm-table th.center, .adm-table td.center { text-align: center; }
.adm-table th.col-r, .adm-table td.col-r { text-align: right; }
.adm-table td { padding: 11px 14px; border-bottom: 1px solid var(--rule-soft); color: var(--ink-soft); vertical-align: middle; }
.adm-table tbody tr { transition: background 140ms ease; }
.adm-table tbody tr:hover { background: var(--paper-soft); }
.adm-table .t-primary { font-family: var(--font-display); font-style: italic; font-size: 16px; color: var(--ink); line-height: 1.15; }
.adm-table .t-mono { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-mute); }
.adm-table .t-mute { color: var(--ink-mute); }
.adm-row-actions { display: inline-flex; align-items: center; gap: 6px; justify-content: flex-end; }
.cell-trunc { max-width: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adm-badge { display: inline-flex; align-items: center; gap: 5px; border-radius: 999px; font-family: var(--font-body); font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase; font-weight: 600; padding: 3px 9px; border: 1px solid var(--rule); color: var(--ink-mute); background: transparent; white-space: nowrap; line-height: 1.4; }
.adm-badge.accent { color: var(--accent-deep); border-color: color-mix(in oklab, var(--accent) 45%, var(--rule)); background: var(--accent-wash); }
.adm-badge.cat { font-family: var(--font-display); font-style: italic; font-size: 13px; letter-spacing: 0; text-transform: none; padding: 1px 10px; color: var(--ink-soft); }
.adm-badge.danger { color: var(--danger); border-color: color-mix(in oklab, var(--danger) 45%, var(--rule)); background: var(--danger-wash); }
.adm-status { display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-body); font-size: 11px; letter-spacing: .06em; color: var(--ink-mute); text-transform: capitalize; }
.adm-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--ink-faint); }
.adm-fieldgroup { font-family: var(--font-body); font-weight: 600; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); margin: 20px 0 8px; }
.adm-dot.ok { background: var(--ok); }
.adm-dot.banned { background: var(--danger); }
.adm-avatar { width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-display); font-style: italic; font-size: 14px; color: var(--ink-soft); border: 1px solid var(--rule); background: linear-gradient(155deg, var(--accent-wash), var(--paper-warm)); }
.adm-count { display: inline-flex; align-items: center; justify-content: center; min-width: 17px; height: 17px; padding: 0 5px; border-radius: 999px; background: var(--accent-deep); color: var(--paper-soft); font-family: var(--font-mono); font-size: 9px; letter-spacing: 0; line-height: 1; }
.adm-toolbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 9px 0; border-top: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); margin-bottom: 14px; }
.adm-toolbar .spacer { flex: 1; }
.adm-toolbar .count { font-family: var(--font-display); font-style: italic; font-size: 14px; color: var(--ink-faint); white-space: nowrap; }
.adm-select { position: relative; display: inline-flex; align-items: baseline; gap: 7px; background: transparent; border: none; border-bottom: 1px dashed var(--rule); padding: 3px 18px 3px 0; cursor: pointer; font-family: var(--font-body); transition: border-color 160ms; }
.adm-select:hover { border-bottom-color: var(--accent); }
.adm-select .k { font-size: 9px; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-faint); }
.adm-select .v { font-family: var(--font-display); font-style: italic; font-size: 15px; color: var(--ink); }
.adm-select .caret { position: absolute; right: 0; top: 52%; transform: translateY(-50%); font-size: 10px; color: var(--ink-faint); }
.adm-search { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--rule-soft); border-radius: 1px; background: var(--paper-soft); padding: 6px 10px; min-width: 220px; }
.adm-search i { color: var(--ink-faint); font-size: 14px; }
.adm-search input { border: none; background: transparent; outline: none; width: 100%; color: var(--ink); font-size: 13px; font-family: var(--font-body); }
/* A11y: the input has no border of its own; ring the wrapper on keyboard focus. */
.adm-search:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; }
.adm-search input::placeholder { color: var(--ink-faint); font-style: italic; }
.adm-empty { text-align: center; padding: 40px 24px; color: var(--ink-faint); }
.adm-empty i { font-size: 26px; opacity: .5; display: block; margin-bottom: 10px; }
.adm-empty .t { font-family: var(--font-display); font-style: italic; font-size: 17px; color: var(--ink-mute); }
.adm-empty .s { font-size: 12px; margin-top: 4px; }
/* Tab header + titled settings block (Stage 17.5.7 CP8a) — consumed in CP8b. */
.adm-tab-head { font-family: var(--font-display); font-style: italic; font-size: 30px; font-weight: 500; color: var(--ink); line-height: 1.1; margin: 16px 0 4px; }
.adm-tab-head-note { font-size: 13px; color: var(--ink-faint); font-style: italic; line-height: 1.5; margin: 0 0 28px; max-width: 64ch; }
.admin-block { background: var(--paper); border: 1px solid var(--rule-soft); border-radius: 2px; overflow: hidden; margin-bottom: 16px; }
.admin-block-head { background: var(--paper-soft); display: flex; align-items: baseline; justify-content: space-between; gap: 10px 16px; flex-wrap: wrap; padding: 12px 16px; border-bottom: 1px solid var(--rule-soft); }
.admin-block-title { font-family: var(--font-body); font-size: 13px; letter-spacing: .12em; text-transform: uppercase; font-weight: 600; color: var(--ink-mute); margin: 0; }
.admin-block-body { background: var(--paper); padding: 16px; }
.admin-block-body .adm-field:last-child { margin-bottom: 0; }
.adm-list-header { display: flex; align-items: center; gap: 12px; padding: 6px 14px; }
.adm-list-header span { font-family: var(--font-body); font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600; color: var(--ink-faint); }
.adm-list-header .adm-arrows { width: 15px; min-width: 15px; }
.adm-list-row .field-input { font-size: 12px; }
.adm-btn-hide { appearance: none; background: none; border: 1px solid var(--rule-soft); border-radius: 50%; cursor: pointer; width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; color: var(--ink-faint); font-size: 13px; transition: color 140ms, border-color 140ms; flex-shrink: 0; }
.adm-btn-hide:hover { color: var(--ink); border-color: var(--rule); }

/* Short Answer field editor — Top / Academic / Bottom each render as their own
   admin block; a field's section is fixed by definition and reorder stays within it. */
.adm-fe-section-empty { padding: 11px 14px; font-size: 12px; font-style: italic; color: var(--ink-faint); }

/* ============================================================
   BIOGRAPHY TAB
   ============================================================ */
.biography-tab {
  padding-bottom: 96px;
}
.biography-tab textarea.prose-input {
  min-height: 280px;
}
.biography-doc {
  display: flex;
  flex-direction: column;
  gap: 68px;
}
.bio-section {
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.bio-heading {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 34px;
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--ink);
}
.bio-body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.78;
  color: var(--ink-soft);
  margin: 0;
  text-wrap: pretty;
}
.bio-body p { margin: 0 0 1.1em; }
.bio-body p:last-child { margin-bottom: 0; }
.bio-body p:first-of-type::first-letter {
  font-family: var(--font-display);
  font-weight: 500;
  font-style: normal;
  font-size: 1.35em;
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.01em;
  padding-right: 0.04em;
}

/* Bubble: display heading, not italic */
body.theme-bubble .bio-heading {
  font-style: normal;
}

/* ==========================================================================
   PHASE 9 STAGE 9.2a — FORUM READ-VIEW SURFACE (structure + neutral/token)
   Three-way split per D-053: this file carries STRUCTURE + theme-NEUTRAL
   appearance (tokens, black/white scrims, on-image banner text) only.
   Island palette literals live in island.css; theme-academia overrides in
   academia.css. Forum separator renamed book-divider -> thread-divider to
   avoid a past collision with the former .book-divider profile divider (now .section-divider).
   Depends on: token layer (--paper-soft,--accent,--ink-*,--rule*,--shadow-*,
   --accent-soft,--accent-wash,--accent-deep,--paper-warm) defined per-theme.
   ========================================================================== */

/* ----- Section A: meta pills (.pill absent live -> ported) ----- */
.pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 11px;
  font-family: var(--font-body);
  font-size: 9.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  font-weight: 600;
  border: 1px solid var(--rule);
  color: var(--ink-mute);
  border-radius: 999px;
  background: var(--paper);
}
.pill.pill-accent {
  border-color: color-mix(in oklab, var(--accent) 40%, var(--rule));
  background: var(--accent-wash);
  color: var(--accent-deep);
}
.pill.pill-num { color: var(--ink); }
.pill.pill-num .n {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0;
  margin-right: 5px;
  color: var(--accent-deep);
  text-transform: none;
}

/* ----- Section B: read-view forum surface ----- */
.cat-card {
  position: relative;
  display: block;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--rule);
  background: var(--paper-soft);
  overflow: hidden;
  cursor: pointer;
  box-shadow: var(--shadow-soft);
  transition: transform 240ms cubic-bezier(.2,.7,.3,1),
              box-shadow 240ms ease,
              border-color 240ms ease;
}
.cat-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
  border-color: var(--accent);
}

/* banner — solid color stand-in with darken overlay; --cat-bg fallback is the
   default tint (neutral token; theme files provide their own fallback). */
.cat-banner {
  position: relative;
  height: 210px;
  width: 100%;
  background: var(--cat-bg, var(--paper-warm));
  overflow: hidden;
}
.cat-banner::before {
  content: "";
  position: absolute; inset: 0;
  background-image:
    repeating-linear-gradient(45deg, transparent 0 14px, rgba(255,255,255,0.05) 14px 15px),
    radial-gradient(circle at 22% 30%, rgba(255,255,255,0.18) 0%, transparent 55%);
}
.cat-banner::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(0,0,0,0.08) 0%,
    rgba(0,0,0,0.22) 38%,
    rgba(0,0,0,0.70) 100%);
}
.cat-banner-body {
  position: absolute;
  left: 40px; right: 40px; bottom: 24px;
  z-index: 2;
  color: #f5efe6;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 72ch;
}
.cat-eyebrow {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  font-weight: 600;
  color: rgba(245, 239, 230, 0.78);
  display: inline-flex; align-items: center; gap: 10px;
}
.cat-eyebrow::before {
  content: "";
  width: 18px; height: 1px; background: currentColor;
  display: inline-block;
}
.cat-banner-title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 46px;
  line-height: 1;
  letter-spacing: -0.015em;
  margin: 0;
  text-shadow: 0 1px 2px rgba(0,0,0,0.35);
}
.cat-banner-desc {
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.55;
  color: rgba(245, 239, 230, 0.86);
  margin: 0;
  max-width: 64ch;
  text-shadow: 0 1px 2px rgba(0,0,0,0.3);
}

/* meta strip below the banner */
.cat-meta {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  align-items: center;
  gap: 32px;
  padding: 14px 24px;
  border-top: 1px solid var(--rule-soft);
  background: var(--paper-soft);
}
.cat-meta-counts {
  display: flex; align-items: baseline; gap: 22px;
}
.cat-meta-num {
  display: inline-flex; align-items: baseline; gap: 8px;
  font-family: var(--font-body);
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
}
.cat-meta-num .n {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 22px;
  letter-spacing: 0;
  color: var(--ink);
  text-transform: none;
}
.cat-meta-last {
  display: flex; align-items: center; gap: 12px;
  min-width: 0;
  justify-self: end;
}
.cat-meta-last-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; text-align: right; }
.cat-meta-last-label {
  font-family: var(--font-body);
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
}
.cat-meta-last-by {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.1;
}
.cat-meta-last-when {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-mute);
  letter-spacing: 0.04em;
}

/* ----- category detail: thread listing ----- */
.thread-list {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.thread-row {
  position: relative;
  display: grid;
  grid-template-columns: 52px 1fr 220px 110px;
  align-items: stretch;
  gap: 22px;
  padding: 22px 24px 22px 12px;
  border-top: 1px solid var(--rule-soft);
  cursor: pointer;
  text-decoration: none;
  color: inherit;
  transition: background 220ms ease, padding-left 220ms ease;
  overflow: hidden;
}
.thread-list .thread-row:last-of-type { border-bottom: 1px solid var(--rule-soft); }
.thread-row:hover {
  background: var(--paper-soft);
  padding-left: 24px;
}
.thread-row-ordinal {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 30px;
  line-height: 1;
  color: var(--accent);
  align-self: start;
  padding-top: 4px;
  text-align: right;
  opacity: 0.85;
}
.thread-row-main { min-width: 0; display: flex; flex-direction: column; gap: 6px; }
/* #72 / #21: title left, compact CW chip right — same row, chip never wraps */
.thread-row-title-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.thread-row-title-row .thread-row-title { flex: 1 1 auto; min-width: 0; margin: 0; }
.thread-row-title-row .warn-callout--compact { flex: 0 0 auto; align-self: flex-start; margin-top: 4px; }
.thread-row-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 26px;
  line-height: 1.18;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin: 0 0 2px;
  text-wrap: pretty;
  position: relative;
  padding-bottom: 8px;
}
.thread-row-title::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 36px; height: 1px;
  background: color-mix(in oklab, var(--accent) 55%, var(--rule));
  transition: width 280ms cubic-bezier(.2,.7,.3,1);
}
.thread-row:hover .thread-row-title::after { width: 80px; }
.thread-row-title .it { font-style: italic; }
.thread-row-desc {
  font-family: var(--font-body);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 4px 0 0;
  max-width: 72ch;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.thread-row-last {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 4px;
  border-left: 1px solid var(--rule-soft);
  padding-left: 22px;
  min-width: 0;
}
.last-label {
  font-family: var(--font-body);
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
}
.last-by {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 17px;
  color: var(--ink);
  line-height: 1.15;
}
.last-owner {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--accent-deep);
}
.last-when {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-mute);
  margin-top: 4px;
  letter-spacing: 0.04em;
}
.thread-row-stats {
  display: flex; flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
  text-align: center;
  border-left: 1px solid var(--rule-soft);
  padding: 22px;
}
.thread-row-stats .stat-num { font-size: 28px; }
.stat {
  display: flex; flex-direction: column;
  align-items: center; gap: 2px;
}
.stat-num {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 24px;
  color: var(--accent-deep);
  line-height: 1;
}
.stat-label {
  font-family: var(--font-body);
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
}

/* portrait teaser pinned right of a thread row. The two token/neutral gradient
   layers stay in base; the Island-toned accent gradient layer moves to
   island.css (Academia overrides the whole background). */
.thread-row-teaser {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 86px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 280ms ease;
}
.thread-row:hover .thread-row-teaser { opacity: 0.65; }

/* ----- thread flags: badges (list) + locked notice + admin controls ----- */
/* Compact flag badge — icon-only (or icon + label for complete). Placed inline
   in the title row; flex: 0 0 auto keeps it from growing. */
.thread-flag {
  display: inline-flex; align-items: center; gap: 3px;
  flex: 0 0 auto; align-self: flex-start; margin-top: 4px;
  padding: 2px 6px;
  font-family: var(--font-body); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--ink-mute); background: var(--paper-soft);
  border: 1px solid var(--rule); border-radius: 999px;
  line-height: 1.4;
}
.thread-flag i { font-size: 12px; }
.thread-flag-label { display: inline; }
/* Complete variant: accent color */
.thread-flag--complete { color: var(--accent-deep); border-color: var(--accent-soft); }

/* Locked notice — shown below the post stack when thread.locked */
.thread-locked-notice {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 18px;
  margin-top: 8px;
  font-family: var(--font-body); font-size: 13px; font-weight: 600;
  color: var(--ink-soft);
  background: var(--paper-soft); border: 1px solid var(--rule);
  border-radius: 6px;
}
.thread-locked-notice i { font-size: 16px; color: var(--ink-mute); }

/* Admin flag-toggle row in the thread header pill area */
.thread-admin-flags {
  display: inline-flex; align-items: center; gap: 6px;
}

/* Inline error below the heading when a flag RPC fails */
.thread-flag-error {
  font-family: var(--font-body); font-size: 12.5px;
  color: var(--danger-ink, #b4453a);
  margin: 4px 0 0; padding: 0;
}

/* ----- thread page: posts ----- */
.post-stack {
  display: flex;
  flex-direction: column;
  gap: 36px;
  margin-top: 4px;
}
.post {
  position: relative;
  background: var(--paper-soft);
  border: 1px solid var(--rule);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
}
.post.is-op { border-top: 3px solid var(--accent); }

.post-banner {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 22px;
  padding: 22px 30px;
  background:
    linear-gradient(135deg,
      color-mix(in oklab, var(--accent) 14%, var(--paper-warm)) 0%,
      var(--paper-warm) 60%,
      color-mix(in oklab, var(--accent) 8%, var(--paper-soft)) 100%);
  border-bottom: 1px solid var(--rule-soft);
  position: relative;
}
.post-banner::before {
  content: "";
  position: absolute; inset: 0;
  background-image: repeating-linear-gradient(45deg, transparent 0 14px, rgba(0,0,0,0.025) 14px 15px);
  pointer-events: none;
}
.banner-titles {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  z-index: 1;
}
.banner-name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 42px;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0;
  text-wrap: pretty;
}
/* Clickable names in the forum (post author → character profile; played-by /
   last-reply → user profile). Inherits the surrounding type; reveals on hover. */
.thread-name-link {
  color: inherit;
  font: inherit;
  font-style: normal;  /* names read better upright even where the surround is italic */
  text-decoration: none;
  cursor: pointer;
  border-bottom: 1px dotted transparent;
  transition: border-color 160ms ease, color 160ms ease;
}
.thread-name-link:hover { border-bottom-color: currentColor; }
.banner-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  z-index: 1;
}
.post-body-grid {
  display: grid;
  grid-template-columns: 220px 1fr;
}
.post-side {
  border-right: 1px solid var(--rule-soft);
  padding: 26px 22px 26px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  background: var(--paper-soft);
}
.post-portrait {
  width: 100%;
  aspect-ratio: 3 / 4;
  background: var(--paper-soft);
  border: 1px solid color-mix(in oklab, var(--accent) 28%, var(--rule));
  position: relative;
  overflow: hidden;
}
.post-portrait::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
}
.post-portrait-fill {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.post-side-divider {
  display: flex; align-items: center; gap: 10px;
  color: var(--accent);
  opacity: 0.6;
  margin: 2px 0;
}
.post-side-divider .ln { flex: 1; height: 1px; background: var(--rule); }
.post-side-divider .orn { font-family: var(--font-display); font-size: 12px; color: var(--accent); }
/* The Phosphor-heart alternative to the ❦ fleuron is hidden by default; a theme
   (Bubble) can show the heart and hide the glyph. */
.post-side-divider .orn i.ph, .thread-divider .ornament i.ph { display: none; }
.side-group-label {
  font-family: var(--font-body);
  font-size: 9px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--accent-deep);
  text-align: center;
}
.side-group-name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 15px;
  color: var(--ink);
  text-align: center;
  line-height: 1.2;
  margin-top: -8px;
}
.side-owner {
  text-align: center;
  margin-top: 8px;
  padding-top: 14px;
  border-top: 1px dashed var(--rule);
  display: flex; flex-direction: column; gap: 3px;
}
.side-owner-label {
  font-family: var(--font-body);
  font-size: 8.5px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
}
.side-owner-name {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.1;
}
.post-content {
  padding: 24px 32px 30px;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.post-content-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 16px;
  border-bottom: 1px dashed var(--rule-soft);
  margin-bottom: 22px;
  flex-wrap: wrap;
}
.post-actions { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.post-stamp, .timeline-attribution {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--ink-mute);
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
}
.timeline-attribution { display: block; text-align: right; }
.post-stamp .date { color: var(--ink); }
.post-stamp .sep { color: var(--ink-faint); }
.post-stamp .post-num {
  font-family: var(--font-display);
  font-style: italic;
  color: var(--accent-deep);
  font-size: 13px;
  letter-spacing: 0;
}
.post-prose {
  font-family: var(--font-body);
  font-size: 15.5px;
  line-height: 1.8;
  color: var(--ink-soft);
  text-wrap: pretty;
  margin: 0;
  width: 100%;
}
.post-prose p { margin: 0 0 1.05em; white-space: pre-line; }
.post-prose p:last-child { margin-bottom: 0; }
.post-prose p:first-of-type::first-letter {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.35em;
  color: var(--ink);
  letter-spacing: -0.01em;
  padding-right: 0.04em;
}

/* post separator — RENAMED from .book-divider to .thread-divider to avoid a past
   collision with the former .book-divider profile divider (now .section-divider). Structural
   + token only; no themed literals. */
.thread-divider {
  display: flex; align-items: center; gap: 18px;
  color: var(--accent);
  opacity: 0.65;
  margin: 4px 60px;
}
.thread-divider .line { flex: 1; height: 1px; background: var(--rule); }
.thread-divider .ornament {
  font-family: var(--font-display);
  font-size: 18px;
  line-height: 1;
  color: var(--accent);
}

/* pagination */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  margin-top: 42px;
  padding-top: 26px;
}
.pagination-info {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  color: var(--ink-mute);
}
.pagination-info .current {
  color: var(--ink);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 17px;
  letter-spacing: 0;
  margin: 0 4px;
}
.pagination-right { display: flex; align-items: center; gap: 18px; }
.pagination-controls { display: flex; gap: 6px; align-items: center; }
.pagination .crumbs { margin: 0; }
.page-btn {
  width: 25px; height: 25px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-mono);
  font-size: 10.5px;
  transition: all 180ms ease;
  flex-shrink: 0;
}
.page-btn:hover { color: var(--ink); border-color: var(--ink-soft); background: var(--paper-warm); }
.page-btn[disabled] { opacity: 0.35; cursor: not-allowed; }
.page-btn[disabled]:hover { color: var(--ink-soft); border-color: var(--rule); background: transparent; }
.page-btn.is-current {
  background: var(--accent-deep); color: var(--paper-soft); border-color: var(--accent-deep);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 11px;
}

/* reply CTA bar */
.thread-foot {
  margin-top: 32px;
  padding: 28px 32px;
  background: var(--paper-soft);
  border: 1px dashed var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.thread-foot-msg {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 19px;
  color: var(--ink-soft);
  margin: 0;
  max-width: 52ch;
  line-height: 1.5;
}

/* ----- Section C: real <img> elements (structural only) ----- */
.cat-banner-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
}
.post-portrait-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
}

/* ----- Section D: forum responsive (layout shifts only) -----
   No existing @media (max-width: 980px) block live -> new standalone block. */
@media (max-width: 980px) {
  .thread-row { grid-template-columns: 40px 1fr; gap: 14px; }
  .thread-row-last,
  .thread-row-stats { grid-column: 1 / -1; border-left: none; padding-left: 54px; padding-top: 6px; flex-direction: row; align-items: center; justify-content: flex-start; gap: 14px; }
  .thread-row-teaser { display: none; }
  .post-banner { grid-template-columns: 1fr; row-gap: 12px; padding: 18px 22px; }
  .banner-actions { grid-column: 1 / -1; }
  .banner-name { font-size: 32px; }
  .post-body-grid { grid-template-columns: 1fr; }
  .post-side {
    border-right: none;
    border-bottom: 1px solid var(--rule-soft);
    display: grid;
    grid-template-columns: 110px 1fr;
    gap: 18px;
    align-items: center;
  }
  .post-portrait { width: 110px; }
  .post-side-divider { display: none; }
  .side-group-label, .side-group-name, .side-owner { text-align: left; }
  .side-owner { border-top: none; padding-top: 0; margin-top: 6px; }
  .cat-banner { height: 200px; }
  .cat-banner-title { font-size: 40px; }
  .cat-banner-body { left: 26px; right: 26px; bottom: 22px; }
  .cat-meta { grid-template-columns: 1fr; gap: 8px; }
  .cat-meta-last { justify-self: start; }
  .cat-meta-last-text { text-align: left; }
}
/* ── Phase 9 forum: page-structure layer (views emit these; mockup assumed shared) ── */
/* .page-contained — canonical shared wrapper for P1 use on pages without their own wrapper. */
.page-contained {
  max-width: var(--page-w-contained);
  margin: 0 auto;
  padding: 36px 56px 140px;
  width: 100%;
}
.page-wrap {
  max-width: 1180px;   /* match the directory frame (.dir-grid) — owner page-gutter standard 2026-06-17 */
  margin: 0 auto;
  padding: 18px clamp(18px, 4vw, 44px) 90px;
  width: 100%;
}

/* ── Stage 10: User Profile page ── */
.up-header { display: flex; align-items: flex-start; gap: 24px; margin-bottom: 40px; }
.up-avatar { flex-shrink: 0; width: 72px; height: 72px; border-radius: 50%; overflow: hidden;
  background: var(--paper-warm); border: 1px solid var(--rule); display: flex; align-items: center; justify-content: center; }
/* #42: plain white border ring — mirrors .dir-avatar white treatment */
.up-avatar--white-ring { background: #fff; border-color: #fff; box-shadow: var(--shadow-soft, 0 1px 6px rgba(0,0,0,.10)); }

.up-avatar-img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; }
.up-avatar-initial { font-family: var(--font-display); font-size: 28px; color: var(--ink-mute); }
.up-identity { display: flex; flex-direction: column; gap: 4px; }
/* user profile only: drop the directory pill's left margin so it centers under the handle */
.up-identity .dir-admin-pill { margin-left: 0; }
.up-display-name { font-family: var(--font-display); font-size: 28px; font-weight: 500; color: var(--ink);
  margin: 0; display: flex; align-items: center; gap: 10px; }
.up-username { font-size: 13px; letter-spacing: .02em; margin-top: 2px; color: var(--ink-mute); }
.up-role-badge { font-family: var(--font-body); font-size: 10px; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--ink-mute); border: 1px solid var(--rule); border-radius: 3px; padding: 2px 6px; }
.up-section { margin-bottom: 48px; }
/* #24: characters section heading — fc-sub-head with pill right-aligned; zero padding override.
   Two-class selector beats pages.css single-class .fc-sub-head { padding: 0 40px } (pages.css
   loads after base.css; same specificity = later wins; .up-main .up-chars-head has higher specificity). */
.up-chars-head { padding: 0; margin: 0 0 16px; }
.up-main .up-chars-head { padding: 0; margin: 0 0 16px; }
/* #25: tighten gap after the last quickstat row (contact) — reduces the .up-section bottom gap
   by targeting the sidebar's DetailsBlock section only. Was 48px → 28px → 12px.
   Root cause of excess bottom space: this margin sits between the quickstats block and the
   dir-card-foot footer row; 28px was still too large. 12px gives visual breathing room without
   feeling like a large gap. */
.up-sidebar .up-section:has(.quickstats) { margin-bottom: 12px; }
/* Remove bottom margin/padding from quickstats container on the user profile page. */
.up-sidebar .quickstats { margin-bottom: 0; padding-bottom: 0; }
.up-bio { font-family: var(--font-body); font-size: 14px; line-height: 1.65; color: var(--ink); margin: 0; }
.up-empty-state { font-family: var(--font-body); font-size: 13px; color: var(--ink-mute); margin: 0; }

/* v1.17.0: two-column user profile — left sidebar (identity / triggers / details)
   + main (about, my characters). Structure only; themes supply the look. */
.up-layout { display: flex; align-items: flex-start; gap: 56px; }
.up-sidebar { flex: 0 0 320px; max-width: 320px; }
.up-main { flex: 1 1 auto; min-width: 0; }
.up-sidebar .up-header { margin-bottom: 28px; }
/* breathing room between "At a glance" stats block and "My characters" section */
.up-main .stats { margin-bottom: 48px; }
/* 3-up character card grid scoped to user profile only (registry uses auto-fill) */
.up-main .char-grid { grid-template-columns: repeat(3, 1fr); gap: 20px; }
@media (max-width: 1024px) {
  .up-layout { flex-direction: column; gap: 28px; }   /* padding between the stacked sidebar + main */
  .up-sidebar { flex: 1 1 auto; max-width: none; }
  /* Keep stacked-column children from overflowing the viewport. */
  .up-main, .up-sidebar, .up-main > *, .up-sidebar > * { min-width: 0; max-width: 100%; }
}
@media (max-width: 600px) {
  .up-main .char-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 400px) {
  .up-main .char-grid { grid-template-columns: 1fr; }
}

/* User profile edit-mode field input */
.up-edit-input {
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink);
  background: var(--paper-warm);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 5px 8px;
  width: 100%;
  box-sizing: border-box;
}
.up-edit-input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }
.up-edit-select { appearance: none; cursor: pointer; }
.up-edit-select:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }
.up-tz-note { font-size: 11px; color: var(--ink-faint); margin: 4px 0 0; line-height: 1.4; }

.forum-page .page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  margin-bottom: 44px;
  flex-wrap: wrap;
}
.page-head-titles { display: flex; flex-direction: column; gap: 10px; min-width: 0; flex: 1 1 auto; }
.page-head-actions { display: flex; gap: 10px; flex-shrink: 0; }

.page-eyebrow {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--accent-deep);
  display: inline-flex; align-items: center; gap: 10px;
}
button.page-eyebrow {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
button.page-eyebrow:hover { opacity: 0.72; }
button.page-eyebrow:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }

.page-title {
  font-family: var(--font-display);
  font-size: 56px;
  line-height: 1;
  letter-spacing: -0.02em;
  font-weight: 400;
  margin: 0;
  color: var(--ink);
}
.page-title .it { font-style: italic; }

.page-subtitle {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 19px;
  color: var(--ink-mute);
  line-height: 1.5;
  margin: 8px 0 0;
  max-width: 64ch;
}

/* forum thread pages: smaller subtitle to pair with dir-heading title scale */
.forum-subtitle {
  font-size: 14px;
  margin: -16px 0 32px;
}

/* forum responsive: tighten the wrap padding (mockup 980 breakpoint) */
@media (max-width: 980px) {
  .page-wrap,
  .page-contained { padding: 24px 24px 100px; }
  .page-title { font-size: 40px; }
}
/* ── Phase 9 forum: breadcrumb ── */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  font-family: var(--font-body);
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-mute);
  margin: 0 0 32px;
  line-height: 1.6;
}
.crumbs a {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: color 160ms ease;
  border-bottom: 1px dotted transparent;
  padding-bottom: 1px;
}
.crumbs a:hover { color: var(--ink); border-bottom-color: currentColor; }
.crumbs .sep { color: var(--ink-faint); font-weight: 400; letter-spacing: 0; font-size: 12px; }
.crumbs .sep .ph { font-size: 11px; opacity: 0.7; }
/* current crumb uses the same all-caps grey link style as the link crumbs
   (just non-clickable + slightly darker ink to mark position) — consistency */
.crumbs .current {
  color: var(--ink);
  font-style: normal;
  font-family: var(--font-body);
  text-transform: uppercase;
  font-weight: 600;
  font-size: 10.5px;
  letter-spacing: 0.24em;
}
/* Phase 9 forum: pill buttons (Share / Edit on post banners) */
.pill-btn {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  padding: 7px 14px;
  border: 1px solid var(--rule);
  background: var(--paper);
  color: var(--ink-mute);
  cursor: pointer;
  border-radius: 999px;
  display: inline-flex; align-items: center; gap: 7px;
  transition: all 180ms ease;
}
.pill-btn:hover {
  color: var(--accent-deep);
  border-color: color-mix(in oklab, var(--accent) 45%, var(--rule));
  background: var(--accent-wash);
}
.pill-btn .ph { font-size: 13px; }
.category-list { display: flex; flex-direction: column; gap: 32px; margin-top: 8px; }
/* ============================================================
   COMPOSE EDITOR — Phase 9 Stage 9.3 (Forum_Mockup.html §4-5).
   Structural rules per D-053; theme-academia variants in academia.css;
   tokens unchanged.
   NAMESPACED under .editor: the bare .field-input / .field (and the rest
   of the .field* set) collide with live LoginModal (.modal .field-input,
   14px body). The mockup's .field-input is a 24px display input — scoping
   under .editor keeps the compose look without clobbering the live modal
   inputs. .editor* and .mode-toggle are absent live, ported as-is.
   ============================================================ */
/* Base field label — used by the Tinder/Instagram dev-block editors, which live
   outside the .editor scope (the more-specific .editor .field-label below still
   wins inside the profile editor). Mirrors .adm-list-header span. */
.field-label {
  font-family: var(--font-body);
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
}

.editor {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 860px;
}
.editor .field { display: flex; flex-direction: column; gap: 10px; }
.editor .field-label {
  font-family: var(--font-body);
  font-size: 10px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-faint);
  display: inline-flex; align-items: center; gap: 9px;
}
.editor .field-input {
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 400;
  background: var(--paper-soft);
  border: 1px solid var(--rule-soft);
  border-radius: 1px;
  padding: 14px 16px;
  color: var(--ink);
  outline: none;
  transition: border-color 180ms ease, background 180ms ease;
  width: 100%;
}
.editor .field-input:focus { border-color: var(--accent); background: var(--paper); }
.editor .field-input::placeholder { color: var(--ink-faint); font-style: italic; }
.editor .field-input.sm { font-size: 14px; }

.editor .field-select {
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  background: var(--paper-soft);
  border: 1px solid var(--rule-soft);
  border-radius: 1px;
  padding: 12px 38px 12px 16px;
  color: var(--ink);
  outline: none;
  width: 240px;
  appearance: none;
  cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-mute) 50%),
                    linear-gradient(135deg, var(--ink-mute) 50%, transparent 50%);
  background-position: calc(100% - 16px) 52%, calc(100% - 11px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  transition: border-color 180ms ease;
}
.editor .field-select:focus { border-color: var(--accent); }
.editor .field-select:disabled { cursor: default; opacity: 0.85; }

.editor .field-textarea {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.75;
  background: var(--paper-soft);
  border: 1px solid var(--rule-soft);
  border-radius: 1px;
  padding: 20px 22px;
  color: var(--ink);
  outline: none;
  min-height: 320px;
  resize: vertical;
  transition: border-color 180ms ease, background 180ms ease;
  width: 100%;
}
.editor .field-textarea:focus { border-color: var(--accent); background: var(--paper); color: var(--ink); }
.editor .field-textarea::placeholder { color: var(--ink-faint); font-style: italic; }

.editor .field-hint {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 14px;
  color: var(--ink-mute);
  margin: 0;
  line-height: 1.4;
}

.editor-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  padding-top: 22px;
  border-top: 1px dashed var(--rule-soft);
}
.editor-actions .btn { min-width: 130px; justify-content: center; }

/* Character picker — custom keyboard-accessible listbox (not native select). */
.editor .char-picker { position: relative; width: 320px; }
/* Phase 18 (large-phone ≤600): the fixed 320px picker can exceed a phone
   column. Let it shrink to its container; the menu already tracks left/right. */
@media (max-width: 600px) {
  .editor .char-picker { width: 100%; max-width: 100%; }
  .editor .char-picker-menu { max-height: 50vh; }
}
.editor .char-picker-trigger {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  background: var(--paper-soft);
  border: 1px solid var(--rule-soft);
  border-radius: 1px;
  padding: 10px 14px;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
  cursor: pointer;
  outline: none;
  transition: border-color 180ms ease;
}
.editor .char-picker-trigger:focus-visible { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-wash); }
.editor .char-picker-trigger[aria-expanded="true"] { border-color: var(--accent); }
.editor .char-picker-trigger .picker-portrait {
  width: 30px; height: 30px; border-radius: 1px; object-fit: cover;
  background: var(--paper-warm); flex-shrink: 0;
}
.editor .char-picker-trigger .picker-name { flex: 1; text-align: left; }
.editor .char-picker-trigger .picker-caret { color: var(--ink-mute); font-size: 12px; }
.editor .char-picker-menu {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 1px;
  max-height: 280px; overflow-y: auto;
  padding: 4px;
  box-shadow: 0 8px 24px color-mix(in oklab, var(--ink) 12%, transparent);
}
.editor .char-picker-option {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 11px;
  font-family: var(--font-body); font-size: 14px;
  color: var(--ink);
  cursor: pointer;
  border-radius: 1px;
}
.editor .char-picker-option .picker-portrait {
  width: 30px; height: 30px; border-radius: 1px; object-fit: cover;
  background: var(--paper-warm); flex-shrink: 0;
}
.editor .char-picker-option.is-active { background: var(--accent-wash); }
.editor .char-picker-option[aria-selected="true"] { font-weight: 600; }
.editor .char-picker-empty {
  padding: 12px 14px; font-family: var(--font-display); font-style: italic;
  font-size: 14px; color: var(--ink-mute);
}

.mode-toggle {
  display: inline-flex;
  border: 1px solid var(--rule);
  border-radius: 1px;
  overflow: hidden;
  align-self: flex-start;
  background: var(--paper-soft);
}
.mode-toggle button {
  background: transparent;
  border: none;
  padding: 11px 18px;
  font-family: var(--font-body);
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-mute);
  cursor: pointer;
  transition: all 180ms ease;
}
.mode-toggle button:hover { color: var(--ink); }
.mode-toggle button.is-active {
  background: var(--accent-deep);
  color: var(--paper-soft);
}

/* ============================================================
   STAGE 14.1 — Cleanup Wave 3, Batch D
   New classes ported from inline styles (I-051, I-115, I-165).
   ============================================================ */

/* I-051: sidebar edit-mode inputs (timeline.js) */
.sidebar-year-input {
  background: transparent;
  border-bottom: 1px dashed var(--rule);
  padding: 2px 0;
  font-family: inherit;
  font-size: inherit;
  font-style: italic;
}
.sidebar-quote-input {
  min-height: 80px;
  font-style: italic;
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.5;
}

/* I-115: fieldCard.js edit-mode input and character counter */
.field-card-input {
  background: transparent;
  border-bottom: 1px dashed var(--rule);
  padding: 2px 0;
  font-family: inherit;
  font-size: inherit;
}
.field-char-counter {
  display: block;
  font-size: 10px;
  text-align: right;
  margin-top: 2px;
  color: var(--ink-faint);
  font-family: var(--font-mono);
  letter-spacing: .02em;
}
.field-char-counter.is-near { color: var(--danger, #c0392b); }

/* I-165: development.js Instagram/Tinder edit-mode layouts */
.field-label-gap { margin-top: 8px; }
.field-url-input {
  font-family: var(--font-mono);
  font-size: 11px;
}
.dev-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: 8px;
}
.dev-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin-top: 8px;
}
.tinder-interest-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-right: 6px;
  margin-bottom: 4px;
}
/* When the editor tag also carries .tinder-pill it inherits the finished-profile
   pill look; keep the remove × inside the pill and reveal it on hover (the base
   .likes-remove is hidden until its .likes-item parent is hovered, which this
   tag is not). */
.tinder-interest-tag.tinder-pill { overflow: visible; }
.tinder-interest-tag .likes-text { flex: 0 1 auto; }
.tinder-interest-tag:hover .likes-remove,
.tinder-interest-tag:focus-within .likes-remove { opacity: 1; }

/* ============================================================
   NOTEBOOK PAGE (Stage 1 scaffold)
   ============================================================ */
.notebook-page {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  /* viewport minus sticky topbar (~88px) AND the fixed bottom bar (~52px) so
     the sidepanel's bottom tab strip lands above the bar instead of behind it */
  min-height: calc(100vh - 148px);
  padding: 0;   /* full-bleed; paper breathing room lives on .notebook-writecol */
  box-sizing: border-box;
  position: relative;
}
.notebook-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 680px;
  flex: 1;
  background: var(--paper-soft);
  border: none;
  border-radius: var(--radius-card, 22px);
  padding: 52px 56px 46px;
  box-sizing: border-box;
  min-height: 480px;
  box-shadow: inset 0 2px 14px rgba(120,104,170,0.09), 0 1px 0 rgba(255,255,255,0.9);
}
.notebook-title-input {
  width: 100%;
  background: transparent;
  border: none;
  padding: 0;
  margin-bottom: 22px;
  font-size: 34px;
  font-weight: 600;
  /* Roomy line-height + small vertical padding so a tall serif (Fraunces) isn't
     clipped at the top/bottom of the single-line input. */
  line-height: 1.32;
  padding: 4px 0;
  font-family: var(--font-display, inherit);
  color: var(--ink);
  outline: none;
  box-sizing: border-box;
}
.notebook-title-input::placeholder { color: var(--ink-faint); }
.notebook-body-input {
  width: 100%;
  min-height: 320px;
  background: transparent;
  border: none;
  resize: none;
  overflow: hidden; /* auto-grow: JS sets height to scrollHeight, page scrolls, no inner bar */
  padding: 0;
  font-size: 17.5px;
  font-family: var(--font-body, inherit);
  line-height: 1.8;
  color: var(--ink-soft);
  outline: none;
  box-sizing: border-box;
}
.notebook-body-input::placeholder { color: var(--ink-faint); }
/* N1b: inactivity fade — text dims bottom-up while idle */
.notebook-body-input--fade {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 45%, rgba(0,0,0,0.18) 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 45%, rgba(0,0,0,0.18) 100%);
}
/* Hard sprint pushes a deeper floor */
.notebook-body-input--fade-hard {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 30%, rgba(0,0,0,0.10) 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 30%, rgba(0,0,0,0.10) 100%);
}
/* N1b: focus/typewriter mode — overlay mirrors the textarea; the textarea text
   goes transparent (caret kept) while the overlay shows the per-line dim. The
   overlay MUST share the textarea's box + typography for the lines to align. */
.notebook-body-wrap {
  position: relative;
  width: 100%;
  /* Height hugs the auto-grown textarea so the focus overlay (inset:0) covers the
     full text height — fixes focus mode clipping/scroll loss on long documents. */
}
/* Two classes (0,2,0) to outrank `body.theme-bubble textarea` (0,1,1). */
.notebook-body-input.notebook-body-input--focus {
  color: transparent;
  caret-color: var(--ink-soft);
}
.notebook-focus-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
  font-size: 17.5px;
  line-height: 1.8;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  word-break: break-word;
  color: var(--ink-soft);
}
.notebook-focus-line {
  opacity: 0.32;
  transition: opacity 200ms ease;
  min-height: 1.8em;
}
.notebook-focus-line--active { opacity: 1; }
/* N1b: sprint vignette — inset glow that ramps in on pause */
.notebook-vignette {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 0 0 0 rgba(var(--notebook-vig), 0);
  transition: box-shadow 3500ms ease;
}
.notebook-vignette--on {
  box-shadow: inset 0 0 90px 24px rgba(var(--notebook-vig), 0.20);
}
.notebook-vignette--hard {
  transition: box-shadow 2100ms ease;
}
.notebook-vignette--hard.notebook-vignette--on {
  box-shadow: inset 0 0 130px 44px rgba(var(--notebook-vig), 0.46);
}
/* No focus ring on the writing surface — mockup keeps the paper border-free
   (the panel itself is the visible affordance). */
.notebook-title-input:focus-visible,
.notebook-body-input:focus-visible { outline: none; }
/* Owner request 2026-06-17: the WRITING surface (body textarea) stays border-free
   even on focus — the paper itself is the affordance, and a ring there reads as an
   unwanted "select border." The a11y focus ring is kept on the title input only.
   (.notebook-body-input:focus-visible keeps outline:none from the rule above.) */
.notebook-title-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── Sitewide toast stack (window.Toast / ToastHost) ─────────────────────────
   Appearance lives in bubble.css + bubble-dark.css (D-053). */
.toast-host {
  position: fixed;
  right: 16px;
  bottom: 74px;                 /* clears the sticky dock bar */
  z-index: 9500;                /* above modals (200) and notebook (45) */
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-end;
  max-height: calc(100vh - 120px);
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  width: min(296px, calc(100vw - 32px));
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 12px 12px 14px;
  border-radius: 16px;
  background: rgba(255,255,255,0.82);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  box-shadow: 0 14px 40px -14px rgba(120,104,170,0.55), inset 0 1px 0 rgba(255,255,255,0.7);
  color: var(--ink-soft);
  font-size: 13px;
  line-height: 1.4;
  animation: toast-in 320ms cubic-bezier(0.2,0.9,0.3,1.2);
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
.toast__ico { flex-shrink: 0; font-size: 16px; margin-top: 1px; color: #6a72c8; }
.toast--warn    .toast__ico { color: var(--warn, #b8860b); }
.toast--success .toast__ico { color: var(--ok,   #1f9e6e); }
.toast--error   .toast__ico { color: var(--danger, #c0392b); }
.toast__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.toast__msg strong { font-weight: 600; color: var(--ink); }
.toast__actions { display: flex; gap: 7px; justify-content: flex-end; flex-wrap: wrap; }
.toast__close {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  background: rgba(255,255,255,0.32);
  color: var(--ink-soft);
  cursor: pointer;
  font-size: 12px;
  transition: color 140ms, border-color 140ms, background 140ms;
}
.toast__close:hover { color: var(--ink); border-color: var(--ink-soft); }
.toast__close:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: 2px; }

/* ── Stage 3: main writing layout ────────────────────────────────────────── */
.notebook-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 296px;
  align-items: stretch; /* #46: stretch so sidepanel border meets the bottom bar */
  width: 100%;       /* full-bleed: sidepanel docks flush to the right edge */
  flex: 1;
  min-height: 0;
}
.notebook-main--no-panel {
  grid-template-columns: minmax(0, 1fr) 40px; /* #46: slim strip is 40px */
}
/* P1: When the side panel is collapsed, cap the writecol to the contained width
   so the writing area doesn't span the full viewport. */
.notebook-main--no-panel .notebook-writecol {
  max-width: var(--page-w-contained);
  width: 100%;
  margin-left: auto;
  margin-right: auto;
}
.notebook-writecol {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Generous bottom padding gives the auto-grown textarea room to scroll its
     last lines above the sticky dock (paired with .notebook-page scroll-padding). */
  padding: 40px 16px 96px;
}
/* Inside the writecol the panel fills available space up to its max-width. */
.notebook-writecol .notebook-panel {
  max-width: 680px;
  width: 100%;
  min-height: 480px;
  padding: 52px 56px 46px;
  box-sizing: border-box;
}

/* ── Stage 3: side panel ─────────────────────────────────────────────────── */
.notebook-sidepanel {
  align-self: stretch;
  border-left: 1px solid var(--rule-soft);
  padding: 30px 24px 0 28px;
  background: rgba(255,255,255,0.28);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  position: relative;
  min-height: 420px;
  box-sizing: border-box;
  /* P10: flex column so tab strip sits at bottom */
  display: flex;
  flex-direction: column;
  /* Gap fix: extend border/bg flush into the app-shell clearance padding so the
     panel's left border meets the bottom bar with no gap. */
  margin-bottom: -56px;
  padding-bottom: 56px;
}
.notebook-side-rail {
  align-self: stretch;
  width: 46px;
  border-left: 1px solid var(--rule-soft);
  background: rgba(255,255,255,0.28);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 30px;
  gap: 14px;
  position: relative;
  box-sizing: border-box;
  margin-bottom: -56px;
  padding-bottom: 56px;
}
/* Toggle button — slim caret tab on the left edge of the side panel */
.notebook-sp-toggle {
  position: absolute;
  left: -12px;
  top: 34px;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: transparent;
  border: none;
  box-shadow: none;
  cursor: pointer;
  display: grid;
  place-items: center;
  color: var(--ink-soft);
  opacity: 0.5;
  z-index: 3;
  font-size: 12px;
  padding: 0;
  transition: opacity 0.15s, color 0.15s;
}
.notebook-sp-toggle:hover { opacity: 1; color: var(--accent-deep, #cc3b7c); }
.notebook-sp-toggle:focus-visible {
  outline: 2px solid var(--accent, #e04c93);
  outline-offset: 2px;
  opacity: 1;
}
/* In the rail the toggle is static */
.notebook-side-rail .notebook-sp-toggle {
  position: static;
  left: auto;
  top: auto;
}
.notebook-sp-head {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin: 0 0 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.notebook-sp-head i { color: var(--notebook-icon-tint); font-size: 14px; }
/* Right-aligned action group: tiny round eye (hide) beside the + (add) */
.notebook-sp-actions {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.notebook-sp-actions i { color: inherit; font-size: inherit; } /* let buttons own icon color */
.notebook-sp-hide {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: none;
  background: transparent;
  color: var(--ink-faint);
  cursor: pointer;
  font-size: 12px;
  padding: 0;
  opacity: 0.7;
  transition: color 160ms, background 160ms, opacity 160ms;
}
.notebook-sp-hide:hover {
  opacity: 1;
  color: var(--accent-deep, #cc3b7c);
  background: rgba(255,255,255,0.5);
}
.notebook-sp-hide:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: 2px; opacity: 1; }
/* + add-note button, pushed to the right of the heading */
.notebook-sp-add {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  background: rgba(255,255,255,0.6);
  color: var(--notebook-icon-tint, #635c7e);
  cursor: pointer;
  font-size: 13px;
  transition: color 160ms, transform 160ms, background 160ms;
}
.notebook-sp-add:hover:not(:disabled) {
  color: var(--accent-deep, #cc3b7c);
  transform: translateY(-1px);
}
.notebook-sp-add:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: 2px; }
.notebook-sp-add:disabled { opacity: 0.35; cursor: not-allowed; }
/* Empty-state add affordance shown when there are zero notes */
.notebook-sticky-empty {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 28px 14px;
  border: 1px dashed var(--rule);
  border-radius: var(--radius-field, 13px);
  background: rgba(255,255,255,0.4);
  color: var(--ink-mute);
  font-size: 13px;
  cursor: pointer;
  transition: color 160ms, border-color 160ms, background 160ms;
}
.notebook-sticky-empty i { font-size: 20px; }
.notebook-sticky-empty:hover {
  color: var(--accent-deep, #cc3b7c);
  border-color: var(--accent-soft, color-mix(in oklab, var(--accent) 40%, transparent));
  background: rgba(255,255,255,0.6);
}
.notebook-sticky-empty:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: 2px; }
.notebook-rail-label {
  writing-mode: vertical-rl;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-top: 6px;
}
.notebook-rail-icon {
  color: var(--ink-soft);
  font-size: 16px;
  margin-top: auto;
  margin-bottom: 24px;
}

/* ── Stage 3: sticky notes ───────────────────────────────────────────────── */
.notebook-sticky {
  position: relative;
  background: var(--paper-soft);
  border: 1px solid var(--rule);
  border-radius: var(--radius-field, 13px);
  padding: 12px 14px 10px;
  margin-bottom: 14px;
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05));
  box-sizing: border-box;
}
/* Per-note delete — appears on hover/focus in the top-right corner */
.notebook-sticky__del {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: 50%; /* round white ghost button */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 1px solid var(--rule);
  background: rgba(255,255,255,0.7);
  color: var(--ink-soft);
  cursor: pointer;
  font-size: 12px;
  font-family: inherit;
  white-space: nowrap;
  opacity: 0;
  transition: opacity 140ms, color 140ms, background 140ms, border-color 140ms;
}
.notebook-sticky:hover .notebook-sticky__del,
.notebook-sticky:focus-within .notebook-sticky__del { opacity: 1; }
.notebook-sticky__del:hover { color: var(--ink); border-color: var(--ink-soft); }
.notebook-sticky__del:focus-visible { opacity: 1; outline: 2px solid var(--accent, #ec5f9b); outline-offset: 1px; }
/* Armed (2-click confirm) state — grows to a pill to fit "Delete?" */
.notebook-sticky__del--armed {
  opacity: 1;
  width: auto;
  padding: 0 10px;
  border-radius: 999px;
  background: linear-gradient(140deg, #f48bbb, #e0568f);
  border-color: transparent;
  color: #fff;
}
.notebook-sticky__textarea {
  width: 100%;
  min-height: 72px;
  background: transparent;
  border: none;
  outline: none;
  resize: none;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-soft);
  font-family: inherit;
  box-sizing: border-box;
}
.notebook-sticky__textarea::placeholder { color: var(--ink-faint); font-style: italic; }
/* #1 — no focus border/outline on sticky note textarea */
.notebook-sticky__textarea:focus,
.notebook-sticky__textarea:focus-visible { outline: none; box-shadow: none; }
/* A11y: the textarea suppresses its own ring; ring the sticky-note wrapper instead. */
.notebook-sticky:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── Stage 3: bottom dock bar ────────────────────────────────────────────── */
.notebook-dockbar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 9px 24px;
  width: 100%;
  position: sticky;
  bottom: 0;
  z-index: 10;
  background: rgba(255,255,255,0.5);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border-top: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  box-shadow: 0 -8px 30px -18px rgba(150,120,190,0.5),
              inset 0 1px 0 rgba(255,255,255,0.6);
  box-sizing: border-box;
}
.notebook-dockbar__words {
  font-size: 13px;
  color: var(--ink-soft);
  white-space: nowrap;
  margin-right: 2px;
}
.notebook-dockbar__words b {
  color: var(--ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.notebook-dockbar__sep {
  width: 1px;
  height: 22px;
  background: var(--rule);
  margin: 0 5px;
  flex-shrink: 0;
}
/* Twin spacers split the free space equally → three regions:
   LEFT (word-count/timer/goal) | CENTER (copy/download) | RIGHT (expand/font/focus/pressure/sep)
   Storyboard uses a single spacer with the same rule (left-stats | right-buttons) — unaffected. */
.notebook-dockbar__spacer { flex: 1; }

/* dock icon button */
.notebook-dock-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid transparent;
  background: transparent;
  color: var(--notebook-icon-tint);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  transition: background 180ms, color 180ms, transform 180ms;
  flex-shrink: 0;
  font-size: 17px;
  padding: 0;
}
.notebook-dock-btn:hover:not(:disabled) {
  background: rgba(255,255,255,0.65);
  color: var(--accent-deep, #cc3b7c);
  transform: translateY(-1px);
}
.notebook-dock-btn:focus-visible {
  outline: 2px solid var(--accent, #ec5f9b);
  outline-offset: 2px;
}
.notebook-dock-btn--on {
  color: var(--accent-deep, #cc3b7c);
  background: var(--accent-wash, #fce8f2);
  border-color: color-mix(in oklab, var(--accent, #ec5f9b) 22%, transparent);
}
/* Active/filled button — used by EditDock for save/submit kinds; also storyboard
   goal trigger when a goal is set. Solid accent fill with white label. */
.notebook-dock-btn--active {
  background: var(--accent, #ec5f9b);
  color: var(--paper, #fff);
  border-color: var(--accent-deep, #cc3b7c);
}
.notebook-dock-btn--active:hover:not(:disabled) {
  background: var(--accent-deep, #cc3b7c);
  color: var(--paper, #fff);
  transform: translateY(-1px);
}
/* Stub buttons (coming-soon N1b): slightly dimmer, no hover lift */
.notebook-dock-btn:disabled {
  opacity: 0.38;
  cursor: not-allowed;
  transform: none !important;
  pointer-events: none;
}

/* font toggle wrapper — positions the popout */
.notebook-dock-font-wrap {
  position: relative;
  display: inline-flex;
}
/* sound menu wrapper + active-dot indicator + volume slider (N1b) */
.notebook-dock-sound-wrap {
  position: relative;
  display: inline-flex;
}
/* No notification badge on dock settings — active state is shown by the
   button's --on fill only (owner: never a dot on the bottom toolbar). */
.notebook-sound-vol {
  -webkit-appearance: none;
  appearance: none;
  width: 118px;
  height: 14px; /* tall enough to host the 14px thumb without clipping */
  background: transparent;
  outline: none;
  cursor: pointer;
}
/* Visible track — drawn via pseudo-elements so it shows on the frosted menu
   (the bare --rule color washed out against the glass). */
.notebook-sound-vol::-webkit-slider-runnable-track {
  height: 5px;
  border-radius: 999px;
  background: rgba(150,120,190,0.45);
}
.notebook-sound-vol::-moz-range-track {
  height: 5px;
  border-radius: 999px;
  background: rgba(150,120,190,0.45);
}
.notebook-sound-vol::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -4.5px; /* center the 14px thumb on the 5px track */
  border-radius: 50%;
  background: var(--accent, #ec5f9b);
  border: 2px solid #fff;
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05));
}
.notebook-sound-vol::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent, #ec5f9b);
  border: 2px solid #fff;
}
.notebook-sound-vol:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: 4px; }

/* Text-size A−/A+ stepper (font popout) */
.notebook-size-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 11px 4px;
}
.notebook-size-row__label { font-size: 13px; font-weight: 600; color: var(--ink); }
.notebook-size-step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.notebook-size-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  background: rgba(255,255,255,0.6);
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 160ms, border-color 160ms, background 160ms;
}
.notebook-size-btn:hover:not(:disabled) { color: var(--accent-deep, #cc3b7c); border-color: var(--accent-soft, #f9cfe2); }
.notebook-size-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.notebook-size-btn:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: 2px; }
.notebook-size-btn__a { font-family: var(--font-display, inherit); font-weight: 600; line-height: 1; }
.notebook-size-btn__a--sm { font-size: 12px; }
.notebook-size-btn__a--lg { font-size: 17px; }
.notebook-size-val {
  min-width: 22px;
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* Text-color picker (font popout) */
.notebook-color-opts {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.notebook-color-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--rule-soft, rgba(0,0,0,0.08));
  background: transparent;
  cursor: pointer;
  display: grid;
  place-items: center;
  font-size: 16px;
  padding: 0;
  transition: color 160ms, border-color 160ms, background 160ms;
}
.notebook-color-btn:hover { border-color: var(--accent-soft, #f9cfe2); }
.notebook-color-btn--on {
  background: var(--accent-wash, #fce8f2);
  border-color: color-mix(in oklab, var(--accent, #ec5f9b) 22%, transparent);
  box-shadow: 0 0 0 1px var(--accent-soft, #f9cfe2);
}
.notebook-color-btn:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: 2px; }

/* fade + sprint menu wrappers */
.notebook-dock-fade-wrap,
.notebook-dock-sprint-wrap,
.notebook-dock-focus-wrap { position: relative; display: inline-flex; }
.notebook-focus-hint {
  padding: 8px 11px 4px;
  font-size: 11.5px;
  color: var(--ink-mute);
}
.notebook-focus-hint kbd {
  font-family: inherit;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--ink-soft);
  background: rgba(255,255,255,0.6);
  border: 1px solid var(--rule);
  border-radius: 5px;
  padding: 1px 5px;
}
.notebook-sprint-arm { width: 100%; justify-content: center; margin-top: 2px; }
.notebook-sprint-hint { padding: 6px 6px 2px; }
.notebook-fade-delay { display: inline-flex; align-items: center; gap: 8px; }
.notebook-fade-delay__val {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  min-width: 22px;
}

/* ── Stage 3: font popout menu ───────────────────────────────────────────── */
.notebook-dock-menu {
  position: absolute;
  bottom: calc(100% + 10px);
  right: 0;
  width: 232px;
  /* Opaque — a parent backdrop-filter (.notebook-dockbar) clips child
     backdrop-filters, so the glass blur would not paint here and the popover
     would read through as transparent. Solid white keeps popovers readable. */
  background: rgba(255,255,255,0.97);
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  border-radius: 18px;
  box-shadow: var(--shadow-card, 0 2px 6px rgba(120,104,170,0.07), 0 20px 46px rgba(150,120,190,0.15)),
              var(--gloss, inset 0 1.5px 0 rgba(255,255,255,0.95));
  padding: 8px;
  z-index: 20;
}
.notebook-dock-menu__arrow {
  position: absolute;
  bottom: -7px;
  right: 12px;
  width: 14px;
  height: 14px;
  background: rgba(255,255,255,0.97);
  border-right: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  border-bottom: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  transform: rotate(45deg);
}
.notebook-dock-menu__head {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding: 7px 11px 9px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.notebook-dock-menu__head i { color: var(--ink-soft); font-size: 14px; }
.notebook-dock-menu__item {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 9px 11px;
  border-radius: 12px;
  cursor: pointer;
  font-size: 13px;
  color: var(--ink-soft);
  background: transparent;
  border: none;
  font-family: inherit;
  text-align: left;
  transition: background 150ms, color 150ms;
}
.notebook-dock-menu__item:hover { background: var(--accent-wash, #fce8f2); color: var(--accent-deep, #cc3b7c); }
.notebook-dock-menu__item--on   { color: var(--accent-deep, #cc3b7c); }
.notebook-dock-menu__item-main  { flex: 1; display: flex; flex-direction: column; }
.notebook-dock-menu__item-sub   { font-size: 11px; color: var(--ink-mute); margin-top: 1px; font-weight: 400; }
.notebook-dock-menu__item-tick  { color: var(--accent, #ec5f9b); font-size: 16px; flex-shrink: 0; }
.notebook-dock-menu__item:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: -2px; border-radius: 12px; }

/* ── Stage 4: export popover wrap + icon badges ──────────────────────────── */
.notebook-dock-export-wrap {
  position: relative;
  display: inline-flex;
}

/* Settings gear wrap — mirrors the font/fade/focus wrap pattern */
.notebook-dock-settings-wrap {
  position: relative;
  display: inline-flex;
}

/* Goal+Timer combined popover: left-anchored like the goal popover.
   Timer slot is displayed in a flex row centered in its section. */
.notebook-goal-timer-pop {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 0;
  max-width: min(280px, calc(100vw - 32px));
  /* Allow the popover to scroll on very short screens */
  max-height: calc(100svh - 80px);
  overflow-y: auto;
}
.notebook-goal-timer-pop .notebook-dock-menu__arrow {
  right: auto;
  left: 12px;
}
/* Timer slot row — center the timer chip inside the popover */
.notebook-goal-timer-pop__timer {
  display: flex;
  justify-content: center;
  padding: 6px 8px 10px;
}
/* The NotebookGoalPopover rendered inside the combined popover must not add
   its own absolute positioning or arrow — it renders as a plain content block.
   Use a chained selector to beat the (0,2,0) specificity of
   .notebook-dock-goal-wrap .notebook-goal-pop further down in this file. */
.notebook-goal-timer-pop.notebook-dock-menu .notebook-goal-pop {
  position: static;
  bottom: auto;
  left: auto;
  max-width: none;
  width: 100%;
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
  padding: 0;
}
.notebook-goal-timer-pop.notebook-dock-menu .notebook-goal-pop > .notebook-dock-menu__arrow {
  display: none;
}

/* Settings gear popover: right-anchored (default .notebook-dock-menu position).
   Wider to accommodate font + controls comfortably.
   Allow vertical scroll on short viewports. */
.notebook-settings-pop {
  position: absolute;
  bottom: calc(100% + 10px);
  right: 0;
  width: min(260px, calc(100vw - 32px));
  max-height: calc(100svh - 80px);
  overflow-y: auto;
}

/* TXT / MD icon badges — monospace pill inside the export menu items */
.notebook-export-menu__ico {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  letter-spacing: 0.02em;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  /* TXT: cool periwinkle gradient — matches the mockup */
  background: linear-gradient(140deg, #aeb8f0, #d4b9ec);
}
.notebook-export-menu__ico--md {
  /* MD: warm pink gradient — matches the mockup */
  background: linear-gradient(140deg, #f6a6c8, #ec6ba2);
}

/* ── Stage 5: goal popover ───────────────────────────────────────────────── */
.notebook-goal-pop {
  width: 264px;
}
.notebook-goal-presets {
  display: flex;
  gap: 7px;
  padding: 6px 8px 2px;
}
.notebook-goal-chip {
  flex: 1;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  background: rgba(255,255,255,0.45);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 8px 0;
  cursor: pointer;
  transition: color 160ms, border-color 160ms, background 160ms;
  font-variant-numeric: tabular-nums;
}
.notebook-goal-chip:hover { color: var(--accent-deep, #cc3b7c); border-color: var(--accent-soft, #f9cfe2); }
.notebook-goal-chip--on {
  background: linear-gradient(140deg, #f48bbb, #ec6ba2 55%, #e0568f);
  color: #fff;
  border-color: transparent;
  box-shadow: 0 4px 12px -6px rgba(236,95,155,0.7);
}
.notebook-goal-field {
  display: flex;
  align-items: center;
  margin: 11px 8px 2px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: rgba(255,255,255,0.5);
  overflow: hidden;
}
.notebook-gf-step {
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  border: none;
  background: transparent;
  color: var(--ink-soft);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background 160ms, color 160ms;
}
.notebook-gf-step:hover { color: var(--accent-deep, #cc3b7c); background: rgba(255,255,255,0.7); }
.notebook-gf-val {
  flex: 1;
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: 7px;
}
.notebook-gf-num {
  font-family: var(--font-display, inherit);
  font-weight: 600;
  font-size: 23px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.notebook-gf-unit {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.notebook-goal-hint {
  text-align: center;
  font-size: 11.5px;
  color: var(--ink-mute);
  padding: 3px 0 1px;
}
.notebook-goal-prog {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 11.5px;
  color: var(--ink-mute);
  padding: 2px 11px 8px;
}
.notebook-goal-prog strong { color: var(--ink-soft); font-weight: 600; }
.notebook-goal-bar-wrap {
  position: relative;
  width: 60px;
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
  flex-shrink: 0;
  background: rgba(174,184,240,0.3);
}
.notebook-goal-bar-wrap .notebook-goal-bar-fill {
  position: absolute;
  inset: 0 auto 0 0;
  height: 100%;
  border-radius: 999px;
  transition: width 600ms ease;
}
.notebook-dock-menu__divider {
  height: 1px;
  background: var(--rule-soft);
  margin: 5px 8px;
}
.notebook-dock-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 11px;
}
.notebook-dock-row__label { font-size: 13px; font-weight: 600; color: var(--ink); }
.notebook-dock-row__desc  { font-size: 11px; color: var(--ink-mute); margin-top: 1px; line-height: 1.4; }
/* toggle switch — shared structural base; skin (on-gradient, dark thumb) lives in theme.css */
.toggle-switch {
  width: 46px;
  height: 27px;
  border-radius: 999px;
  background: var(--rule);
  position: relative;
  cursor: pointer;
  flex: 0 0 auto;
  transition: background 200ms;
  border: none;
  padding: 0;
  /* focus ring */
  outline-offset: 2px;
}
.toggle-switch:focus-visible {
  outline: 2px solid var(--accent, #ec5f9b);
}
.toggle-switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05));
  transition: left 220ms cubic-bezier(0.2,0.9,0.3,1.3);
}
.toggle-switch--on { background: linear-gradient(140deg, #f48bbb, #e0568f); }
.toggle-switch--on::after { left: 22px; }
.toggle-switch--disabled { opacity: 0.45; cursor: not-allowed; }

/* .notebook-switch — alias kept so existing notebook/storyboard usage keeps working */
.notebook-switch {
  width: 46px;
  height: 27px;
  border-radius: 999px;
  background: var(--rule);
  position: relative;
  cursor: pointer;
  flex: 0 0 auto;
  transition: background 200ms;
  border: none;
}
.notebook-switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05));
  transition: left 220ms cubic-bezier(0.2,0.9,0.3,1.3);
}
.notebook-switch--on { background: linear-gradient(140deg, #f48bbb, #e0568f); }
.notebook-switch--on::after { left: 22px; }


.notebook-goal-actions {
  display: flex;
  gap: 8px;
  padding: 10px 8px 4px;
}
.notebook-goal-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  border-radius: 999px;
  padding: 9px 14px;
  cursor: pointer;
  transition: opacity 160ms, transform 160ms;
  border: 1px solid transparent;
}
.notebook-goal-btn--ghost {
  background: rgba(255,255,255,0.32);
  border-color: var(--rule);
  color: var(--ink-soft);
}
.notebook-goal-btn--ghost:hover { color: var(--ink); border-color: var(--ink-soft); }
.notebook-goal-btn--accent {
  background: linear-gradient(140deg, #f48bbb, #ec6ba2 55%, #e0568f);
  color: #fff;
  box-shadow: 0 6px 18px -8px rgba(236,95,155,0.6);
}
.notebook-goal-btn--accent:hover { opacity: 0.9; }

/* ── Stage 5: goal trigger in the dock bar ──────────────────────────────── */
.notebook-dock-goal-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.notebook-goal-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
}
.notebook-goal-trigger__icon {
  color: var(--notebook-icon-tint, #635c7e);
  font-size: 16px;
  line-height: 1;
  transition: color 160ms;
}
.notebook-goal-trigger__icon--done { color: var(--ok, #1f9e6e); }
.notebook-goal-trigger__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mute);
  transition: color 160ms;
  white-space: nowrap;
}
.notebook-goal-trigger:hover .notebook-goal-trigger__icon,
.notebook-goal-trigger:hover .notebook-goal-trigger__label {
  color: var(--accent-deep, #cc3b7c);
}
.notebook-dock-goal-wrap--done .notebook-goal-trigger:hover .notebook-goal-trigger__icon {
  color: var(--ok, #1f9e6e);
}
.notebook-goal-bar-outer {
  display: inline-flex;
  align-items: center;
  width: 132px;
}
/* The slim inline fill bar */
.notebook-goal-bar {
  position: relative;
  height: 8px;
  width: 100%;
  border-radius: 999px;
  overflow: hidden;
  /* Track: faint gradient of the fill palette */
  background: linear-gradient(90deg,rgba(174,184,240,0.35) 0%,rgba(212,185,236,0.35) 45%,rgba(246,166,200,0.35) 100%);
}
.notebook-goal-bar-fill {
  position: absolute;
  inset: 0 auto 0 0;
  height: 100%;
  border-radius: 999px;
  transition: width 500ms ease;
}
/* No shimmer/flash — the fill simply grows, then turns green at 100% (the
   --done gradient lives in the theme skin, bubble.css). */
/* Goal pct text next to the bar */
.notebook-goal-pct {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-deep, #cc3b7c);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.notebook-goal-pct--done { color: var(--ok, #1f9e6e); }
/* Goal popover wrapper needs relative positioning */
.notebook-dock-goal-wrap .notebook-goal-pop {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 0;
  /* Clamp width so it doesn't clip off-screen at narrow viewports */
  max-width: min(264px, calc(100vw - 32px));
}
/* Arrow: re-anchor to left edge to sit under the trigger button */
.notebook-dock-goal-wrap .notebook-dock-menu__arrow {
  right: auto;
  left: 12px;
}

/* ── Stage 5: pomodoro ───────────────────────────────────────────────────── */
.notebook-pomo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--glass-fill, rgba(255,255,255,0.55));
  -webkit-backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  border-radius: 999px;
  padding: 5px 10px 5px 6px;
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05)),
              var(--gloss, inset 0 1.5px 0 rgba(255,255,255,0.95));
}
.notebook-pomo-tomato {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(255,255,255,0.6);
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  color: var(--notebook-icon-tint, #635c7e);
  font-size: 14px;
  box-shadow: var(--gloss, inset 0 1.5px 0 rgba(255,255,255,0.95));
  cursor: pointer;
  transition: color 160ms;
}
.notebook-pomo-tomato:hover { color: var(--accent-deep, #cc3b7c); }
.notebook-pomo-time {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.notebook-pomo-lab {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.notebook-pomo-ctrl {
  width: 28px !important;
  height: 28px !important;
  font-size: 14px !important;
}
/* Collapsed resting chip — icon + "Timer" label (or live countdown when running).
   Matches the dock "Set a goal" trigger so the dock reads as one flat row. */
.notebook-pomo-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font-family: inherit;
  color: inherit;
}
.notebook-pomo-chip__icon {
  color: var(--notebook-icon-tint, #635c7e);
  font-size: 16px;
  line-height: 1;
  transition: color 160ms;
}
.notebook-pomo-chip__label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mute);
  white-space: nowrap;
  transition: color 160ms;
}
.notebook-pomo-chip__time {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.notebook-pomo-chip:hover .notebook-pomo-chip__icon,
.notebook-pomo-chip:hover .notebook-pomo-chip__label { color: var(--accent-deep, #cc3b7c); }
.notebook-pomo-chip--done .notebook-pomo-chip__time { color: var(--ok, #1f9e6e); }

/* ── N1b: timer config popover (left-anchored) + stepper rows ──────────────── */
.notebook-timer-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.notebook-timer-pop {
  left: 0;
  right: auto;
  width: 250px;
}
.notebook-timer-pop .notebook-dock-menu__arrow {
  left: 18px;
  right: auto;
}
.notebook-timer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 6px;
}
.notebook-timer-row__label {
  font-size: 13px;
  color: var(--ink-soft);
}
.notebook-timer-stepper {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.notebook-timer-stepper__val {
  min-width: 56px;
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.notebook-timer-stepper__val small {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-left: 3px;
}

/* Collapsed mini chip */
.notebook-pomo-mini {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--glass-fill, rgba(255,255,255,0.55));
  -webkit-backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05)),
              var(--gloss, inset 0 1.5px 0 rgba(255,255,255,0.95));
  color: var(--notebook-icon-tint, #635c7e);
  font-size: 16px;
  cursor: pointer;
  transition: color 160ms, transform 160ms;
}
.notebook-pomo-mini:hover { color: var(--accent-deep, #cc3b7c); transform: translateY(-1px); }
.notebook-pomo-mini--done { color: var(--ok, #1f9e6e); border-color: color-mix(in oklab, var(--ok, #1f9e6e) 35%, transparent); }

/* ── Stage 5: fullscreen mode ────────────────────────────────────────────── */
/* Sit above .app-topbar (z-index: 50) so the page covers all chrome */
.notebook-page--fullscreen {
  position: fixed;
  inset: 0;
  z-index: 51;
  overflow: auto;
}
.notebook-page--fullscreen .notebook-sidepanel,
.notebook-page--fullscreen .notebook-side-rail,
.notebook-page--fullscreen .notebook-dockbar {
  display: none !important;
}
.notebook-page--fullscreen .notebook-main {
  grid-template-columns: 1fr;
}
/* P1: In expanded (fullscreen) view the writecol is the only column; cap it
   so the panel stays at the contained width and doesn't span a giant viewport. */
.notebook-page--fullscreen .notebook-main .notebook-writecol {
  max-width: var(--page-w-contained);
  width: 100%;
  margin-left: auto;
  margin-right: auto;
}
/* Floating HUD positioned top corners — above the fullscreen page layer */
.notebook-fs-hud {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 24px;
  z-index: 52;
  pointer-events: none;
}
.notebook-fs-exit {
  pointer-events: auto;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  background: var(--glass-fill, rgba(255,255,255,0.55));
  -webkit-backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05)),
              var(--gloss, inset 0 1.5px 0 rgba(255,255,255,0.95));
  color: var(--ink-soft);
  font-size: 18px;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: color 160ms, transform 160ms;
}
.notebook-fs-exit:hover { color: var(--accent-deep, #cc3b7c); transform: translateY(-1px); }
.notebook-fs-badge {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--glass-fill, rgba(255,255,255,0.55));
  -webkit-backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  backdrop-filter: var(--glass-blur, blur(18px) saturate(1.35));
  border: 1px solid var(--glass-stroke, rgba(255,255,255,0.75));
  border-radius: 999px;
  padding: 8px 18px;
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05)),
              var(--gloss, inset 0 1.5px 0 rgba(255,255,255,0.95));
}
.notebook-fs-badge__num {
  font-family: var(--font-display, inherit);
  font-weight: 600;
  font-size: 17px;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.notebook-fs-badge__lab {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.notebook-fs-badge__pct {
  color: var(--accent-deep, #cc3b7c);
  font-weight: 600;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.notebook-fs-badge__pct--done { color: var(--ok, #1f9e6e); }

/* ── Stage 3: responsive — auto-collapse side panel + dock adjustments ───── */
@media (max-width: 767px) {
  .notebook-main,
  .notebook-main--no-panel {
    grid-template-columns: minmax(0, 1fr) 46px;
  }
  .notebook-dockbar { padding: 9px 14px; gap: 6px; }
  .notebook-writecol .notebook-panel {
    padding: 30px 22px;
    max-width: 100%;
  }
}

/* ── NB-Side: Source panel tab strip + source pane (P10) ────────────────── */
/* Tab strip — pinned to the bottom of the flex sidepanel, read as a deliberate
   two-segment control. The strip is at the BOTTOM, so the active indicator sits
   on the TOP edge (the edge nearest the panel content it controls), backed by a
   clear frosted fill. */
.notebook-sp-tabs {
  display: flex;
  gap: 0;
  margin-top: auto;
  /* break out of the sidepanel's 24px/28px gutters so the strip spans full width */
  margin-left: -28px;
  margin-right: -24px;
  margin-bottom: 0;
  border-top: 1px solid var(--rule-soft);
  background: rgba(255,255,255,0.28);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.notebook-sp-tab {
  flex: 1;
  padding: 11px 10px 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: none;
  cursor: pointer;
  background: transparent;
  color: var(--ink-mute);
  /* top edge carries the active indicator; transparent until active */
  border-top: 2px solid transparent;
  margin-top: -1px; /* overlap the strip's border-top so the indicator replaces it */
  transition: color 140ms, border-color 140ms, background 140ms;
}
/* divider between the two segments */
.notebook-sp-tab + .notebook-sp-tab { box-shadow: inset 1px 0 0 var(--rule-soft); }
.notebook-sp-tab:hover {
  color: var(--ink);
  background: rgba(255,255,255,0.4);
}
.notebook-sp-tab--active {
  color: var(--accent, #ec5f9b);
  border-top-color: var(--accent, #ec5f9b);
  background: rgba(255,255,255,0.5);
}
/* keep the divider readable when a segment is active (active fill would hide it) */
.notebook-sp-tab--active + .notebook-sp-tab,
.notebook-sp-tab--active { box-shadow: inset 1px 0 0 var(--rule-soft); }
.notebook-sp-tab:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: -2px; }

/* ── #46 — sb-colstrip collapse strip for the notebook side panel ─────────── */
/* The rail when collapsed: mirrors .notebook-side-rail but uses the sb-colstrip
   full-height slim-strip pattern instead of the old caret-tab. */
.notebook-side-rail--strip {
  align-self: stretch;
  width: 40px;
  border-left: 1px solid var(--rule-soft);
  background: rgba(255,255,255,0.20);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  position: relative;
  box-sizing: border-box;
  margin-bottom: -56px;
  padding-bottom: 56px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
/* Full-height slim collapse strip — shared base, mirrors .sb-colstrip */
.notebook-sp-colstrip {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  width: 100%;
  height: 100%;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--ink-faint);
  font-size: 13px;
  padding: 14px 0 0;
  transition: color 160ms, background 160ms;
}
.notebook-sp-colstrip:hover { color: var(--accent-deep, #cc3b7c); background: color-mix(in oklab, var(--accent) 10%, transparent); }
.notebook-sp-colstrip:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: -2px; }
/* Hide strip: absolute, on the inner left edge of the open panel */
.notebook-sp-colstrip--hide {
  position: absolute;
  top: 0; left: 0;
  width: 16px;
  height: 100%;
  z-index: 4;
  padding: 14px 0 0;
}

/* ── #45 — source-selector pill row in the panel header ─────────────────────*/
/* Head row when it hosts the selector pill instead of plain text. */
.notebook-sp-head--selector {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 14px;
  padding-top: 0;
}
/* The PillPopover wrap inside the head fills available space */
.notebook-sp-head--selector .dir-pill-wrap { flex: 1; min-width: 0; }
.notebook-sp-head--selector .dir-pill-wrap .dir-foot-btn { width: 100%; justify-content: flex-start; }

/* ── #47 — storyboard-style empty state for zero stickies ───────────────────*/
.notebook-sp-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 14px;
  padding: 40px 16px;
  min-height: 220px;
}
.notebook-sp-empty__art {
  width: 52px;
  height: 52px;
  border-radius: 16px;
  display: grid;
  place-items: center;
  background: var(--bub-grad-2, linear-gradient(150deg,#cdd6f8,#e6d4f1 40%,#f9d2e0 72%,#fde4d3));
  color: rgba(54,48,78,0.5);
  font-size: 24px;
  box-shadow: var(--shadow-card), var(--gloss);
}
body.dark-mode .notebook-sp-empty__art {
  background: linear-gradient(150deg,#303873,#43306a 40%,#5e3360 72%,#6e3a5a);
  color: rgba(236,232,248,0.7);
}
.notebook-sp-empty__title {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 18px;
  color: var(--ink);
}
.notebook-sp-empty__text {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-mute);
  max-width: 200px;
  line-height: 1.5;
}
.notebook-sp-empty__actions { display: inline-flex; gap: 8px; margin-top: 2px; }

/* Sources pane fills remaining height in the flex sidepanel.
   Cards/sections below borrow the sticky-note treatment (--paper-soft fill,
   --rule border, --radius-field corners, --shadow-soft) so the two tabs read as
   one coherent panel. All token-driven for dark-mode safety. */
.notebook-source { display: flex; flex-direction: column; gap: 12px; flex: 1; overflow-y: auto; padding-bottom: 8px; }
/* Scene / Character toggle — a small segmented control matching the bottom tabs */
.notebook-source__kind { display: flex; gap: 0; border: 1px solid var(--rule-soft);
  border-radius: var(--radius-field, 13px); overflow: hidden; }
.notebook-source__kindbtn { flex: 1; padding: 6px 8px; font-size: 11px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; border: none; font-family: inherit;
  background: transparent; cursor: pointer; color: var(--ink-mute);
  transition: color 140ms, background 140ms; }
.notebook-source__kindbtn + .notebook-source__kindbtn { box-shadow: inset 1px 0 0 var(--rule-soft); }
.notebook-source__kindbtn:hover { color: var(--ink); background: rgba(255,255,255,0.4); }
.notebook-source__kindbtn.is-active { background: var(--accent); color: #fff; box-shadow: none; }
.notebook-source__kindbtn:focus-visible { outline: 2px solid var(--accent, #ec5f9b); outline-offset: -2px; }
.notebook-source__body { display: flex; flex-direction: column; gap: 10px; }
.notebook-source__picker { width: 100%; padding: 8px 10px; font-size: 12px; font-family: inherit;
  color: var(--ink); border: 1px solid var(--rule); border-radius: var(--radius-field, 13px);
  background: var(--pg-well); cursor: pointer; transition: border-color 140ms; }
.notebook-source__picker:hover { border-color: var(--accent-soft); }
/* #3/#4 — no focus border on source pickers */
.notebook-source__picker:focus,
.notebook-source__picker:focus-visible { outline: none; box-shadow: none; }
/* A11y: this select suppresses its own ring at higher specificity than the global
   baseline; restore a keyboard-focus ring (later-wins override; Fragile #7). */
.notebook-source__picker:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Character-ideas panel — no outer card; header bare on panel bg */
.notebook-source__charideas { display: flex; flex-direction: column; gap: 8px; }
/* Picked-source detail — a sticky-style card */
.notebook-source__detail { display: flex; flex-direction: column; gap: 8px;
  background: var(--paper-soft); border: 1px solid var(--rule);
  border-radius: var(--radius-field, 13px); padding: 12px 14px 10px;
  box-shadow: var(--shadow-soft, 0 1px 2px rgba(120,104,170,0.05)); }
.notebook-source__meta { display: flex; flex-wrap: wrap; gap: 5px; margin: 0; }
.notebook-source__chip { font-size: 10px; padding: 3px 8px; border-radius: 999px;
  background: color-mix(in oklab, var(--ink-mute) 12%, transparent);
  border: 1px solid var(--rule-soft); color: var(--ink-mute); }
.notebook-source__section { margin-top: 0; }
.notebook-source__section + .notebook-source__section { margin-top: 4px; }
.notebook-source__label { font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .08em; color: var(--ink-mute); margin-bottom: 3px; }
.notebook-source__prose { font-size: 13px; line-height: 1.55; white-space: pre-wrap; color: var(--ink-soft); }
.notebook-source__charhead { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.notebook-source__icon { width: 28px; height: 28px; border-radius: 50%; object-fit: cover;
  border: 1px solid var(--rule-soft); flex-shrink: 0; }
.notebook-source__charname { font-weight: 600; font-size: 13px; color: var(--ink); }
.notebook-source__empty { color: var(--ink-mute); font-style: italic; font-size: 12px; padding: 12px 0; text-align: center; }

/* ============================================================
   ACTIVITY DRAWER (Stage 20.6)
   ============================================================ */
.activity-drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(0,0,0,0.32);
}
.activity-drawer {
  position: fixed;
  right: 0;
  top: 0;
  height: 100vh;
  width: 360px;
  max-width: 100vw;
  background: var(--paper);
  border-left: 1px solid var(--rule);
  box-shadow: -16px 0 36px rgba(20,15,10,0.10);
  z-index: 100;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: activityDrawerIn 200ms cubic-bezier(.2,.7,.3,1);
}
@keyframes activityDrawerIn {
  from { transform: translateX(100%); }
  to   { transform: none; }
}
@media (max-width: 640px) {
  /* Phase 18: 100vw includes the scrollbar gutter → horizontal overflow.
     Use 100% of the fixed-position containing block, capped so the panel
     never exceeds the visible width even when a scrollbar is present. */
  .activity-drawer { width: 100%; max-width: 100vw; }
}
.activity-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px 14px;
  border-bottom: 1px solid var(--rule-soft);
  flex-shrink: 0;
}
.activity-drawer-header h2 {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 22px;
  font-weight: 500;
  color: var(--ink);
}
.activity-drawer-close {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--ink-mute);
  padding: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 140ms ease;
}
.activity-drawer-close:hover { color: var(--ink); }
.activity-drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0;
}
.activity-drawer-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 20px;
  cursor: pointer;
  background: none;
  width: 100%;
  text-align: left;
  transition: background 140ms ease;
}
.activity-drawer-row:hover,
.activity-drawer-row:focus-visible {
  background: var(--paper-warm);
  outline: none;
}
.activity-drawer-row--unread {
  background: var(--accent-wash);
}
.activity-drawer-row-icon {
  flex-shrink: 0;
  color: var(--ink-mute);
  padding-top: 2px;
}
.activity-drawer-row-content {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.activity-drawer-row-heading {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.activity-drawer-row-detail {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 400;
  color: var(--ink-mute);
}
.activity-drawer-row-time {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 2px;
}
.activity-drawer-footer {
  flex-shrink: 0;
  border-top: 1px solid var(--rule-soft);
  padding: 12px 20px;
}
.activity-drawer-footer .activity-drawer-clear {
  /* Layout: stretch full-width inside the footer. Appearance is via dir-foot-btn + dir-pill-btn. */
  width: 100%;
  flex: none;
}
.activity-drawer-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 48px 20px;
  color: var(--ink-faint);
  text-align: center;
}
.activity-drawer-empty p {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 16px;
}
.activity-drawer-loading,
.activity-drawer-error {
  padding: 24px 20px;
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 15px;
  color: var(--ink-mute);
}

/* ── Stretch #10: Link requests section (inside activity drawer) ── */
.link-requests-section {
  padding: 4px 0 0;
}
.link-requests-section-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 20px 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.link-request-row {
  /* Inherits .activity-drawer-row layout; not a hover-nav row, so no cursor pointer */
  cursor: default;
  align-items: flex-start;
}
.link-request-row:hover,
.link-request-row:focus-visible {
  background: none;
}
.link-request-row-actions {
  display: flex;
  gap: 10px;
  margin-top: 6px;
}
.link-request-btn {
  font-size: 11px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.link-request-btn--accept {
  color: var(--accent);
}
.link-request-btn--decline {
  color: var(--ink-mute);
}
.link-request-btn:disabled {
  opacity: 0.45;
  pointer-events: none;
}
.link-request-row-error {
  color: var(--danger, #c0392b);
}
.link-requests-section-divider {
  height: 1px;
  background: var(--rule-soft);
  margin: 6px 20px 8px;
}

/* ── Comms templates (Phase 17) ── */
/* Structure only (D-053): layout, sizing, shape, typography sizes, transforms.
   Palette (background / border / shadow / text color) lives in island.css and
   academia.css, keyed on body.theme-* selectors. */

/* Shared — applies to every comms template render component. */
.comms-template {
  width: 100%;
  max-width: 430px;
  margin: 0 auto;
}
.comms-template.comms--letter,
.comms-template.comms--journal { max-width: 600px; }
/* I-306: default prose keeps full post width — the comms wrapper is only a CSS scope. */
.comms-template.comms--default { max-width: none; margin: 0; }
.comms-template .comms--portrait {
  position: relative;
  flex-shrink: 0;
  z-index: 1;
}
/* Glow halo behind the portrait — structure here; gradient color is per-theme. */
.comms-template .comms--portrait::before {
  content: "";
  position: absolute;
  inset: -12px;
  border-radius: 50%;
  z-index: 0;
  filter: blur(11px);
}
.comms-template .comms--portrait-ring {
  position: relative;
  z-index: 1;
  border-radius: 50%;
  overflow: hidden;
  display: grid;
  place-items: center;
}
.comms-template .comms--portrait-ring img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.comms-template .comms--portrait-fill {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.06em;
}
.comms-template .comms--name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  line-height: 1.25;
}
.comms-template .comms--sublabel {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: lowercase;
}
.comms-template .comms--action-text {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 12.5px;
  line-height: 1.7;
  letter-spacing: 0.01em;
}

/* Call template — structure. */
.comms-template.comms--call {
  /* variant marker; frame layout + all palette live below / in theme files */
}
.comms-template .comms--call-frame {
  position: relative;
  width: 430px;
  max-width: 100%;
  border-radius: 34px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.comms-template .comms--call-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 24px 26px 20px;
}
.comms-template .comms--call-head .comms--portrait-ring {
  width: 58px;
  height: 58px;
}
.comms-template .comms--call-head-name {
  text-align: right;
}
.comms-template .comms--call-head-name .comms--name {
  font-size: 25px;
}
.comms-template .comms--call-stage {
  padding: 40px 30px 18px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.comms-template .comms--call-ring {
  position: relative;
  width: 92px;
  height: 92px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  margin-bottom: 22px;
}
.comms-template .comms--call-ring i {
  font-size: 38px;
  line-height: 1;
}
/* gentle pulsing halo behind the call ring; halo color is set per-theme. */
.comms-template .comms--call-ring::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  z-index: -1;
  animation: commsCallPulse 2.6s ease-out infinite;
}
@keyframes commsCallPulse {
  0%   { box-shadow: 0 0 0 0 currentColor; opacity: 0.5; }
  100% { box-shadow: 0 0 0 26px transparent; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .comms-template .comms--call-ring::after { animation: none; }
}
.comms-template .comms--call-status {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 40px;
  line-height: 1;
  letter-spacing: -0.01em;
}
.comms-template .comms--call-status-dots {
  opacity: 0.5;
}
.comms-template .comms--call-elapsed {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  margin-top: 10px;
}
.comms-template .comms--call-bubble {
  position: relative;
  align-self: flex-end;
  max-width: 88%;
  margin: 30px 4px 8px;
  padding: 11px 15px;
  border-radius: 18px 18px 5px 18px;
  text-align: left;
}
.comms-template .comms--call-bubble-bq {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 19.5px;
  line-height: 1.5;
  text-wrap: pretty;
}

.comms-template .comms--call-controls {
  margin-top: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 34px;
  padding: 24px 30px 30px;
}
.comms-template .comms--call-btn {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: none;
  cursor: default;
}
.comms-template .comms--call-btn i {
  font-size: 23px;
  line-height: 1;
}
.comms-template .comms--call-btn.is-end {
  width: 64px;
  height: 64px;
}
.comms-template .comms--call-btn.is-end i {
  font-size: 27px;
}
.comms-template .comms--call-ctl {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.comms-template .comms--call-ctl-cap {
  font-family: var(--font-mono);
  font-size: 8.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-top: 9px;
  text-align: center;
}

/* ── Message ── */
/* Structure only (D-053). Shared classes (.comms--portrait*, .comms--name) are
   authored once above — not repeated here. Palette lives in the theme files. */
.comms-template.comms--message {
  /* variant marker; frame layout + palette live below / in theme files */
}
.comms-template .comms--message-frame {
  position: relative;
  width: 430px;
  max-width: 100%;
  border-radius: 34px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.comms-template .comms--message-statusbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 24px 0;
  font-size: 14px;
}
.comms-template .comms--message-statusbar-clock {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
}
.comms-template .comms--message-statusbar-right {
  display: flex;
  align-items: center;
  gap: 7px;
}
.comms-template .comms--message-statusbar-right i {
  font-size: 15px;
  line-height: 1;
}
.comms-template .comms--message-contact {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 14px 24px 16px;
}
.comms-template .comms--message-contact .comms--portrait-ring {
  width: 46px;
  height: 46px;
}
.comms-template .comms--message-contact-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}
.comms-template .comms--message-contact-meta .comms--name {
  font-size: 21px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.comms-template .comms--message-contact-status {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.comms-template .comms--message-online-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
}
.comms-template .comms--message-thread {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px 22px 6px;
  min-height: 120px;
}
.comms-template .comms--message-row {
  display: flex;
  flex-direction: column;
  max-width: 78%;
  align-self: flex-start;
  align-items: flex-start;
}
.comms-template .comms--message-row.is-right {
  align-self: flex-end;
  align-items: flex-end;
}
.comms-template .comms--message-bubble {
  position: relative;
  padding: 11px 15px;
  font-size: 14.5px;
  line-height: 1.5;
}
.comms-template .comms--message-row.is-left .comms--message-bubble {
  border-radius: 18px 18px 18px 5px;
}
.comms-template .comms--message-row.is-right .comms--message-bubble {
  border-radius: 18px 18px 5px 18px;
}
.comms-template .comms--message-bubble-img {
  display: block;
  width: 240px;
  max-width: 100%;
  margin-bottom: 9px;
  border-radius: 12px;
  overflow: hidden;
}
.comms-template .comms--message-bubble-img img {
  display: block;
  width: 100%;
  height: auto;
}
.comms-template .comms--message-bubble-img-placeholder {
  display: grid;
  place-items: center;
  width: 240px;
  max-width: 100%;
  height: 104px;
  border-radius: 12px;
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.04em;
}
.comms-template .comms--message-bubble-text {
  text-wrap: pretty;
}
.comms-template .comms--message-action {
  align-self: center;
  max-width: 86%;
  padding: 5px 4px;
  text-align: center;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 11px;
  line-height: 1.6;
  letter-spacing: 0.04em;
}
.comms-template .comms--message-sendbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 14px 18px 18px;
  padding: 11px 12px 11px 16px;
  border-radius: 26px;
}
.comms-template .comms--message-sendbar-add {
  font-size: 21px;
  line-height: 1;
}
.comms-template .comms--message-sendbar-placeholder {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.comms-template .comms--message-sendbar-send {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  flex-shrink: 0;
}
.comms-template .comms--message-sendbar-send i {
  font-size: 18px;
  line-height: 1;
}

/* ── Letter ── */
/* Structure only (D-053). Handwriting body/sign-off/signature use 'Cedarville Cursive' (added
   to index.html in 17.5). Ruled-paper line COLOR comes from the theme files via
   --comms-letter-rule-color; base references it but never sets it. The deckled
   edge's zigzag background encodes the per-theme paper color, so it is authored
   in the theme files (base carries only the edge's box dimensions). */
.comms-template.comms--letter {
  /* variant marker; paper layout + palette live below / in theme files */
}
.comms-template .comms--letter-paper {
  position: relative;
  width: 100%;
  max-width: 100%;
  border-radius: 2px;
}
.comms-template .comms--letter-tape {
  position: absolute;
  top: -9px;
  width: 74px;
  height: 24px;
  transform: rotate(-4deg);
  opacity: 0.7;
}
.comms-template .comms--letter-tape.is-left { left: 24px; }
.comms-template .comms--letter-tape.is-right {
  right: 24px;
  transform: rotate(3.5deg);
}
.comms-template .comms--letter-edge {
  display: block;
  width: 100%;
  height: 11px;
}
.comms-template .comms--letter-inner {
  padding: 30px 40px 36px;
}
.comms-template .comms--letter-head {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding-bottom: 20px;
  margin-bottom: 10px;
}
.comms-template .comms--letter-meta-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.comms-template .comms--letter-meta-label {
  flex-shrink: 0;
  width: 42px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.comms-template .comms--letter-meta-name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 24px;
  line-height: 1;
  white-space: nowrap;
}
.comms-template .comms--letter-date {
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
}
.comms-template .comms--letter-body {
  position: relative;
  padding: 14px 4px 6px 4px;
  font-family: 'Cedarville Cursive', cursive;
  font-weight: 500;
  font-size: 23px;
  line-height: 34px;
  letter-spacing: 0.2px;
  text-wrap: pretty;
  /* Issue 5 + polish v1.16.x: ruled-line background AND the left margin rule (::before)
     + left indent removed; clean flush-left paper. */
}
.comms-template .comms--letter-body p { margin: 0 0 0.9em; white-space: pre-line; }
.comms-template .comms--letter-body p:last-child { margin-bottom: 0; }
.comms-template .comms--letter-signoff {
  margin-top: 18px;
  padding-left: 4px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 18px;
}
.comms-template .comms--letter-signoff-text {
  font-family: 'Cedarville Cursive', cursive;
  font-weight: 600;
  font-size: 27px;
  line-height: 1.2;
}
.comms-template .comms--letter-sig {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}
.comms-template .comms--letter-sig-name {
  font-family: 'Cedarville Cursive', cursive;
  font-weight: 700;
  font-size: 30px;
  line-height: 1;
}
.comms-template .comms--letter-portrait {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
}
.comms-template .comms--letter-portrait img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ── Journal ── */
/* Structure only (D-053). Handwriting body uses 'Cedarville Cursive' (loaded in 17.5 — the
   mockup itself uses it). Dotted-paper dot COLOR + spine stitch COLOR come from
   the theme files via --comms-journal-dot-color / --comms-journal-stitch-color;
   base references them but never sets them. .comms--journal-book needs no
   structural ::before (the mockup's edge is border + shadow only). */
.comms-template.comms--journal {
  /* variant marker; book layout + palette live below / in theme files */
}
.comms-template .comms--journal-book {
  position: relative;
  width: 100%;
  max-width: 100%;
  padding: 0;
  border-radius: 3px 6px 6px 3px;
}
.comms-template .comms--journal-spine {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 26px;
  border-radius: 3px 0 0 3px;
}
/* perforated stitch line down the spine (dashed; color is per-theme) */
.comms-template .comms--journal-spine::after {
  content: "";
  position: absolute;
  top: 22px;
  bottom: 22px;
  left: 13px;
  width: 0;
  border-left: 1.5px dashed var(--comms-journal-stitch-color);
}
.comms-template .comms--journal-inner {
  position: relative;
  z-index: 1;
  padding: 30px 56px 26px 48px;
}
.comms-template .comms--journal-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 16px;
  margin-bottom: 8px;
}
.comms-template .comms--journal-eyebrow {
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
}
.comms-template .comms--journal-day {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 30px;
  line-height: 1.02;
}
.comms-template .comms--journal-date {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  line-height: 1.5;
  text-align: right;
  white-space: nowrap;
}
.comms-template .comms--journal-date-label {
  display: block;
  margin-bottom: 2px;
  font-size: 8.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.comms-template .comms--journal-body {
  position: relative;
  padding: 14px 2px 6px;
  font-family: 'Cedarville Cursive', cursive;
  font-weight: 500;
  font-size: 23px;
  line-height: 32px;
  letter-spacing: 0.2px;
  text-wrap: pretty;
  /* Issue 5 (polish v1.15.x): dot-paper background removed; clean paper. */
}
.comms-template .comms--journal-body p { margin: 0 0 0.9em; white-space: pre-line; }
.comms-template .comms--journal-body p:last-child { margin-bottom: 0; }
.comms-template .comms--journal-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 18px;
  padding-top: 14px;
}
.comms-template .comms--journal-flourish {
  font-family: var(--font-display);
  font-size: 18px;
}
.comms-template .comms--journal-page {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

/* ============================================================================
   SECTION F1 — Timeline chrome: centered single-column, no sidebar (redesign)
   ============================================================================ */

.tlx-page { max-width: var(--page-w-contained); margin: 0 auto; padding: 40px 48px 96px; }
.tlx-head {
  display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
  padding-bottom: 20px; margin-bottom: 8px;
  border-bottom: 1px solid var(--rule);
}
.tlx-tools { margin-left: auto; display: inline-flex; gap: 2px; }

/* Entry list + rail structure */
.tlx-list { list-style: none; margin: 30px 0 0; padding: 0; }
.tlx-entry { display: grid; grid-template-columns: 40px 1fr; gap: 22px; padding-bottom: 32px; }
.tlx-rail { position: relative; display: flex; justify-content: center; }
.tlx-thread { position: absolute; top: 10px; bottom: -32px; width: 1px; background: var(--rule); }
.tlx-entry:last-child .tlx-thread { display: none; }
.tlx-card { min-width: 0; padding-top: 1px; }
.tlx-date { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink-faint); }
.tlx-title { font-family: var(--font-display); font-weight: 500; font-size: 22px; line-height: 1.12; color: var(--ink); margin: 5px 0 7px; }
.tlx-desc { font-family: var(--font-body); font-size: 14.5px; line-height: 1.7; color: var(--ink-soft); margin: 0; max-width: 60ch; }

/* Rail medallion attribution support classes */
.tlx-node {
  position: relative; overflow: hidden;
  width: 34px; height: 34px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-style: normal; font-weight: 500; font-size: 15px;
  color: var(--accent-deep); background: var(--accent-wash);
  border: 1.5px solid var(--paper-warm); box-shadow: 0 0 0 4px var(--paper);
}
/* Initial sits as the node's base content; photo layers over it and, on
   load failure, hides (onError) so the initial shows through — no empty circle. */
.tlx-node-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; border-radius: 50%;
}
.tlx-dot { width: 11px; height: 11px; border-radius: 50%; margin-top: 6px; background: var(--accent); box-shadow: 0 0 0 4px var(--paper); }
.tlx-byline { display: inline-flex; align-items: center; gap: 9px; padding: 4px 13px 4px 4px; border: 1px solid var(--rule); border-radius: 999px; background: var(--paper-soft); margin-bottom: 12px; white-space: nowrap; }
.tlx-avatar {
  width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-style: italic; font-size: 13px;
  color: var(--accent-deep); background: var(--accent-wash);
  border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
}
.tlx-who { font-family: var(--font-display); font-style: italic; font-size: 15px; color: var(--ink); white-space: nowrap; }
.tlx-who-inline { font-family: var(--font-display); font-style: italic; font-size: 14px; color: var(--ink-mute); white-space: nowrap; }
.tlx-who-inline::before { content: "—"; margin: 0 6px; color: var(--ink-faint); }
/* I-168: attribution is now a <button>; strip default button chrome so it renders identically. */
button.tlx-who-inline { background: none; border: none; padding: 0; cursor: pointer; text-align: inherit; }

/* Global node shows ✦ glyph — no photo layer, so overflow:hidden is not needed
   and clips the star character on some platforms. Use overflow:visible + a tighter
   font-size so the glyph sits cleanly inside the 34px circle. */
.tlx-global .tlx-node { color: var(--accent-deep); overflow: visible; font-size: 13px; line-height: 1; }
.tlx-global .tlx-avatar { background: color-mix(in oklab, var(--accent) 18%, var(--paper-warm)); }
.tlx-global .tlx-byline { background: color-mix(in oklab, var(--accent) 11%, var(--paper-soft)); border-color: color-mix(in oklab, var(--accent) 36%, var(--rule)); }
.tlx-global .tlx-who { font-family: var(--font-body); font-size: 10px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; font-style: normal; color: var(--accent-deep); }
/* "World Event" attribution — match character-name .tlx-who-inline: display font, italic, no caps */
.tlx-global .tlx-who-inline { font-family: var(--font-display); font-size: 14px; font-weight: 400; letter-spacing: 0; text-transform: none; font-style: italic; color: var(--ink-mute); }
.tlx-global .tlx-who-inline::before { content: ""; }
.tlx-global .tlx-dot { background: transparent; }

/* OPTION B — rail medallion: initial/star on rail, name as caption */
.attr-rail .tlx-dot { display: none; }
.attr-rail .tlx-byline { display: none; }
.attr-rail .tlx-thread { top: 40px; }
.attr-rail .tlx-metarow { display: flex; align-items: center; gap: 0; flex-wrap: wrap; }
.attr-rail .tlx-metarow .cw-symbol { margin-left: 6px; }

/* ============================================================================
   SECTION F2 — Guidebook chrome: thin left nav, no profile sidebar/tabs (redesign)
   ============================================================================ */

.gbx-layout {
  display: grid;
  grid-template-columns: 230px minmax(0, 1fr);
  gap: 48px;
  /* width:100% so the grid fills its (centered) mount instead of shrink-wrapping
     to content — keeps the content column a constant width across pages and
     across faceclaims/timeline section switches, regardless of how much is in it. */
  width: 100%;
  max-width: 1180px;   /* match the directory frame (.dir-grid) — owner page-gutter standard 2026-06-17 */
  margin: 0 auto;
  padding: 18px clamp(18px, 4vw, 44px) 90px;
  align-items: start;
}
.gbx-nav {
  position: sticky;
  top: 88px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-right: 1px solid var(--rule);
  padding-right: 18px;
}
.gbx-nav-head {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-mute);
  padding: 0 12px 8px;
}
.gbx-navitem {
  text-align: left;
  background: transparent;
  border: none;
  border-left: 2px solid transparent;
  padding: 9px 14px;
  font-family: var(--font-display);
  font-size: 17px;
  color: var(--ink-mute);
  cursor: pointer;
  transition: color 150ms ease, background 150ms ease, border-color 150ms ease;
  border-radius: 0 6px 6px 0;
}
.gbx-navitem:hover { color: var(--ink); background: color-mix(in oklab, var(--ink) 5%, transparent); }
.gbx-navitem.active {
  color: var(--accent-deep);
  border-left-color: var(--accent);
  background: color-mix(in oklab, var(--accent) 8%, transparent);
}
.gbx-content { min-width: 0; }
.gbx-content .section-title-block { margin-top: 4px; }

@media (max-width: 880px) {
  .gbx-layout { grid-template-columns: 1fr; gap: 24px; }
  .gbx-nav {
    position: static; flex-direction: row; flex-wrap: wrap; gap: 4px;
    border-right: none; border-bottom: 1px solid var(--rule);
    padding-right: 0; padding-bottom: 12px;
  }
  .gbx-nav-head { display: none; }
  .gbx-navitem { border-left: none; border-bottom: 2px solid transparent; border-radius: 6px; }
  .gbx-navitem.active { border-left-color: transparent; border-bottom-color: var(--accent); }
}

/* ============================================================================
   BOOT SPLASH (Band A S5.3 / I-359) — branded loading screen that replaces the
   empty-flash. Markup is pre-mount in index.html (with inline critical layout);
   app.js sets the admin icon + site title, then adds .is-hidden to fade it out.
   Token palette → it picks up the pre-paint theme. ============================ */
.app-splash {
  transition: opacity 460ms ease;
}
.app-splash.is-hidden { opacity: 0; pointer-events: none; }
.app-splash-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
.app-splash-icon {
  font-size: 80px;
  line-height: 1;
  color: var(--accent, #DDAE54);
  animation: appSplashPulse 1.5s ease-in-out infinite;
}
.app-splash-title {
  display: none;
  font-family: var(--font-display, serif);
  font-style: italic;
  font-size: 22px;
  letter-spacing: 0.04em;
  color: var(--ink, #ECE2CC);
}
@keyframes appSplashPulse {
  0%, 100% { opacity: 0.5; }
  50%       { opacity: 1;   }
}
@media (prefers-reduced-motion: reduce) {
  .app-splash-icon { animation: none; }
}

/* ============================================================
   ROUNDED SURFACES — radius tokenized (v1.17.0 Stage 4).
   Promoted from Eclipse's "tarot rounding" to ALL themes via --radius-*
   (Eclipse's values are the shared default). Radius only — no box/spacing
   change. A theme opts out by overriding the --radius-* tokens. These plain
   selectors sit after the earlier border-radius:1px declarations so they win
   by source order; Eclipse's old body.theme-eclipse radius overrides are removed.
   ============================================================ */
.char-card,
.cat-card,
.post,
.thread-foot,
.modal { border-radius: var(--radius-modal); }

.field-card,
.traits-block,
.dev-block,
.idea-card,
.connection-card,
.ship-card,
.short-answer-academic,
.error-boundary-box,
.tracker-entry,
.network-cap-banner--hard { border-radius: var(--radius-card); }

.portrait-frame,
.post-portrait,
.post-portrait-img,
.char-card-image,
.tinder-block,
.ig-cell,
.tracker-thumb { border-radius: var(--radius-image); }

.field-input,
textarea.prose-input,
.connection-url-input,
.timeline-date-input,
.timeline-title-input,
.gallery-input,
.dev-add-picker,
.toast,
.card-action,
.btn-icon,
.topbar-bell,
.topbar-nav-icon,
.topbar-hamburger,
.btn,
.btn-ghost,
.btn-accent,
.add-btn,
.page-btn,
.reorder-btn { border-radius: var(--radius-field); }

/* ============================================================
   CONTENT WARNING LABELS (Phase 26 C3)
   .cw-pill — inline before a post body or below a portrait.
   .cw-badge — compact row badge in thread list rows.
   Both reference theme-routed tokens (D-053 compliant).
   Reused in timeline, ad board, and any future CW surface.
   ============================================================ */
/* ============================================================
   FIRST-RUN WIZARD (Phase 30 / F2)
   Full-screen overlay + centered panel. Three screens: site identity,
   migrations, create admin. All colors via theme tokens (D-053 compliant).
   ============================================================ */
.wizard-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in oklab, var(--paper) 88%, transparent);
  backdrop-filter: blur(6px);
  animation: fadeIn 240ms ease;
}
.wizard-panel {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius-modal);
  padding: 44px 48px 48px;
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 8px 40px color-mix(in oklab, var(--ink) 12%, transparent);
  animation: modalIn 300ms cubic-bezier(.2,.7,.3,1);
}
.wizard-progress {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-bottom: 32px;
}
.wizard-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  border: 1.5px solid var(--rule);
  color: var(--ink-mute);
  background: transparent;
  transition: background 200ms ease, color 200ms ease, border-color 200ms ease;
}
.wizard-step--active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-wash);
}
.wizard-step--done {
  border-color: var(--accent);
  color: var(--paper);
  background: var(--accent);
}
.wizard-screen {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.wizard-heading {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 500;
  font-style: italic;
  color: var(--ink);
  margin: 0 0 4px;
  line-height: 1.15;
}
.wizard-hint {
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink-mute);
  margin: 0;
  line-height: 1.5;
}
.wizard-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.wizard-field label {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.wizard-input {
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius-field);
  padding: 10px 13px;
  width: 100%;
  transition: border-color 160ms ease;
  outline: none;
}
.wizard-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-wash);
}
.wizard-key-row {
  display: flex;
  gap: 8px;
  align-items: center;
}
.wizard-key-row .wizard-input {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12px;
}
.wizard-optional {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  font-size: 12px;
  color: var(--ink-faint, var(--ink-mute));
}
/* Migration runner */
.wizard-migration-runner {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.wizard-migration-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--rule-soft, var(--rule));
  border-radius: var(--radius-card);
  padding: 8px 0;
}
.wizard-migration-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 14px;
  font-family: var(--font-body);
  font-size: 12px;
  transition: background 160ms ease;
}
.wizard-migration-item--pending  { color: var(--ink-mute); }
.wizard-migration-item--running  { color: var(--accent); background: var(--accent-wash); }
.wizard-migration-item--done     { color: var(--ink-mute); }
.wizard-migration-item--error    { color: var(--danger, #c0392b); background: color-mix(in oklab, var(--danger, #c0392b) 8%, transparent); }
.wizard-migration-name {
  font-family: var(--font-mono);
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1;
}
.wizard-migration-status {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  flex-shrink: 0;
}

.cw-pill {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-deep);
  background: var(--accent-wash);
  border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--rule));
  border-radius: 999px;
  padding: 3px 10px;
  margin-bottom: 10px;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cw-badge {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-body);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-deep);
  background: var(--accent-wash);
  border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--rule));
  border-radius: 999px;
  padding: 2px 8px;
  margin-top: 4px;
  white-space: nowrap;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* L2 admin sub-tab bar — smaller than the L1 tab bar, sits inside a tab body. */
.admin-subtab-bar { margin: 0 0 14px; }
.admin-subtab-bar .tab { font-size: 13px; padding-top: 6px; padding-bottom: 6px; }

/* === Ad Board (C-Ad) ===
   Token fix (merge): plan CSS used --wash/--soft which are not defined tokens
   (see C-Dir note below); mapped to the real token set (css/tokens.css):
   --wash -> --paper-soft, --soft -> --rule (border) / --paper-warm (fill+hover). */
.ad-board-page { max-width: 860px; margin: 0 auto; padding: 32px 20px; }
.ad-board-header { margin-bottom: 20px; }
.ad-board-heading { font-family: var(--font-display); font-style: italic; font-size: 28px; font-weight: 500; color: var(--ink); margin: 0 0 4px; }
.ad-board-note { font-size: 13px; color: var(--ink-faint); margin: 0; }
.ad-board-filter-bar { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-bottom: 20px; }
.ad-board-search { flex: 1; min-width: 180px; height: 34px; padding: 0 12px; border: 1px solid var(--rule); border-radius: 6px; background: var(--paper-soft); color: var(--ink); font-size: 13px; font-family: var(--font-body); }
.ad-board-search:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.ad-board-list { display: flex; flex-direction: column; gap: 6px; }
.ad-board-empty { color: var(--ink-faint); font-style: italic; font-size: 14px; padding: 24px 0; text-align: center; }
.ad-card { display: flex; align-items: flex-start; gap: 14px; padding: 14px 16px; background: var(--paper-soft); border: 1px solid var(--rule); border-radius: 8px; cursor: pointer; text-align: left; width: 100%; transition: background 0.15s; }
.ad-card:hover { background: var(--paper-warm); }
.ad-card-portrait { width: 48px; height: 48px; border-radius: 50%; overflow: hidden; flex-shrink: 0; background: var(--paper-warm); display: flex; align-items: center; justify-content: center; }
.ad-card-portrait-img { width: 100%; height: 100%; object-fit: cover; }
.ad-card-portrait-init { font-size: 18px; font-weight: 600; color: var(--ink-soft); }
.ad-card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ad-card-title { font-size: 15px; font-weight: 500; color: var(--ink); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ad-card-tagline { font-size: 13px; color: var(--ink-soft); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ad-card-char { font-size: 12px; color: var(--ink-faint); margin: 4px 0 0; font-style: italic; }
.ad-card-category { font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); background: var(--rule); border-radius: 4px; padding: 2px 6px; white-space: nowrap; align-self: flex-start; flex-shrink: 0; margin-top: 2px; }

/* === User Directory (C-Dir) ===
   Plan CSS used --wash/--soft which are not defined tokens; mapped to the real
   token set (css/tokens.css): --wash -> --paper-soft, --soft -> --rule (border)
   / --paper-warm (fill+hover). */
.user-dir-page { max-width: 860px; margin: 0 auto; padding: 32px 20px; }
.user-dir-header { margin-bottom: 24px; }
.user-dir-heading { font-family: var(--font-display); font-style: italic; font-size: 28px; font-weight: 500; color: var(--ink); margin: 0 0 4px; }
.user-dir-note { font-size: 13px; color: var(--ink-faint); margin: 0; }
.user-dir-list { display: flex; flex-direction: column; gap: 4px; }
.user-dir-member { display: flex; align-items: center; gap: 14px; padding: 12px 16px; background: var(--paper-soft); border: 1px solid var(--rule); border-radius: 8px; cursor: pointer; text-align: left; width: 100%; transition: background 0.15s; }
.user-dir-member:hover { background: var(--paper-warm); }
.user-dir-avatar { width: 40px; height: 40px; border-radius: 50%; overflow: hidden; flex-shrink: 0; background: var(--paper-warm); display: flex; align-items: center; justify-content: center; }
.user-dir-avatar-img { width: 100%; height: 100%; object-fit: cover; }
.user-dir-avatar-init { font-size: 16px; font-weight: 600; color: var(--ink-soft); }
.user-dir-name { font-size: 15px; color: var(--ink); font-weight: 500; flex: 1; }
.dir-empty { color: var(--ink-faint); font-style: italic; font-size: 14px; padding: 24px 0; text-align: center; }
.adb-empty { color: var(--ink-faint); font-style: italic; font-size: 14px; padding: 24px 0; text-align: center; }

/* === Sidebar pending-approval badge (C-Approval Task 8) ===
   Shown below the character name when approval_status is not 'approved'.
   Disappears automatically once approved. Colors are theme-routed. */
.sidebar-pending-badge {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--warn);
  background: var(--warn-wash);
  border: 1px solid var(--warn-border);
  border-radius: 999px;
  padding: 2px 9px;
  margin-top: 4px;
}

/* ---- Faceclaims page (C-Faceclaims) ---- */
.fc-page { max-width: 900px; margin: 0 auto; padding: 16px; }
.fc-header { margin-bottom: 16px; }
.fc-heading { font-size: 24px; margin: 0 0 4px; }
.fc-intro { color: var(--ink-mute); font-size: 14px; margin: 0; white-space: pre-wrap; }
.fc-section { margin-top: 24px; }
.fc-section-head { font-size: 16px; margin: 0 0 8px; display: flex; align-items: baseline; gap: 8px; }
.fc-count { font-size: 12px; color: var(--ink-faint); font-weight: 400; }
.fc-empty { color: var(--ink-faint); font-size: 13px; padding: 8px 0; }

.fc-row, .fc-reserve-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 8px 10px; background: none; border: none; border-bottom: 1px solid var(--rule);
  text-align: left; cursor: pointer; color: inherit; font: inherit;
}
.fc-row:hover, .fc-reserve-row:hover { background: var(--paper-warm); }
.fc-portrait { flex: 0 0 auto; width: 36px; height: 36px; border-radius: 50%; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; background: var(--paper-soft); }
.fc-portrait-img { width: 100%; height: 100%; object-fit: cover; }
.fc-portrait-init { font-size: 14px; color: var(--ink-mute); }
.fc-claim { flex: 1 1 auto; font-weight: 600; }
.fc-charname { flex: 0 0 auto; color: var(--ink-mute); font-size: 13px; }
.fc-reserve-date { flex: 0 0 88px; color: var(--ink-faint); font-size: 12px; }
.fc-banned-row { display: flex; gap: 12px; padding: 6px 10px; border-bottom: 1px solid var(--rule); }
.fc-banned-reason { color: var(--ink-faint); font-size: 13px; }

/* P32 lifted statics (DX-053) — structural layout only; appearance in theme.css */
.fc-alpha-filter-head { display: flex; align-items: center; justify-content: space-between; padding-bottom: 4px; }
.fc-alpha-filter-label { padding-bottom: 0; }
.fc-alpha-clear-btn { margin-right: 4px; }
.fc-banner-title-block { margin-bottom: 0; padding-bottom: 0; border-bottom: none; justify-content: space-between; }
.fc-process-wrap { padding: 24px 40px 40px; }
.fc-sub-head { position: relative; }
.fc-sub-head-actions { margin-left: auto; flex-shrink: 0; align-self: center; display: flex; gap: 6px; }

/* Bubble-mockup tokens — neutral fallback for non-Bubble themes.
   Full candy-glass polish lives in bubble.css; this keeps the new
   Faceclaims/Directory/Ad Board/Stats classes readable elsewhere. */
html:not(.theme-bubble) {
  --pg-grad: var(--accent);
  --pg-grad-2: var(--paper);
  --pg-on-candy: var(--ink);
  --pg-on-candy-soft: var(--ink);
  --pg-card: var(--paper);
  --pg-well: var(--paper);
  --pg-rail: var(--paper);
  --pg-foot-wave: none;
  --glass-fill: var(--paper);
  --glass-stroke: var(--rule);
  --glass-blur: none;
  --glass-aero: none;
  --gloss: none;
  --bub-rim: var(--rule);
  --bub-grad: var(--accent);
  --bub-grad-2: var(--paper);
  --bub-wave-mask: none;
  --shadow-soft: 0 1px 4px rgba(0,0,0,0.06);
  --shadow-card: 0 2px 10px rgba(0,0,0,0.10);
  --radius-field: 10px;
  --radius-image: 14px;
  --radius-card: 16px;
  --radius-modal: 20px;
}

/* ── Compose layout — side-by-side editor + live preview ─────────────────── */
.compose-layout {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 1.5rem;
}

.compose-main {
  flex: 1 1 0;
  min-width: 0;
}

.compose-preview {
  flex: 1 1 0;
  min-width: 0;
  border: 1px solid var(--rule, #ddd);
  border-radius: 4px;
  padding: 1rem;
}

.compose-preview-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}

.compose-preview-show {
  align-self: flex-start;
}

@media (max-width: 768px) {
  .compose-layout {
    flex-direction: column;
  }
  .compose-preview-show {
    align-self: auto;
  }
}

/* ============================================================
   N3: BOTTOM BAR — sitewide chrome (always rendered)
   Structure: [avatar | login/logout] · [contextual] · [toggles]
   Skin lives in bubble.css / bubble-dark.css (T4/N6 add more).
   z-index 20: above page content (z-index ≤ 12 for notebook/sb
   docks which are sticky-in-scroll, not fixed) but below the
   topbar (z-index 50) and overlays (z-index ≥ 80).
   ============================================================ */
.bottombar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 24px;
  width: 100%;
  /* Pinned to the viewport bottom on every page (mirrors the old .rfd-dock
     pattern). NOT position:sticky — the bar renders near the top of .app-shell
     (right after the sticky topbar), so sticky would leave it in-flow at the
     top. fixed pins it to the bottom regardless of DOM order. */
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  border-top: 1px solid var(--rule, rgba(0,0,0,0.08));
  background: var(--paper, #ffffff);
  box-sizing: border-box;
  min-height: 44px;
}
/* Reserve clearance so the fixed bar never occludes the last line of page
   content. The bar is always rendered sitewide, so the clearance is global. */
.app-shell {
  padding-bottom: 56px;
}

/* ── EditDock (N7) — docked edit-mode action sheet ─────────────────────────────
   Small pill pinned just above the bottom bar: mono one-line notice + buttons.
   z-index sits BELOW the bar (20) so bar popovers always win. */
.editdock {
  position: fixed;
  bottom: 64px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 19;
  display: flex;
  flex-direction: column;     /* notice on top, actions below */
  align-items: stretch;
  gap: 10px;                  /* the line break between notice and buttons */
  max-width: calc(100vw - 32px);
  padding: 14px 16px;
  border-radius: 18px;        /* rounded card, not a pill, now that it stacks */
  border: 1px solid var(--rule, rgba(0,0,0,0.08));
  background: var(--paper-soft, #fff);
  box-shadow: 0 8px 28px -10px rgba(0,0,0,0.25);
  animation: editdock-pop 260ms cubic-bezier(.2,.9,.3,1.25) both;
}
.editdock--left  { left: 18px; right: auto; transform: none; }
.editdock--right { left: auto; right: 18px; transform: none; }
@keyframes editdock-pop {
  from { opacity: 0; transform: translateX(-50%) translateY(10px) scale(0.96); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0)    scale(1); }
}
.editdock--left, .editdock--right { animation-name: editdock-pop-side; }
@keyframes editdock-pop-side {
  from { opacity: 0; transform: translateY(10px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}
.editdock-notice {
  font-family: var(--font-mono, monospace);
  font-size: 10.5px;
  letter-spacing: 0.03em;
  color: var(--ink-mute, #777);
  text-align: center;
  white-space: normal;        /* was nowrap + ellipsis — allow the break */
  min-width: 0;
}
.editdock-notice--success { color: var(--ok, #2fa882); }
.editdock-notice--error   { color: var(--danger, #c0392b); }
/* ── EditDock save-state note (inline in bottom bar, before buttons) ── */
.editdock-note           { font-size: 11.5px; font-weight: 600; letter-spacing: 0.01em; margin-left: 4px; align-self: center; white-space: nowrap; }
.editdock-note--clean    { color: var(--ink-faint); }
.editdock-note--dirty    { color: var(--accent-deep); }
.editdock-note--saved    { color: var(--ok, #1f9e6e); }
.editdock-actions {
  display: flex;
  align-items: center;
  justify-content: center;    /* centers a lone Edit button */
  flex-wrap: wrap;
  gap: 8px;
}
/* EditDock buttons use the dir-foot-btn shape but size to content, not flex:1 */
.editdock-actions .dir-foot-btn { flex: 0 0 auto; }
/* #1/#2: dir-foot-btn pills portaled into the bottom bar (char-profile +
   user-profile edit docks, and the EditDock declarative buttons) must size to
   their text, not stretch via the base .dir-foot-btn{flex:1}. Combined with
   .bottombar-actions{justify-content:center} they cluster in the centre. */
.bottombar-actions .dir-foot-btn { flex: 0 0 auto; }

/* Phase 18: CSS fallback if the JS ResizeObserver overflow menu hasn't measured
   yet — actions wrap instead of forcing the bar wider than the viewport. */
.bottombar-actions { flex-wrap: wrap; }

@media (max-width: 480px) {
  /* phone: tighter bar gutters; keep a single comfortable row, allow horizontal
     scroll for the action cluster as a last resort so nothing clips off-screen. */
  .bottombar { padding: 6px 12px; gap: 6px; }
  .bottombar-actions {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .bottombar-actions::-webkit-scrollbar { display: none; }
}

.bottombar-left {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Profile photo button (auth-only) */
.bottombar-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  padding: 0;
  cursor: pointer;
  background: var(--paper-warm, #f5f0ff);
  overflow: hidden;
  flex-shrink: 0;
}
.bottombar-avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
}
.bottombar-avatar-initial {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft, #635c7e);
  line-height: 1;
}

/* Login / Logout buttons */
.bottombar-login,
.bottombar-logout {
  font-size: 13px;
  font-weight: 500;
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
  color: var(--ink, #36304e);
}

/* Login button carries a text label beside the sign-in icon, so it overrides
   the fixed 36px .notebook-dock-btn circle into an auto-width pill. */
.notebook-dock-btn.bottombar-login-btn {
  width: auto;
  gap: 6px;
  padding: 0 12px;
  border-radius: 18px;
}
.bottombar-login-label {
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
}

/* Contextual slot (centre) — permanent portal host (N4a+).
   Pages portal their dock controls here via BottomBarPortal.
   overflow: visible so dock popup menus (position:absolute; bottom: calc(100%+10px))
   can render above the bar without being clipped. Horizontal overflow is handled
   by BottomBarActions' JS-based ⋮ collapse — no CSS clip needed. */
.bottombar-contextual {
  flex: 1;
  min-width: 0;
  overflow: visible;
}

/* Actions row inside the contextual slot — horizontal flex container for
   portaled dock controls. N4b: overflow rows collapse trailing buttons into ⋮. */
.bottombar-actions {
  display: flex;
  align-items: center;
  /* #2: cluster the portaled edit-dock buttons in the centre with a pleasant gap
     rather than spreading them edge-to-edge. A full-width single child (the
     notebook/storyboard dock, which fills 100% and self-aligns via its own
     __spacer regions) is unaffected by centering. */
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-width: 0;
  overflow: visible;
  position: relative;
}

/* Generic action button for portaled per-page rows */
.bottombar-action-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  height: 30px;
  border: 1px solid var(--border, rgba(0,0,0,.12));
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
  font-size: 12px;
  font-family: var(--font-body);
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: background 0.12s, color 0.12s;
}
.bottombar-action-btn:hover {
  background: var(--hover-tint, rgba(0,0,0,.05));
}
.bottombar-action-btn.is-active,
.bottombar-action-btn.dirty {
  color: var(--accent, currentColor);
  border-color: var(--accent, currentColor);
}
.bottombar-action-btn.is-danger {
  color: var(--danger, #c0392b);
  border-color: var(--danger, #c0392b);
}
.bottombar-action-btn.is-primary {
  background: var(--accent, currentColor);
  color: var(--paper, #fff);
  border-color: var(--accent, currentColor);
}
/* Search input in the bottom bar */
.bottombar-search {
  height: 28px;
  padding: 0 10px;
  border: 1px solid var(--border, rgba(0,0,0,.12));
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
  font-size: 12px;
  font-family: var(--font-body);
  min-width: 100px;
  max-width: 200px;
  outline: none;
}
.bottombar-search:focus {
  border-color: var(--accent, currentColor);
}
/* A11y: the border-color-only focus is low-contrast; add a keyboard-focus ring. */
.bottombar-search:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Row gap between action items */
.bottombar-actions > * + * {
  margin-left: 6px;
}
/* ⋮ More button */
.bba-more-btn {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0;
  padding: 2px 8px;
  border-radius: 6px;
  line-height: 1;
}
/* Overflow popover */
.bba-popover {
  position: absolute;
  bottom: calc(100% + 6px);
  right: 0;
  background: var(--paper);
  border: 1px solid var(--border, rgba(0,0,0,.12));
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0,0,0,.12);
  padding: 4px;
  z-index: 999;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.bba-popover-item {
  display: contents;
}
.bba-popover-item .bottombar-action-btn {
  width: 100%;
  justify-content: flex-start;
  border: none;
  border-radius: 6px;
  padding: 6px 10px;
  height: auto;
}

/* Pinned cluster: always-visible children that are excluded from ⋮ collapsing.
   Rendered at the trailing edge of the actions row (after ⋮ when present).
   flex-shrink:0 ensures the cluster never gets squeezed out by collapsible items. */
.bba-pinned {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  margin-left: auto;
}
/* When there are no collapsible items (i.e. the pinned group IS all children),
   margin-left:auto would push it to the far right; reset to keep it centred. */
.bottombar-actions:only-child .bba-pinned,
.bottombar-actions > .bba-pinned:first-child {
  margin-left: 0;
}
/* Inner wrapper used by EditDock to group the save buttons + SaveNote together. */
.bba-pinned-save {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Status pill inside the portaled profile action row (N4a).
   Structure: token-driven colors, no hard palette values (D-053). */
.bba-status-pill {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-family: var(--font-body);
  flex-shrink: 0;
  white-space: nowrap;
  line-height: 1.4;
}
.bba-pill-saved {
  background: color-mix(in oklab, var(--accent, currentColor) 15%, transparent);
  color: var(--accent, currentColor);
}
.bba-pill-dirty {
  background: color-mix(in oklab, var(--danger, #c0392b) 12%, transparent);
  color: var(--danger, #c0392b);
}

/* Reparent fix (N4a): .notebook-dockbar and .sb-dock were position:sticky/bottom:0
   inside page scroll containers. Inside .bottombar-contextual they live in the
   already-sticky bottom bar — sticky/bottom must be neutralized to avoid double-sticky
   layering and to let the bar set its own dimensions. */
.bottombar-contextual .notebook-dockbar {
  position: static;
  bottom: auto;
  /* width:100% (not auto) so the dock fills the contextual slot and its
     __spacer{flex:1} regions have free space to distribute — without this the
     dock shrinks to content and the intended left/right alignment collapses
     (#17 notebook, #18 storyboard "alignment didn't stick"). */
  width: 100%;
  padding: 0;
  /* Let the bar's own min-height size the dock; remove the excess z-index too. */
  z-index: auto;
  /* Remove the page-level backdrop/shadow designed for a standalone sticky bar — the
     .bottombar shell itself provides background + border-top. !important is required so
     per-theme palette overrides (e.g. bubble-dark's
     `body.theme-bubble.dark-mode .notebook-dockbar`, specificity (0,3,1)) don't win and
     re-paint the dock's own glass surface inside the bar. D-053: the host owns the
     surface once the dock is reparented; themes still own the standalone dock palette
     wherever it is NOT inside .bottombar-contextual. */
  background: transparent !important;
  box-shadow: none !important;
  border-top: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* dir-foot-btn pills used for Copy/Download in the notebook dockbar center group
   must size to content, not flex:1-stretch (which would eat the spacer budget). */
.notebook-dockbar .dir-foot-btn { flex: 0 0 auto; }
/* Locked state for Copy/Download during an unfinished hard sprint.
   Mirrors the visual appearance of :disabled (no explicit :disabled rule exists →
   use opacity 0.45) but keeps pointer-events active so the click fires a toast. */
.dir-foot-btn.is-locked { opacity: 0.45; cursor: not-allowed; }

/* ============================================================================
   PHASE 17B · CANDY-PILL — structural shape (theme-neutral)
   Colors live in theme.css under body.theme-bubble. The shape mirrors the
   .adb-pill.cat pill used in adBoard/plotting (same inline-flex+gap+padding).
   ============================================================================ */

/* REST — base shape only; colors come from body.theme-bubble in theme.css */
.candy-pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 13px 5px 11px;
  white-space: nowrap;
  border-radius: 999px;
  font-weight: 600; font-size: 11.5px; letter-spacing: 0.02em;
  cursor: pointer;
  transition: all 160ms ease;
}

/* ACTIVE — glass lift; colors/backdrop come from theme.css */
.candy-pill--active {
  position: relative;
  /* backdrop-filter applied in theme.css; structural lift here */
  transform: translateY(-1px);
}

/* SAVE — same shape as REST; brand gradient skin comes from theme.css */
.candy-pill--save {
  position: relative;
  transform: translateY(-1px);
}

/* Notice region (N4b) — one-line mono save/submit/delete outcome display.
   Sits between the contextual slot and the right toggles. Hidden when empty.
   Structure rules here; tone colors use CSS tokens (D-053). */
.bottombar-notice {
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1;
  padding: 3px 10px;
  border-radius: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 280px;
  flex-shrink: 1;
  /* Fade in smoothly */
  animation: bottombar-notice-in 0.15s ease;
}
@keyframes bottombar-notice-in {
  from { opacity: 0; transform: translateY(3px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* Tone variants — token-driven, no hard palette values (D-053) */
.bottombar-notice--info {
  color: var(--ink-soft, #635c7e);
  background: color-mix(in oklab, var(--ink-soft, #635c7e) 10%, transparent);
}
.bottombar-notice--success {
  color: var(--accent, currentColor);
  background: color-mix(in oklab, var(--accent, currentColor) 12%, transparent);
}
.bottombar-notice--error {
  color: var(--danger, #c0392b);
  background: color-mix(in oklab, var(--danger, #c0392b) 10%, transparent);
}

/* Toggle slots (right) — N6: Typing Sounds + Dark Mode */
.bottombar-toggles {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.bottombar-slot {
  width: 36px;
  height: 36px;
  border-radius: 50%;
}
/* Toggle button structure — skin lives in bubble.css / bubble-dark.css.
   Metrics match .notebook-dock-btn (36px circle, 17px icon). */
.bottombar-toggle-btn {
  width: 36px;
  height: 36px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid transparent;
  cursor: pointer;
  font-size: 17px;
  transition: background 180ms, color 180ms, opacity 180ms, transform 180ms;
  background: transparent;
  flex-shrink: 0;
}
.bottombar-toggle-btn:hover {
  transform: translateY(-1px);
}
.bottombar-toggle-btn[aria-pressed="true"] {
  opacity: 1;
}
.bottombar-toggle-btn[aria-pressed="false"] {
  opacity: 0.55;
}

/* ── Guidebook: Groups block ── */
.gb-groups { width: 100%; }
/* read-view groups header: no left/right padding or margin */
.gb-groups .fc-sub-head { padding-left: 0; padding-right: 0; margin-left: 0; margin-right: 0; }
.gb-groups .fc-cols { padding: 8px 0 16px; }
/* body field: preserve line breaks; center-align text (#fix-groups-body-center) */
.gb-groups .fc-claim-foot .who.gb-groups-body { white-space: pre-wrap; text-align: center; }

/* edit mode */
.gb-groups-edit { display: flex; flex-direction: column; }
.gb-groups-edit-grid { padding-left: 0; padding-right: 0; }
.gb-groups-edit-card { cursor: default; }
.gb-groups-edit-card:hover { transform: none; box-shadow: var(--shadow-soft), var(--gloss); border-color: var(--glass-stroke); }
.gb-groups-edit-card-body { padding: 14px 14px 8px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.gb-groups-field {
  width: 100%; box-sizing: border-box;
  background: transparent; border: none; border-bottom: 1px solid var(--rule);
  font-family: var(--font-body); font-size: 13px; color: var(--ink);
  padding: 4px 2px; outline: none; transition: border-color 150ms;
}
.gb-groups-field:focus { border-bottom-color: var(--accent); }
/* #8 — title field: approximate the read-view .fc-face-name (display font, italic, larger) */
.text-block-heading-input.gb-groups-field {
  font-family: var(--font-display); font-style: italic; font-weight: 500;
  font-size: 17px; color: var(--ink); letter-spacing: 0; line-height: 1.2;
}
/* #8 — subtitle: approximate the read-view .fc-claim-foot .lbl (tiny caps, muted) */
.gb-groups-subtitle {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--ink-mute);
}
/* #8 — body textarea: approximate the read-view .fc-claim-foot .who (semi-bold body text).
   Compound selector beats textarea.prose-input specificity at equal class count via cascade. */
textarea.gb-groups-textarea {
  min-height: 80px; resize: vertical;
  font-family: var(--font-body); font-size: 13.5px; font-weight: 600; color: var(--ink);
  white-space: pre-wrap;
}
.gb-groups-edit-card-foot {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px;
  padding: 6px 10px 10px;
  border-top: 1px solid var(--rule);
}
.gb-groups-del-prompt { font-size: 11.5px; color: var(--ink-mute); margin-right: 4px; }

/* Infinite-scroll sentinel (Pagination phase) — structural; skin in bubble.css */
.infinite-sentinel {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 16px 0 28px;
  width: 100%;
}
.infinite-sentinel__more {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 18px;
  border-radius: 999px;
  background: var(--paper-warm);
  border: 1px solid var(--rule);
  font-weight: 600;
}
.infinite-sentinel__more .ph-circle-notch { animation: spin 0.9s linear infinite; }

@keyframes spin { to { transform: rotate(360deg); } }
/* tokens.css — shared --theme-* -> legacy-token remap (v1.17.0 Stage 5/6).
   Also holds cross-cutting layout tokens (P0 polish pass). */

:root {
  /* Canonical contained page width — Profile+sidebar reference (polish/P0).
     Used by .page-contained (new), .profile, .gbx-layout, .page-wrap. */
  --page-w-contained: 1280px;
  --page-w-full: 100%;
}

/*
   ONE remap for EVERY theme, light and dark. Per-theme nuance (the accent-mix
   ratios) is supplied by --mix-* tokens in each theme's palette block, so the
   FORMULA is shared while each theme keeps its exact computed values. Lives on
   html.theme-* so app.js per-character inline writes win on the same element.
   Stage 6: Island now declares a raw --theme-* palette (FLAT, --mix-*=0%) and
   joins this remap too. The accent surfaces (--accent-soft/-wash/-deep) are raw
   slots each theme fills from its own polarity: Island supplies pastel tints +
   a darker deep; the dark themes alias them to bg-surface/bg-raised/accent-lit. */
html.theme-island,
html.theme-academia,
html.theme-eclipse,
html.theme-bubble {
  /* accent-tinted borders + secondary inks; the accent proportion is the per-theme --mix-* */
  --theme-border:      color-mix(in oklab, var(--theme-border-neutral), var(--accent, var(--theme-accent)) var(--mix-border-accent));
  --theme-border-deep: color-mix(in oklab, var(--theme-border-deep-neutral), var(--accent, var(--theme-accent)) var(--mix-border-deep-accent));
  --theme-ink:         var(--theme-ink-neutral);
  --theme-ink-mid:     color-mix(in oklab, var(--theme-ink-mid-neutral), var(--accent, var(--theme-accent)) var(--mix-ink-mid-accent));
  --theme-ink-muted:   color-mix(in oklab, var(--theme-ink-muted-neutral), var(--accent, var(--theme-accent)) var(--mix-ink-soft-accent));
  --theme-ink-faint:   color-mix(in oklab, var(--theme-ink-faint-neutral), var(--accent, var(--theme-accent)) var(--mix-ink-soft-accent));

  /* legacy aliases (shared by all three themes; per-theme values flow in
     from each palette block's raw --theme-* tokens) */
  --paper:        var(--theme-bg-base);
  --paper-warm:   var(--theme-bg-raised);
  --paper-soft:   var(--theme-bg-surface);
  --ink:          var(--theme-ink);
  --ink-soft:     var(--theme-ink-mid);
  --ink-mute:     var(--theme-ink-muted);
  --ink-faint:    var(--theme-ink-faint);
  --rule:         var(--theme-border);
  --rule-soft:    var(--theme-border-deep);
  --accent:       var(--theme-accent);
  --accent-soft:  var(--theme-accent-soft);
  --accent-wash:  var(--theme-accent-wash);
  --accent-deep:  var(--theme-accent-deep);
}

/* ── A11y: honor prefers-reduced-motion. Neutralizes all transitions/animations
   app-wide for users who request reduced motion. Targeted per-component guards
   elsewhere remain; this is the global floor. ── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================================
   MOBILE POLISH (≤640) — consolidated, appended LAST so it wins the cascade
   (equal specificity → later source wins; tech-debt #7). Covers the 2026-06-17
   mobile review: secondary-page gutters, heading scale, threads density, and a
   no-horizontal-scroll guard. (body, not an inner wrapper, so the sticky topbar
   is unaffected.)
   ============================================================================ */
@media (max-width: 640px) {
  /* Never a horizontal scrollbar (backstop over the min-width:0 / overflow fixes). */
  body { overflow-x: hidden; }

  /* (.gbx-layout / .page-wrap gutters now match the directory's frame standard
     at the base rule — max-width 1180 + clamp(18px,4vw,44px) — so no mobile
     override here; clamp gives 18px on phones, identical to .dir-grid.) */

  /* Section-nav pills (guidebook / timeline / faceclaims): the ≤880 row-nav let
     each item stretch the full row width. Make them compact, content-width pills
     that wrap, so they don't dominate the screen. */
  .gbx-nav {
    gap: 6px;
    flex-wrap: nowrap;          /* one row instead of a tall wrapped block */
    overflow-x: auto;           /* swipe sideways through the sections */
    padding-bottom: 6px;
    scrollbar-width: none;      /* hide the scrollbar (Firefox) */
  }
  .gbx-nav::-webkit-scrollbar { display: none; }  /* hide the scrollbar (WebKit) */
  .gbx-navitem {
    flex: 0 0 auto; width: auto; white-space: nowrap;
    padding: 5px 12px; font-size: 13px;
    border-radius: 999px; border-bottom: none;
  }
  .gbx-navitem.active { border-bottom: none; background: color-mix(in oklab, var(--accent) 14%, transparent); }

  /* Shipping tab portraits: the fixed 100px circles read oversized once the ship
     card stacks single-column on phones. Bring them to the 72px avatar standard. */
  .ship-portrait-frame { width: 72px; height: 72px; }

  /* Notebook bottom dock — fit the narrow bottom-bar center slot after the
     goal+timer / gear consolidation: icon-only Copy/Download, compact word-count,
     no inline goal bar or separators; horizontal-scroll as a backstop so nothing
     ever clips. (Sitewide bottom-bar toggles are a different component — untouched.) */
  .notebook-dockbar { gap: 4px; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .notebook-dockbar::-webkit-scrollbar { display: none; }
  .notebook-dockbar__sep { display: none; }
  .notebook-dockbar .dir-foot-btn { font-size: 0; padding: 7px 9px; gap: 0; }
  .notebook-dockbar .dir-foot-btn i { font-size: 15px; }
  .notebook-dockbar__words { font-size: 11px; white-space: nowrap; flex: 0 0 auto; }
  .notebook-goal-trigger__label { display: none; }   /* icon + % only on phones */
  .notebook-goal-bar-outer { display: none; }         /* drop the inline goal bar */

  /* Heading block: smaller + tighter. */
  .section-title { font-size: 26px; }
  .section-title-en { font-size: 13px; }
  .section-title-block { margin-bottom: 16px; padding-bottom: 12px; }

  /* Threads: halve the category banner, and put counts + last-reply on ONE row
     (the base mobile rule stacked them into a tall block). */
  .cat-banner { height: 104px; }
  .cat-banner-title { font-size: 26px; }
  .cat-banner-body { left: 16px; right: 16px; bottom: 12px; }
  .cat-meta {
    display: flex; flex-direction: row; flex-wrap: nowrap;
    align-items: center; justify-content: space-between;
    gap: 10px; padding: 8px 14px;
  }
  .cat-meta-counts { gap: 12px; flex-shrink: 0; }
  .cat-meta-num { font-size: 8.5px; letter-spacing: 0.14em; }
  .cat-meta-num .n { font-size: 15px; }
  .cat-meta-last { gap: 6px; min-width: 0; }
  .cat-meta-last-text { flex-direction: row; align-items: baseline; gap: 6px; text-align: right; }
  .cat-meta-last-label { display: none; }   /* drop "Last reply" label to fit one line */
  .cat-meta-last-by { font-size: 13px; white-space: nowrap; }
  .cat-meta-last-when { display: none; }     /* drop the timestamp to fit one line */
}
