@font-face {
  font-family: "Geist";
  src: url("/assets/Geist-Variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --ink: #1b2330;
  --muted: #5d6675;
  --line: #e2e6ee;
  --accent: #0f6db3;
  --accent-soft: #e6f1fa;
  --accent-bar: #cfe5f5;
  --bg: #f5f7fb;
  --card: #ffffff;
  --ok: #1d7a4f;
  /* free contextual comments: highlight + danger tokens (not part of the
     original theme, added for docs/mockups/comments-a-rail.html) */
  --hl: #fdf0c4;
  --hl-line: #e8c65a;
  --hl-active: #ffe081;
  --danger: #8c2f2f;
}
* { box-sizing: border-box; }
html { font-size: 17px; }
body {
  margin: 0;
  font-family: "Geist", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
  line-height: 1.6;
}

.topbar {
  position: sticky; top: 0; z-index: 10;
  display: flex; justify-content: space-between; align-items: center;
  background: var(--card); border-bottom: 1px solid var(--line);
  padding: 10px 28px;
}
.topbar .brand { font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; font-size: 0.8rem; color: var(--accent); }
.topbar .identity { font-size: 0.85rem; color: var(--muted); }

.container { max-width: 900px; margin: 0 auto; padding: 36px 24px 100px; }
.board-header h1 { font-size: 2.1rem; line-height: 1.2; margin: 0.2em 0 0.6em; }
h1 { font-size: 1.9rem; }
h2 { font-size: 1.35rem; margin: 2.2em 0 0.6em; }
a { color: var(--accent); }
p { margin: 0.7em 0; }

.media-frame { background: #0e1117; border-radius: 14px; overflow: hidden; box-shadow: 0 6px 24px rgba(20, 35, 60, 0.12); }
.media-frame video { display: block; width: 100%; }
.media-frame img { display: block; width: 100%; background: #fff; }
img, video { max-width: 100%; }
.caption { color: var(--muted); font-size: 0.88rem; margin-top: 8px; }
.media-placeholder {
  border: 2px dashed var(--line); border-radius: 12px; padding: 40px 20px;
  text-align: center; color: var(--muted); background: var(--card);
}

.block { margin: 34px 0; scroll-margin-top: 70px; }

/* Content tables (markdown tables inside board text blocks): a themed card with a
   soft header, hairline row rules and gentle zebra — same tokens as the rest of the
   board so it tracks whichever theme is active. */
.block table {
  width: 100%;
  border-collapse: collapse;
  margin: 22px 0;
  font-size: 0.94rem;
  line-height: 1.45;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 3px;
  overflow: hidden;            /* clips the rounded corners under collapsed borders */
}
.block table th,
.block table td {
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.block table thead th {
  background: var(--accent-soft);
  color: var(--ink);
  font-weight: 600;
  border-bottom: 1px solid var(--accent-bar);
}
.block table tbody tr:nth-child(even) td { background: var(--bg); }
.block table tbody tr:last-child td { border-bottom: none; }
.block table td:first-child { color: var(--ink); font-weight: 500; }
.block-card {
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  padding: 24px 26px; margin: 34px 0;
  box-shadow: 0 2px 10px rgba(20, 35, 60, 0.05);
}

/* Until identity + first sync arrive, interactive blocks are locked so nobody
   acts on stale-empty state and "re-votes" over their own saved answer. */
.block-card.interactive.loading { opacity: 0.55; pointer-events: none; user-select: none; }
.block-card.interactive.loading::after {
  content: "Loading saved answers…";
  display: block; margin-top: 10px;
  font-size: 0.8rem; color: var(--muted);
  animation: hub-pulse 1.2s ease-in-out infinite;
}
@keyframes hub-pulse { 0%, 100% { opacity: 0.45 } 50% { opacity: 1 } }

/* Interactive blocks stand out: these are where feedback is expected. */
.block-card.interactive {
  position: relative;
  border-left: 5px solid var(--accent);
  background: linear-gradient(180deg, #fdfeff 0%, #f4f9fd 100%);
  box-shadow: 0 4px 16px rgba(15, 109, 179, 0.10);
  margin-top: 44px;
}
.input-badge {
  position: absolute; top: -12px; right: 18px;
  background: var(--accent); color: #fff;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
  padding: 4px 12px; border-radius: 999px;
  box-shadow: 0 2px 8px rgba(15, 109, 179, 0.3);
}

/* Freeze banner: a calm, intentional close — never the red error color.
   "countdown"/"owner" use the soft accent; "frozen" reads as the final state. */
.freeze-banner {
  margin: 0 0 22px; padding: 13px 18px; border-radius: 12px;
  font-size: 0.95rem; line-height: 1.45;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent-bar);
}
.freeze-banner.frozen { font-weight: 600; }

/* Frozen badge in the boards table: muted pill, reads as "closed" not "error". */
.badge-frozen {
  display: inline-block; margin-left: 6px; padding: 1px 8px;
  border-radius: 999px; font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.02em; vertical-align: middle;
  background: var(--accent-soft); color: var(--accent);
  border: 1px solid var(--accent-bar);
}

/* Frozen card: the saved answers stay VISIBLE (this is the read-only record),
   but inputs are disabled and the accent rail/badge go muted so it reads closed.
   Distinct from .loading, which HIDES content during the pre-sync gate. */
.block-card.interactive.frozen {
  border-left-color: var(--muted);
  background: var(--card);
  box-shadow: 0 2px 10px rgba(20, 35, 60, 0.05);
}
.block-card.interactive.frozen .input-badge {
  background: var(--muted); box-shadow: none;
}
.block-card.frozen select:disabled,
.block-card.frozen textarea:disabled,
.block-card.frozen input:disabled {
  background: #f3f4f6; color: var(--muted); cursor: not-allowed; opacity: 1;
}
.block-card .prompt { font-weight: 650; font-size: 1.08rem; margin: 0 0 4px; }
.hint { color: var(--muted); font-size: 0.84rem; margin: 0 0 12px; }

.mermaid-block { position: relative; background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 18px; overflow-x: auto; box-shadow: 0 2px 10px rgba(20,35,60,0.05); }
.mermaid-block .mermaid { display: flex; justify-content: center; }
/* never show raw mermaid source: hidden from first paint until processed */
.mermaid:not([data-processed="true"]) { visibility: hidden; }
.mermaid-block.loading-diagram { min-height: 150px; }
.mermaid-block.loading-diagram::before {
  content: ""; position: absolute; top: calc(50% - 26px); left: calc(50% - 14px);
  width: 28px; height: 28px; border-radius: 50%;
  border: 3px solid var(--accent-soft); border-top-color: var(--accent);
  animation: hub-spin 0.8s linear infinite;
}
.mermaid-block.loading-diagram::after {
  content: "Rendering diagram…"; position: absolute; top: calc(50% + 14px);
  left: 0; right: 0; text-align: center; color: var(--muted); font-size: 0.8rem;
}
@keyframes hub-spin { to { transform: rotate(360deg) } }

/* poll */
.poll-row { margin: 10px 0; }
.poll-option {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; border: 1.5px solid var(--line); border-radius: 10px;
  cursor: pointer; background: #fff; overflow: hidden;
  transition: border-color 0.15s;
}
.poll-option:hover { border-color: var(--accent); }
.poll-option.mine { border-color: var(--accent); }
.poll-option .bar {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  background: var(--accent-bar); opacity: 0.55; transition: width 0.4s ease;
}
.poll-option .label { position: relative; flex: 1; font-weight: 500; }
/* Leading mark tells the rule at a glance: circle = pick one, square = pick several */
.poll-option .mark {
  position: relative; z-index: 1; flex: none;
  width: 19px; height: 19px;
  border: 2px solid #b9c2cf; background: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; color: #fff;
}
.poll[data-multi="0"] .poll-option .mark { border-radius: 50%; }
.poll[data-multi="1"] .poll-option .mark { border-radius: 5px; }
.poll-option:hover .mark { border-color: var(--accent); }
.poll[data-multi="0"] .poll-option.mine .mark {
  border-color: var(--accent); background: var(--accent);
  box-shadow: inset 0 0 0 3.5px #fff;
}
.poll[data-multi="1"] .poll-option.mine .mark {
  border-color: var(--accent); background: var(--accent);
}
.poll[data-multi="1"] .poll-option.mine .mark::after {
  content: ""; width: 13px; height: 13px;
  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>') center/contain no-repeat;
}
.poll-option .count { position: relative; color: var(--muted); font-size: 0.85rem; white-space: nowrap; }
.voters { color: var(--muted); font-size: 0.8rem; margin: 3px 2px 0; min-height: 1em; }
.voters .chip {
  display: inline-block; background: var(--accent-soft); color: var(--accent);
  border-radius: 999px; padding: 1px 9px; margin: 2px 3px 0 0; font-size: 0.76rem; font-weight: 550;
}

/* select */
.control-row { display: flex; align-items: center; gap: 12px; }
select.hub-select { font-size: 1rem; padding: 10px 12px; border-radius: 10px; border: 1.5px solid var(--line); min-width: 260px; background: #fff; }
select.hub-select:focus { border-color: var(--accent); outline: none; }

/* input */
textarea.hub-input { width: 100%; min-height: 100px; font: inherit; padding: 12px; border-radius: 10px; border: 1.5px solid var(--line); resize: vertical; }
textarea.hub-input:focus { border-color: var(--accent); outline: none; }
.autosave-row { display: flex; justify-content: space-between; align-items: center; margin-top: 6px; }

.answers-list { margin-top: 14px; }
.answers-list .answer { border-top: 1px solid var(--line); padding: 10px 0; }
.answer .who { font-size: 0.78rem; font-weight: 600; color: var(--accent); margin-bottom: 2px; }
.select-answers .answer-inline { display: inline-block; margin-right: 14px; padding: 4px 0; font-size: 0.92rem; }
.answer-inline .who { font-weight: 600; color: var(--accent); }

/* save status: idle → saving → saved */
.save-status { font-size: 0.82rem; color: var(--muted); min-width: 70px; transition: color 0.2s; }
.save-status.saving { color: var(--muted); }
.save-status.saved { color: var(--ok); font-weight: 600; }
.save-status.error { color: #a33; font-weight: 700; }

/* comments: a conversation, not a log */
.comments .comment {
  display: flex; gap: 12px; align-items: flex-start;
  border-top: 1px solid var(--line); padding: 14px 0;
}
.comments .comment:first-child { border-top: 0; }
.comment .avatar {
  flex: none; width: 32px; height: 32px; border-radius: 3px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent);
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.02em;
}
.comment .avatar.mine { background: var(--accent); color: #fff; }
.comment-content { flex: 1; min-width: 0; }
.comment .who {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 0.82rem; color: var(--muted); margin-bottom: 2px;
}
.comment .who strong { color: var(--ink); font-size: 0.88rem; }
.comment .when { font-size: 0.76rem; }
.comment .edited { font-style: italic; font-size: 0.74rem; }
.comment-body { line-height: 1.5; overflow-wrap: break-word; }
.comment-actions { margin-left: auto; display: flex; gap: 2px; }
.icon-btn {
  background: none; border: 0; padding: 3px 5px; cursor: pointer;
  color: #b2bac6; border-radius: 2px; line-height: 1;
}
.icon-btn .icon { width: 14px; height: 14px; vertical-align: middle; }
.icon-btn:hover { color: var(--accent); background: var(--accent-soft); filter: none; }
.comment-edit-form { display: flex; gap: 8px; margin-top: 4px; align-items: center; }
.comment-edit-form input { flex: 1; font: inherit; padding: 8px 10px; border-radius: 2px; border: 1.5px solid var(--accent); }
.comment-form { display: flex; gap: 10px; margin-top: 12px; }
.comment-form input { flex: 1; font: inherit; padding: 11px 12px; border-radius: 10px; border: 1.5px solid var(--line); }
.comment-form input:focus { border-color: var(--accent); outline: none; }

/* ask callout */
.ask-callout { border-left: 4px solid var(--accent); background: var(--accent-soft); border-radius: 0 12px 12px 0; padding: 16px 20px; font-size: 1.02rem; }
.ask-callout .target { font-weight: 700; }

/* markdown blockquotes render as callouts: use `>` to make a point pop */
blockquote {
  margin: 1.4em 0; padding: 16px 20px;
  border-left: 4px solid var(--accent); background: var(--accent-soft);
  border-radius: 0 12px 12px 0; font-size: 1.02rem;
}
blockquote p { margin: 0.2em 0; }

button {
  font: inherit; background: var(--accent); color: #fff; border: 0;
  padding: 10px 18px; border-radius: 10px; cursor: pointer; font-weight: 550;
}
button:hover { filter: brightness(1.08); }
button.link { background: none; color: var(--accent); padding: 4px 0; text-decoration: underline; }

.board-footer { margin-top: 60px; color: var(--muted); font-size: 0.85rem; border-top: 1px solid var(--line); padding-top: 18px; }

/* lightbox */
.media-frame img, .lightboxable { cursor: zoom-in; }
.lightbox-overlay {
  position: fixed; inset: 0; z-index: 100; background: rgba(12, 18, 28, 0.88);
  display: flex; align-items: center; justify-content: center; cursor: zoom-out;
  padding: 32px; animation: lb-fade 0.15s ease;
}
.lightbox-overlay img { max-width: 96vw; max-height: 92vh; width: auto; height: auto; border-radius: 8px; box-shadow: 0 12px 60px rgba(0,0,0,0.5); background: #fff; }
.lightbox-overlay .lightbox-close { position: fixed; top: 14px; right: 20px; color: #fff; font-size: 1.6rem; background: none; padding: 4px 10px; }

/* diagram lightbox: zoomable, pannable fullscreen SVG */
.mermaid-block .mermaid { cursor: zoom-in; }
.diagram-expand {
  position: absolute; top: 10px; right: 10px;
  background: var(--card); color: var(--muted);
  border: 1px solid var(--line); padding: 6px 8px; line-height: 1;
}
.diagram-expand:hover { color: var(--accent); filter: none; }
.diagram-expand .icon { width: 15px; height: 15px; }
.diagram-lb { cursor: default; padding: 0; display: block; }
.lb-stage {
  position: absolute; inset: 0; overflow: hidden;
  cursor: grab; touch-action: none;
  user-select: none; -webkit-user-select: none;
}
.diagram-lb .lightbox-close { z-index: 103; }
.lb-stage:active { cursor: grabbing; }
.lb-canvas {
  position: absolute; top: 0; left: 0; transform-origin: 0 0;
  background: #fff; border-radius: 3px; padding: 24px;
  box-shadow: 0 12px 60px rgba(0,0,0,0.5);
}
.lb-controls {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
  display: flex; gap: 6px; z-index: 101;
}
.lb-controls button {
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line); width: 42px; height: 42px;
  display: inline-flex; align-items: center; justify-content: center;
}
.lb-controls button:hover { color: var(--accent); filter: none; }
.lb-controls .icon { width: 18px; height: 18px; }
body.lb-open { overflow: hidden; }
@keyframes lb-fade { from { opacity: 0 } to { opacity: 1 } }

/* image lightbox: same zoom/pan stage as diagrams */
.image-lb { cursor: default; padding: 0; display: block; }
.image-lb .lightbox-close { z-index: 103; }
.image-lb .lb-canvas { padding: 0; background: transparent; box-shadow: none; border-radius: 0; }
.image-lb .lb-canvas img { max-width: none; max-height: none; border-radius: 3px; box-shadow: 0 12px 60px rgba(0,0,0,0.5); }

/* admin + login extras */
.muted { color: var(--muted); font-size: 0.88rem; }
.request-access { border-top: 1px solid var(--line); margin-top: 22px; padding-top: 14px; }
.request-access p { margin: 0 0 4px; }
button.secondary { background: var(--card); color: var(--accent); border: 1.5px solid var(--accent); }
button.danger { background: #a33; }
.inline-form { display: inline-flex; gap: 8px; }
.actions-cell button { padding: 6px 12px; font-size: 0.85rem; }
.not-listed { margin-top: 16px; }

.conn-status { position: fixed; bottom: 14px; right: 16px; font-size: 0.75rem; color: var(--ok); background: var(--card); border: 1px solid var(--line); border-radius: 999px; padding: 5px 12px; box-shadow: 0 2px 8px rgba(20,35,60,0.08); }
.conn-status.offline { color: #a33; }

/* login */
.login-body { display: flex; min-height: 100vh; align-items: center; justify-content: center; background: var(--bg); }
.login-card { background: var(--card); border: 1px solid var(--line); border-radius: 16px; padding: 36px; max-width: 400px; width: 92%; box-shadow: 0 8px 30px rgba(20,35,60,0.08); }
.login-card h1 { font-size: 1.35rem; margin-top: 0; }
.login-card p { overflow-wrap: anywhere; }
.login-card input { width: 100%; font: inherit; padding: 12px; border-radius: 10px; border: 1.5px solid var(--line); margin: 10px 0; }
.login-card input:focus { border-color: var(--accent); outline: none; }
.login-card button[type=submit] { width: 100%; }
.login-card .error { color: #a33; }
.resend { margin-top: 8px; text-align: center; }
/* spaced-out one-time-code field — same convention as Synolia Share */
.code-input {
  letter-spacing: 0.45em; font-size: 1.45rem; text-align: center;
  font-variant-numeric: tabular-nums;
}
.code-input::placeholder { letter-spacing: 0.3em; color: #c3cbd6; }

/* admin user directory */
.user-list { background: var(--card); border: 1px solid var(--line); border-radius: 3px; }
.user-row {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 18px; border-top: 1px solid var(--line);
}
.user-row:first-child { border-top: 0; }
.user-row:target { background: var(--accent-soft); }
.monogram {
  flex: none; width: 34px; height: 34px; border-radius: 3px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent);
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.03em;
}
.user-main { flex: 1; min-width: 0; }
.user-email { font-weight: 600; overflow-wrap: anywhere; }
.user-meta { font-size: 0.8rem; color: var(--muted); }
.pill {
  flex: none; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
  padding: 3px 10px; border-radius: 2px; text-transform: uppercase;
}
.pill-ok { background: #e7f2ec; color: var(--ok); }
.pill-muted { background: #eef1f5; color: var(--muted); }
.pill-warn { background: #f7efe2; color: #8a6a25; }
.pill-danger { background: #f7e7e7; color: #8c2f2f; }
.manage { position: relative; flex: none; }
.manage summary {
  cursor: pointer; list-style: none; color: var(--accent);
  font-size: 0.85rem; font-weight: 600; padding: 6px 10px;
  border: 1px solid var(--line); border-radius: 2px; background: var(--card);
}
.manage summary::-webkit-details-marker { display: none; }
.manage[open] summary { background: var(--accent-soft); }
.manage-panel {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
  width: 290px; background: var(--card); border: 1px solid var(--line);
  border-radius: 3px; box-shadow: 0 8px 28px rgba(16, 24, 40, 0.14);
  padding: 12px; display: flex; flex-direction: column; gap: 8px;
}
.manage-action { display: flex; gap: 6px; }
.manage-action input[type="password"] {
  flex: 1; font: inherit; font-size: 0.85rem; padding: 7px 9px;
  border: 1.5px solid var(--line); border-radius: 2px; min-width: 0;
}
.manage-action button { white-space: nowrap; }
.btn-ghost {
  background: var(--card); color: var(--accent);
  border: 1px solid var(--accent); padding: 7px 12px;
  font-size: 0.85rem; font-weight: 600; border-radius: 2px;
}
.btn-ghost:hover { background: var(--accent-soft); filter: none; }
.btn-ghost.ghost-danger { color: #8c2f2f; border-color: #c9a0a0; }
.btn-ghost.ghost-danger:hover { background: #f7e7e7; }
.manage-action:nth-child(2) .btn-ghost, .manage-action:nth-child(3) .btn-ghost { width: 100%; }

/* destructive-action confirmation pop */
.confirm-remove { position: relative; display: inline-block; }
.confirm-remove summary { list-style: none; cursor: pointer; display: inline-block; }
.confirm-remove summary::-webkit-details-marker { display: none; }
.confirm-pop {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  width: 260px; background: var(--card);
  border: 1px solid #c9a0a0; border-top: 3px solid #8c2f2f;
  border-radius: 3px; box-shadow: 0 8px 28px rgba(16, 24, 40, 0.18);
  padding: 14px; text-align: left;
}
.confirm-pop p { margin: 0 0 10px; line-height: 1.4; }
.confirm-actions { display: flex; gap: 8px; }
.confirm-actions .danger { font-size: 0.85rem; padding: 7px 12px; }
.confirm-actions .btn-ghost { font-size: 0.85rem; padding: 7px 12px; }

table.activity { width: 100%; border-collapse: collapse; font-size: 0.9rem; background: var(--card); border-radius: 12px; overflow: hidden; }
table.activity th, table.activity td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); }
table.activity th { background: var(--accent-soft); }
.note-cell { max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filter-row { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; }
.filter-row select,
.filter-row input { font: inherit; padding: 9px 12px; border-radius: 8px; border: 1.5px solid var(--line); background: #fff; max-width: 100%; }
.filter-row input:focus,
.filter-row select:focus { border-color: var(--accent); outline: none; }
/* the deadline picker needs room for "mm/dd/yyyy, --:-- --" + the calendar icon */
.filter-row input[type="datetime-local"] { min-width: 230px; color: var(--ink); }

/* ---------- chapter navigation ---------- */
.board-nav {
  position: fixed; left: 24px; top: 90px; z-index: 60;
  width: 230px; max-height: calc(100vh - 130px); overflow-y: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  padding: 14px 10px; box-shadow: 0 4px 16px rgba(20, 35, 60, 0.07);
  font-size: 0.86rem;
}
.nav-title {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); padding: 0 10px 8px;
}
.nav-link {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 10px; border-radius: 8px;
  color: var(--ink); text-decoration: none; line-height: 1.3;
}
.nav-link:hover { background: var(--accent-soft); }
.nav-link.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.nav-dot { flex: none; width: 18px; text-align: center; font-size: 0.8rem; }
.nav-link:not(.has-input) .nav-dot { color: #c3cbd6; }
.nav-link.has-input .nav-dot { filter: saturate(1.2); }
.nav-legend {
  margin-top: 10px; padding: 8px 10px 0; border-top: 1px solid var(--line);
  font-size: 0.74rem; color: var(--muted);
}
.nav-burger {
  display: none; position: fixed; left: 16px; bottom: 16px; z-index: 70;
  width: 52px; height: 52px; border-radius: 50%;
  font-size: 1.3rem; line-height: 1; padding: 0;
  box-shadow: 0 4px 16px rgba(15, 109, 179, 0.35);
}
.nav-backdrop {
  display: none; position: fixed; inset: 0; z-index: 58;
  background: rgba(12, 18, 28, 0.45);
}

/* below 1360px the side gutter can't fit the rail: switch to burger drawer */
@media (max-width: 1359px) {
  .board-nav {
    left: 0; top: 0; bottom: 0; max-height: none; height: 100%;
    width: min(300px, 84vw); border-radius: 0 16px 16px 0;
    transform: translateX(-105%); transition: transform 0.22s ease;
    z-index: 65; padding-top: 20px;
  }
  body.nav-open .board-nav { transform: translateX(0); }
  body.nav-open .nav-backdrop { display: block; }
  .nav-burger { display: block; }
}

/* ---------- activity feed ---------- */
/* desktop: a floating foldable panel on the RIGHT, mirroring the chapter rail */
.act-rail {
  position: fixed; right: 0; top: 90px; z-index: 61;
  width: 320px; max-height: calc(100vh - 130px);
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line); border-right: none;
  border-radius: 3px 0 0 3px; box-shadow: 0 4px 16px rgba(20, 35, 60, 0.10);
  transform: translateX(324px); transition: transform 0.24s cubic-bezier(.4, 0, .2, 1);
}
body.act-open .act-rail { transform: translateX(0); }
/* the always-visible handle hanging off the panel's left edge */
.act-handle {
  position: absolute; left: -40px; top: 14px; width: 40px; padding: 12px 0;
  cursor: pointer; background: var(--accent); color: #fff; border: none;
  border-radius: 3px 0 0 3px; box-shadow: -2px 2px 10px rgba(20, 35, 60, 0.18);
  display: flex; flex-direction: column; align-items: center; gap: 8px; font: inherit;
}
.act-handle .act-vtxt {
  writing-mode: vertical-rl; transform: rotate(180deg);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
}
.act-handle .icon { width: 18px; height: 18px; }
body.act-open .act-handle { box-shadow: none; }
.act-badge {
  background: var(--bronze); color: #fff; font-size: 0.62rem; font-weight: 700;
  border-radius: 2px; padding: 1px 4px; line-height: 1.4;
}
.act-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--line);
}
.act-head .act-title {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}
.act-head .act-close {
  background: none; border: none; color: var(--muted); cursor: pointer;
  padding: 2px; line-height: 1; box-shadow: none;
}
.act-head .act-close:hover { color: var(--ink); filter: none; }
.act-body { overflow-y: auto; padding: 6px 0; }
.act-item { display: flex; gap: 10px; padding: 9px 14px; align-items: flex-start; }
.act-item + .act-item { border-top: 1px solid #eef1f5; }
a.act-item, a.act-link { text-decoration: none; color: inherit; }
.act-link { cursor: pointer; transition: background 0.12s; }
.act-link:hover { background: var(--accent-soft); }
.act-link:hover .tg { text-decoration: underline; }
.act-item .avatar {
  flex: none; width: 30px; height: 30px; border-radius: 2px;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.68rem; font-weight: 700; color: #fff; background: var(--accent);
}
.act-item .avatar.act-syn { background: var(--bronze); }
.act-txt { font-size: 0.84rem; line-height: 1.4; color: var(--ink); min-width: 0; }
.act-txt .nm { font-weight: 600; }
.act-txt .vb { color: var(--muted); }
.act-txt .tg { color: var(--accent); }
.act-meta { display: flex; align-items: center; gap: 6px; margin-top: 2px; color: var(--muted); font-size: 0.72rem; }
.act-meta .icon { width: 12px; height: 12px; }
.act-meta .chap { color: var(--bronze); }
.act-empty { padding: 20px 14px; color: var(--muted); font-size: 0.85rem; }
/* flash the target block after a click-through */
.act-flash { animation: actFlash 1.6s ease-out; }
@keyframes actFlash {
  0%, 30% { box-shadow: 0 0 0 3px var(--bronze); }
  100% { box-shadow: 0 0 0 3px transparent; }
}
/* Same click-through flash, but for a `comment` activity entry landing on the
   THREAD's own anchor (main.js:activateBlockComments()) instead of the whole
   block above -- its mark.hl or .hub-unit edge. outline, not box-shadow: both
   of those elements already use box-shadow for mark.hl.active/.hub-peek and
   .hub-unit.has-unit's own state (see the free-comments section below), which
   can be showing at the very same moment this fires, and outline never
   participates in layout or collides with a box-shadow value the way stacking
   two box-shadow declarations on one element would. */
.hub-anchor-flash { outline: 3px solid transparent; outline-offset: 3px; animation: hubAnchorFlash 1.6s ease-out; }
@keyframes hubAnchorFlash {
  0%, 30% { outline-color: var(--bronze); }
  100% { outline-color: transparent; }
}
/* in-drawer "subfolder" version, only shown inside the burger nav (mobile) */
.nav-activity { display: none; margin-top: 10px; border-top: 1px solid var(--line); padding-top: 6px; }
.nav-activity > summary {
  list-style: none; cursor: pointer; padding: 7px 10px; border-radius: 8px;
  display: flex; align-items: center; gap: 8px;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}
.nav-activity > summary::-webkit-details-marker { display: none; }
.nav-activity > summary:hover { background: var(--accent-soft); }
.nav-activity > summary .chev { margin-left: auto; transition: transform 0.18s; }
.nav-activity[open] > summary .chev { transform: rotate(90deg); }
.nav-activity .act-body { max-height: 50vh; }
.nav-activity .act-item { padding: 8px 6px; }
/* below 1360px the right rail can't fit: hide it, surface activity in the drawer */
@media (max-width: 1359px) {
  .act-rail { display: none; }
  .nav-activity { display: block; }
}

/* ---------- mobile ---------- */
@media (max-width: 640px) {
  html { font-size: 16px; }
  .container { padding: 22px 14px 80px; }
  .topbar { padding: 8px 14px; }
  .topbar .identity { max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .board-header h1 { font-size: 1.5rem; }
  h2 { font-size: 1.18rem; }
  .block { margin: 26px 0; }
  .block-card { padding: 18px 14px; margin: 30px 0; border-radius: 12px; }
  .block-card.interactive { margin-top: 36px; }
  .input-badge { font-size: 0.68rem; right: 10px; }
  .media-frame { border-radius: 10px; margin: 0 -4px; }
  .control-row { flex-direction: column; align-items: stretch; gap: 8px; }
  select.hub-select { width: 100%; min-width: 0; }
  .comment-form { flex-direction: column; }
  .comment-form button { width: 100%; }
  .comment .avatar { width: 28px; height: 28px; font-size: 0.7rem; }
  .comment .who { flex-wrap: wrap; }
  .poll-option { padding: 11px 10px; }
  .poll-option .count { font-size: 0.78rem; }
  .autosave-row { flex-direction: column; align-items: flex-start; gap: 2px; }
  table.activity { display: block; overflow-x: auto; white-space: nowrap; }
  .block table { display: block; overflow-x: auto; }
  .filter-row { flex-wrap: wrap; }
  .login-card { padding: 26px 18px; }
  .conn-status { bottom: 8px; right: 8px; font-size: 0.7rem; }
  .ask-callout { padding: 12px 14px; }
  .lightbox-overlay { padding: 10px; }
}

/* ============================================================
   THEME EXPERIMENT: "corporate classy" — navy + bronze, squared
   corners, flat hairlines. Remove this block to restore classic.
   ============================================================ */
:root {
  --ink: #1b2330;
  --muted: #5b6675;
  --line: #d8dde5;
  --accent: #1f3a5f;          /* deep navy */
  --accent-soft: #edf1f6;
  --accent-bar: #d9e2ed;
  --bg: #f3f4f6;
  --card: #ffffff;
  --ok: #2e7d54;
  --bronze: #a98a4e;          /* restrained second accent */
}

/* squared, flat geometry */
.block-card, .mermaid-block, .media-frame, .login-card, .board-nav,
table.activity, .ask-callout, blockquote, .media-placeholder { border-radius: 3px; }
.block-card, .mermaid-block, .board-nav, .login-card {
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
}
.media-frame { box-shadow: 0 1px 3px rgba(16, 24, 40, 0.12); }
button, .poll-option, select.hub-select, textarea.hub-input,
.comment-form input, .login-card input, .comment-edit-form input,
.filter-row select { border-radius: 2px; }
.poll-option .mark { border-radius: 1px; }
.poll[data-multi="0"] .poll-option .mark { border-radius: 50%; }
.ask-callout, blockquote { border-radius: 0 3px 3px 0; }
.input-badge, .conn-status, .voters .chip { border-radius: 2px; }

/* typographic discipline */
.board-header h1 {
  font-weight: 750; letter-spacing: -0.015em;
  border-bottom: 3px solid var(--accent); padding-bottom: 14px;
}
h2 {
  font-weight: 700; letter-spacing: -0.008em;
  border-bottom: 1px solid var(--line); padding-bottom: 8px;
}
.topbar { border-bottom: 2px solid var(--accent); }
.topbar .brand { color: var(--accent); letter-spacing: 0.12em; }

/* bronze marks where input is expected: quiet but unmistakable */
.input-badge { background: var(--bronze); box-shadow: none; }
.nav-link.has-input .nav-dot { filter: none; }
.nav-legend { color: var(--bronze); }
.block-card.interactive { border-left: 3px solid var(--bronze); background: var(--card); }
.poll-option.mine { border-color: var(--accent); background: var(--accent-soft); }
.voters .chip { background: var(--accent-soft); color: var(--accent); }
.nav-link.active { border-left: 2px solid var(--bronze); border-radius: 0 2px 2px 0; }

button { font-weight: 600; letter-spacing: 0.01em; }
button.danger { background: #8c2f2f; }
.save-status.saved { color: var(--ok); }

/* lucide icon plumbing */
.icon {
  width: 1em; height: 1em; flex: none;
  vertical-align: -0.15em; display: inline-block;
}
.input-badge .icon { width: 0.95em; height: 0.95em; margin-right: 2px; }
.nav-dot .icon { width: 15px; height: 15px; vertical-align: -2px; }
.nav-dot .dot {
  display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: #c3cbd6; vertical-align: 2px;
}
.nav-legend .icon { width: 13px; height: 13px; vertical-align: -2px; }
.nav-burger .icon { width: 22px; height: 22px; vertical-align: middle; }
.save-status .icon { vertical-align: -2px; margin-right: 2px; }
.lightbox-close .icon { width: 22px; height: 22px; }

/* ============================================================
   ::upload — file upload field (navy+bronze theme, squared)
   ============================================================ */
.upload-block .dropzone {
  margin-top: 10px; border: 2px dashed var(--accent-bar); border-radius: 3px;
  background: #fbfcfe; padding: 26px 20px; text-align: center;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  cursor: pointer; transition: border-color .15s, background .15s;
}
.upload-block .dropzone:hover { border-color: var(--accent); background: #fff; }
.upload-block .dropzone.drag { border-color: var(--accent); background: var(--accent-soft); }
.upload-block .dropzone .cloud {
  width: 38px; height: 38px; color: var(--accent); margin-bottom: 6px; overflow: visible;
}
.upload-block .dz-title { font-weight: 600; color: var(--ink); margin: 0; }
.upload-block .dz-title b { color: var(--accent); text-decoration: underline; }
.upload-block .dz-sub { color: var(--muted); font-size: .82rem; margin: 4px 0 0; }

.upload-block .file-list {
  list-style: none; margin: 16px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 9px;
}
.upload-block .file-row {
  display: flex; align-items: flex-start; gap: 13px; padding: 11px 13px;
  background: var(--card); border: 1px solid var(--line); border-radius: 3px;
}
.upload-block .file-row.mine { border-color: var(--accent-bar); background: #fbfcfe; }
.upload-block .file-icon {
  flex: none; width: 36px; height: 36px; border-radius: 3px; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent);
}
.upload-block .file-icon .icon { width: 19px; height: 19px; }
.upload-block .file-icon.pdf { background: #fbeceb; color: #b23b32; }
.upload-block .file-icon.img { background: #eaf4ee; color: var(--ok); }
.upload-block .file-meta { flex: 1; min-width: 0; }
.upload-block .file-name {
  font-weight: 600; font-size: .95rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.upload-block .file-sub {
  font-size: .78rem; color: var(--muted);
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
}
.upload-block .file-sub.error-text { color: #b23b32; }
.upload-block .owner-chip { display: inline-flex; align-items: center; gap: 6px; }
.upload-block .avatar {
  width: 20px; height: 20px; border-radius: 2px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent); font-size: .62rem; font-weight: 700;
}
.upload-block .avatar.mine { background: var(--accent); color: #fff; }
.upload-block .tag-you { color: var(--accent); font-weight: 600; }
.upload-block .file-actions { flex: none; display: flex; gap: 3px; }
.upload-block .file-actions .icon-btn {
  background: none; border: 0; padding: 7px; cursor: pointer; color: #9aa4b2;
  border-radius: 2px; line-height: 1; display: inline-flex; text-decoration: none;
}
.upload-block .file-actions .icon-btn:hover { color: var(--accent); background: var(--accent-soft); }
.upload-block .file-actions .icon-btn.danger:hover { color: #8c2f2f; background: #f7ecec; }
.upload-block .file-actions .icon { width: 17px; height: 17px; }

/* per-file note */
.upload-block .file-note { margin-top: 7px; }
.upload-block .note-text {
  display: flex; gap: 7px; align-items: flex-start; font-size: .86rem; color: var(--ink);
  background: var(--accent-soft); border: 1px solid var(--line);
  border-left: 3px solid var(--accent-bar); border-radius: 2px; padding: 7px 10px; line-height: 1.45;
}
.upload-block .note-text .icon { flex: none; width: 14px; height: 14px; color: var(--accent); margin-top: 3px; }
.upload-block .note-body { flex: 1; min-width: 0; white-space: pre-wrap; }
.upload-block .note-actions { flex: none; display: flex; gap: 1px; margin-left: 4px; opacity: 0; transition: opacity .12s; }
.upload-block .file-row.mine .note-text:hover .note-actions,
.upload-block .file-row.mine .note-text:focus-within .note-actions { opacity: 1; }
.upload-block .note-actions .icon-btn { background: none; border: 0; padding: 4px; cursor: pointer; color: #9aa4b2; border-radius: 2px; display: inline-flex; }
.upload-block .note-actions .icon-btn:hover { color: var(--accent); background: #fff; }
.upload-block .note-actions .icon-btn.danger:hover { color: #8c2f2f; }
.upload-block .note-actions .icon { width: 14px; height: 14px; }
.upload-block .note-edit-link {
  display: inline-flex; align-items: center; gap: 5px; background: none; border: 0;
  padding: 3px 0; margin-top: 2px; color: var(--accent); font: inherit; font-size: .82rem; cursor: pointer;
}
.upload-block .note-edit-link .icon { width: 14px; height: 14px; }
.upload-block .note-edit-link:hover { text-decoration: underline; }
.upload-block .note-editor textarea {
  width: 100%; font: inherit; font-size: .86rem; padding: 8px 10px;
  border: 1.5px solid var(--accent); border-radius: 2px; resize: vertical; min-height: 44px;
  background: #fff; color: var(--ink);
}
.upload-block .note-editor textarea:focus { outline: none; }
.upload-block .note-editor-row { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.upload-block .note-save {
  font: inherit; font-size: .82rem; font-weight: 600; background: var(--accent); color: #fff;
  border: 0; padding: 6px 13px; border-radius: 2px; cursor: pointer;
}
.upload-block .note-cancel { font: inherit; font-size: .82rem; background: none; border: 0; color: var(--muted); cursor: pointer; }
.upload-block .note-remove { font: inherit; font-size: .82rem; background: none; border: 0; color: #8c2f2f; cursor: pointer; margin-left: auto; }
.upload-block .note-remove:hover { text-decoration: underline; }

/* uploading / progress */
.upload-block .file-row.uploading { border-style: dashed; }
.upload-block .progress-wrap { margin-top: 6px; height: 6px; border-radius: 99px; background: var(--accent-soft); overflow: hidden; }
.upload-block .progress-bar { height: 100%; width: 0; background: var(--accent); border-radius: 99px; transition: width .2s; }
.upload-block .progress-pct { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 600; }
.upload-block .spinner {
  width: 16px; height: 16px; margin-top: 8px;
  border: 2px solid var(--accent-soft); border-top-color: var(--accent);
  border-radius: 50%; animation: hub-spin .7s linear infinite;
}
@keyframes hub-spin { to { transform: rotate(360deg); } }

/* frozen: drop zone + edit/delete controls retire; downloads + notes stay readable */
.block-card.interactive.frozen.upload-block .dropzone { display: none; }

/* ---------- free contextual comments (see docs/superpowers/specs/2026-08-13-free-comments-design.md) ---------- */
/* Ported from docs/mockups/comments-a-rail.html, "DIRECTION A: MARGIN RAIL" and
   below (approved design). Renamed for the real board: .shell -> .board-shell,
   .doc -> #board-doc, .rail -> .comment-rail, .sheet -> .comment-sheet,
   .fab -> .comment-fab. */
/* The text column (#board-doc, .container) owns its own centering --
   max-width:900px, margin:0 auto, unchanged from the pre-comments layout.
   .board-shell used to be a centered 1268px grid with the rail as a real
   column, which shifted the text ~170px left of true page centre (the grid
   reserved 312px+32px on the right that the text column's own auto-margins
   had no way to see). The user asked for the text to go back to dead-centre
   and stay there even while cards open/close/appear -- so the rail is taken
   out of flow entirely (position:absolute) and floats over the right-hand
   margin instead of claiming a track. No max-width here: board-shell spans
   the full viewport (like .topbar already does), so the text's own centering
   always lands on true viewport centre regardless of window width.
   A first cut left the rail always on: on a window too narrow to fit both,
   the rail's left edge landed INSIDE the text column's right edge and cards
   covered up to 86px of every line near a comment. Fixed by gating the rail
   itself on width (see the min-width:1500px breakpoint below) instead of
   letting it overlap -- below that width desktop falls back to the same
   badge + bottom-sheet mode narrow/mobile already uses.

   The rail is anchored to the TEXT COLUMN's right edge, not the viewport's
   (Google Docs' model). Pinned to the viewport it drifted further from the
   text the wider the window got -- ~230px of dead space at 1900px -- and its
   top-right corner tucked under the 40px .act-handle that's fixed to the
   viewport edge. Anchored to the content, the gap is constant at every width.

   Geometry, all derived rather than guessed. .board-shell spans the full
   viewport and is the containing block, so an absolutely positioned child's
   `50%` is viewport/2 and its `right` counts from the viewport's right edge:
     text column right edge = 50% + 450px      (half of max-width:900px)
     rail left edge         = 50% + 470px      (that + a 20px gap; 44px clear
                                                of the glyphs, since .container
                                                adds 24px of its own padding)
     rail right edge        = 100% - 16px      (breathing room at the viewport)
   With `left`, `right` AND `max-width` all set the box is over-constrained,
   so CSS drops `right` (direction:ltr) once max-width binds: the rail grows
   rightward from the text and simply stops at 400px. Below 400px it tracks
   the window. Width is deliberately NOT a clamp() on 100vw -- 100vw INCLUDES
   the scrollbar while media queries exclude it, and these pages always have
   one, so a vw formula would run ~15px wide and overhang the viewport.

   The visibility threshold falls out of the same numbers -- the rail is worth
   showing while it can still hold its 264px floor:
     (100% - 16px) - (50% + 470px) >= 264px  ->  viewport >= 1500px
   1500 rather than a rounder number is chosen to keep the rail on 1512px
   (MacBook Pro 14") and 1536px (1920 at Windows 125% scaling), both common
   client screens. Keep this in sync with .c-card's max-width, the media
   queries below, rail.js's isNarrow() and focus.js's WIDE(). */
.board-shell { position: relative; padding: 36px 24px 140px; }
#board-doc { min-width: 0; }
.comment-rail {
  position: absolute; top: 0;
  left: calc(50% + 470px); right: 16px; width: auto; max-width: 400px;
}

/* highlights */
mark.hl {
  background: var(--hl); box-shadow: inset 0 -2px 0 var(--hl-line); border-radius: 2px;
  color: inherit; cursor: pointer; padding: 1px 0; transition: background 0.15s, box-shadow 0.15s;
}
mark.hl:hover { background: var(--hl-active); }
mark.hl.active { background: var(--hl-active); box-shadow: inset 0 -2px 0 #c99a12; }
/* Card <-> highlight linking (rail.js:syncEmphasis()): with two cards open in
   the rail, every highlight otherwise renders identically -- there is no way
   to tell which card belongs to which quote. `.hub-peek` is the lighter,
   pointer-follows-the-card echo of `.active` above (which already existed as
   dead CSS ported from the mockup; syncEmphasis() is what finally sets it):
   only the underline brightens, the fill stays put, so hovering a card never
   reads as strongly as actually opening one. Declared before `.active` so
   that on the rare thread that is simultaneously the active one AND some
   OTHER co-located thread's hover target (only possible for a shared
   `.hub-unit` bar, see below), equal-specificity cascade order alone makes
   `.active` win -- the active card's own highlight always stays dominant. */
mark.hl.hub-peek { box-shadow: inset 0 -2px 0 var(--hl-active); }
.anchor-media { position: relative; cursor: pointer; }
.anchor-media.active .media-frame, .anchor-media.active .diagram {
  outline: 3px solid var(--hl-line); outline-offset: 3px; border-radius: 14px;
}
.media-pin {
  position: absolute; top: 10px; right: 10px; background: var(--card); border: 1px solid var(--line);
  border-radius: 999px; padding: 4px 11px; font-size: 0.78rem; font-weight: 650; color: var(--accent);
  box-shadow: 0 3px 10px rgba(20, 35, 60, 0.18); display: inline-flex; gap: 5px; align-items: center; z-index: 2;
}
.diagram { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 18px; }

/* rail cards */
/* Now that the rail floats over the page instead of sitting in its own grid
   track (see .board-shell above), a card can visually sit on top of body
   text whenever the window is too narrow to clear it -- the shadow needs to
   read as "a card floating above the page" in that case, not just the
   subtle depth it had when it only ever sat over empty margin. Applied
   unconditionally rather than only while overlapping (which CSS can't detect
   here); it reads fine over plain margin too. */
/* width:100%, never a second hardcoded px value -- the rail alone owns the
   width (it's fluid between a 264px floor and a 400px cap, see .comment-rail),
   and a card that carried its own copy of that number would silently drift out
   of sync the next time one of them is tuned. */
.c-card {
  position: absolute; left: 0; width: 100%; background: var(--card); border: 1px solid var(--line);
  border-radius: 13px; padding: 13px 14px; box-shadow: 0 6px 20px rgba(20, 35, 60, 0.14);
  transition: top 0.18s cubic-bezier(.2, .7, .3, 1), box-shadow 0.15s, border-color 0.15s; cursor: pointer;
}
.c-card.active { border-color: var(--hl-line); box-shadow: 0 8px 26px rgba(20, 35, 60, 0.16); cursor: default; }
/* Which anchor kind a card carries -- state.js/rail.js:threadCardHTML(). */
.c-card .anchor-label {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; margin: 0 0 5px;
}
.c-card .anchor-label.precise { color: #8a6a00; }
.c-card .anchor-label.whole { color: var(--accent); }
.c-card .quoted {
  font-size: 0.76rem; color: var(--muted); border-left: 3px solid var(--hl-line);
  padding-left: 8px; margin: 0 0 9px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; font-style: italic;
}
.c-card .quoted.whole { border-left-color: var(--accent); }
.c-card.collapsed .thread .comment:not(:first-child) { display: none; }
.c-card.collapsed .thread .comment .body {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.c-card.collapsed .reply-row { display: none; }
.more-line { font-size: 0.78rem; color: var(--accent); font-weight: 600; margin: 7px 0 0; }
.c-card.active .more-line { display: none; }

/* comment atoms (shared). .comment, .avatar, .who, .when, .edited and
   .icon-btn are common enough names that the shipped board UI already uses
   them (question-comment threads, upload owner chips, ::select/::input
   answers) — style.css is a shared asset every already-published board loads
   live, so an unscoped rule here would restyle those boards the moment this
   ships, with no republish. Scoped under .hub-comment-ui, which every element
   the comment UI renders already carries or sits inside (see anchor.js's
   SKIP constant for the same boundary used the other direction). */
.hub-comment-ui .comment { display: flex; gap: 9px; margin: 0 0 12px; }
.hub-comment-ui .comment:last-of-type { margin-bottom: 0; }
.hub-comment-ui .avatar {
  flex: none; width: 29px; height: 29px; border-radius: 50%; background: #dfe6f2; color: #41506b;
  font-size: 0.72rem; font-weight: 700; display: flex; align-items: center; justify-content: center;
}
.hub-comment-ui .avatar.mine { background: var(--accent-soft); color: var(--accent); }
.c-body { min-width: 0; flex: 1; }
.hub-comment-ui .who { font-size: 0.8rem; display: flex; gap: 7px; align-items: baseline; flex-wrap: wrap; }
.hub-comment-ui .who strong { font-weight: 650; }
.hub-comment-ui .when { color: var(--muted); font-size: 0.74rem; }
.hub-comment-ui .edited { color: var(--muted); font-size: 0.72rem; font-style: italic; }
.body { font-size: 0.87rem; line-height: 1.5; margin: 2px 0 0; white-space: pre-wrap; word-wrap: break-word; }
.c-actions { margin-left: auto; display: flex; gap: 2px; opacity: 0; transition: opacity 0.12s; }
.hub-comment-ui .comment:hover .c-actions, .c-card.active .c-actions { opacity: 1; }
.hub-comment-ui .icon-btn {
  background: none; border: 0; padding: 2px 4px; cursor: pointer; color: var(--muted);
  font-size: 0.74rem; border-radius: 5px;
}
.hub-comment-ui .icon-btn:hover { background: var(--bg); color: var(--ink); }
.hub-comment-ui .icon-btn.danger:hover { color: var(--danger); }
.reply-row { margin-top: 11px; display: flex; gap: 8px; align-items: flex-start; }
.reply-row textarea, .composer textarea, .draft-row textarea, .edit-row textarea {
  flex: 1; font: inherit; font-size: 0.87rem; border: 1px solid var(--line);
  border-radius: 9px; padding: 8px 10px; resize: none; min-height: 36px; max-height: 150px; background: #fcfdff;
  line-height: 1.45;
}
.reply-row textarea:focus, .composer textarea:focus, .draft-row textarea:focus, .edit-row textarea:focus {
  outline: 2px solid var(--accent-bar); border-color: var(--accent);
}
.btn {
  background: var(--accent); color: #fff; border: 0; border-radius: 9px; padding: 8px 14px; font: inherit;
  font-size: 0.83rem; font-weight: 600; cursor: pointer;
}
.btn:disabled { opacity: 0.45; cursor: default; }
.btn.ghost { background: none; color: var(--muted); font-weight: 500; }

/* New-thread composer and inline comment editing: same card, a full-width
   textarea over a right-aligned action row (unlike .reply-row's inline
   textarea+button, since these need two buttons and can't share one line). */
.draft-row, .edit-row { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.c-card .quoted + .draft-row { margin-top: 0; }
.draft-row textarea, .edit-row textarea { width: 100%; box-sizing: border-box; }
.draft-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* selection pill */
.sel-pill {
  position: absolute; z-index: 25; background: var(--ink); color: #fff; border: 0; border-radius: 9px;
  padding: 7px 13px; font: inherit; font-size: 0.82rem; font-weight: 600; cursor: pointer;
  box-shadow: 0 6px 20px rgba(10, 20, 40, 0.3); display: none; gap: 6px; align-items: center;
}
.sel-pill:not([hidden]) { display: inline-flex; }

/* One-time desktop discovery hint (compose.js:mountHint()) -- shown on the
   first hover of the document, auto-dismissing. pointer-events:none so it
   can never intercept the hover/selection it's explaining, and it never
   competes for layout (fixed, out of flow) so nothing shifts under it. */
.hub-hint {
  position: fixed; top: 78px; left: 50%; transform: translateX(-50%);
  z-index: 20; background: var(--ink); color: #fff; border-radius: 9px;
  padding: 8px 16px; font: inherit; font-size: 0.82rem; font-weight: 600;
  box-shadow: 0 6px 20px rgba(10, 20, 40, 0.3); pointer-events: none;
  opacity: 0; transition: opacity 0.25s ease; white-space: nowrap;
}
.hub-hint.show { opacity: 1; }

/* mobile pieces */
.thread-badge { display: none; }
.sheet-backdrop { position: fixed; inset: 0; background: rgba(12, 20, 35, 0.45); z-index: 50; display: none; }
.sheet-backdrop:not([hidden]) { display: block; }
.comment-sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 51; background: var(--card);
  border-radius: 18px 18px 0 0; box-shadow: 0 -8px 40px rgba(10, 20, 40, 0.28);
  max-height: 78vh; overflow: auto; padding: 8px 18px 22px; transform: translateY(100%);
  transition: transform 0.22s cubic-bezier(.2, .75, .3, 1);
}
.comment-sheet.show { transform: translateY(0); }
.grabber { width: 42px; height: 4px; border-radius: 3px; background: var(--line); margin: 8px auto 14px; }
.comment-fab {
  position: fixed; right: 16px; bottom: 18px; z-index: 45; background: var(--accent); color: #fff; border: 0;
  border-radius: 999px; padding: 12px 18px; font: inherit; font-size: 0.88rem; font-weight: 650;
  box-shadow: 0 8px 24px rgba(15, 109, 179, 0.4); cursor: pointer; display: none; gap: 7px; align-items: center;
}

/* 1499/1500: the narrow side of the rail-fit threshold derived above
   .comment-rail, i.e. the last width at which the rail can no longer hold its
   264px floor beside the text. Splitting it as max-width:1499 here /
   min-width:1500 on the wide rule below (not the same number on both, which
   is a bug this already had once) means the two queries partition the axis
   with no width that matches both -- when both read 1180, a viewport of
   exactly 1180px matched both at once, so the rail was hidden by this rule
   AND the desktop-only hover dot / hidden-bubble rules below fired at the
   same width, leaving a single-pixel dead band with neither mode's
   affordances fully in effect. Must stay equal to rail.js's isNarrow()
   (max-width:1499px) below. */
@media (max-width: 1499px) {
  /* .board-shell no longer grids text against the rail (see the desktop rule
     above) -- #board-doc's own .container max-width/margin already centres
     it at any width, so there's nothing left to override here except hiding
     the rail itself. */
  .comment-rail { display: none; }
  .comment-fab:not([hidden]) { display: inline-flex; }
  .thread-badge {
    display: inline-flex; align-items: center; gap: 3px; vertical-align: super; font-size: 0.62rem;
    font-weight: 700; background: var(--accent); color: #fff; border-radius: 999px; padding: 1px 6px;
    margin-left: 3px; cursor: pointer; line-height: 1.5;
  }
  .media-pin { font-size: 0.72rem; }
  /* .hub-unit-chip is the desktop click target for a whole-unit thread --
     sheet.js's .media-pin already fills that role on narrow screens (it
     predates this feature and already fires for any non-mark anchor, image/
     diagram or paragraph alike), so showing both here would double the same
     indicator on one unit. */
  .hub-unit-chip { display: none; }
  /* The rail's .c-card is absolutely positioned to float beside its
     highlight (see rail.js's relayout()) — meaningless in the sheet, which
     is a normal-flow column of cards. Mirrors .rail-stranded .c-card, the
     same reset already used for the rail's own non-floating group. */
  #sheet-body .c-card { position: static; width: auto; margin-bottom: 12px; }
  #sheet-body .c-card:last-child { margin-bottom: 0; }
}
@media (max-width: 560px) {
  html { font-size: 16px; }
  .board-shell { padding: 22px 16px 120px; }
  .board-header h1 { font-size: 1.6rem; }
}

/* Threads whose block was deleted: kept, never dropped, but out of the float
   layout since they have no anchor to align to. */
.rail-stranded { position: relative; }
.rail-stranded .c-card { position: static; width: auto; margin-bottom: 12px; }
.rail-stranded h3 {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin: 0 0 2px;
}
.c-card .quoted.orphan { border-left-color: var(--muted); color: var(--muted); }

/* Close control on an open card. A block-level header row (not float/absolute
   positioning) so it can never overlap the quoted text above it, whatever the
   quote's length. Only rendered while the card is active — see threadCardHTML. */
.c-card-head { display: flex; justify-content: flex-end; margin: -5px -6px 2px 8px; }
.c-card-head .icon-btn { font-size: 1rem; line-height: 1; padding: 2px 7px; }

/* @mention chips in rendered comment bodies + the autocomplete menu that
   inserts them, plus the unread visual chrome (rail card border, sheet
   badges, mobile FAB). */
.mention { color: var(--accent); font-weight: 600; }
.mention-menu {
  position: absolute; z-index: 30; background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 8px 24px rgba(20,35,60,.16); overflow: hidden;
  display: flex; flex-direction: column; min-width: 180px;
}
.mention-menu button {
  background: none; border: 0; text-align: left; padding: 8px 12px; font: inherit;
  font-size: .86rem; cursor: pointer; color: var(--ink);
}
.mention-menu button:hover { background: var(--accent-soft); }
.c-card.unread { border-left: 3px solid var(--accent); }
.c-card.unread .quoted::after {
  content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); margin-left: 6px; vertical-align: middle;
}
.thread-badge.unread, .media-pin.unread { box-shadow: 0 0 0 2px var(--accent-soft); }
.comment-fab.has-unread { background: var(--accent); }
.reply-row, .draft-row { position: relative; }

/* Whole-unit anchors (kind='block'): a paragraph, image or diagram marks its
   own edge instead of highlighting text -- never a <mark>, so any number of
   these can share a paragraph with any number of precise highlights without
   colliding. See docs/superpowers/specs/2026-08-14-unit-comments-design.md.
   Classes are applied in state.js:repaint(); the count chip markup itself
   lands in a later task. */
/* markUnits() (state.js) applies `.hub-unit` unconditionally to every
   commentable paragraph on every board, whether or not it has a comment --
   including boards published before this feature whose stored HTML
   predates it entirely (style.css/bundle.js are shared, loaded live on
   container restart). So the class itself must be perfectly layout-neutral:
   applying it to a comment-free board must change nothing rendered.
   Horizontal bleed is padding + an equal, opposite margin (12px each) --
   safe, because these elements have no horizontal margin of their own to
   collide with. Vertical breathing room can't use the same trick: unlike
   horizontal, p/h1/h2/h3/blockquote/li each carry their OWN, DIFFERENT
   vertical margin already (0.7em, 2.2em+0.6em, 1.4em, ... -- and a bare h1/
   h3 inside a sectioning element takes a margin from the browser's UA
   stylesheet that this file never sets at all, so there is no literal value
   here to subtract from). A flat vertical padding+margin offset was tried
   and measured: it collapses headings into the text above them, a swing of
   hundreds of pixels on heading-heavy content -- worse than the padding
   bug it was meant to fix. So there is no vertical padding or margin here
   at all, ever: the box-shadow rules below paint the focus tint and edge
   bar OUTSIDE the border box, which (unlike padding) takes no layout space,
   so they can bleed the same way without touching this element's height or
   position in any state. */
.hub-unit {
  position: relative; border-radius: 10px;
  padding-left: 12px; padding-right: 12px;
  margin-left: -12px; margin-right: -12px;
  transition: box-shadow 0.22s ease;
}
.hub-unit.has-unit { box-shadow: inset 3px 0 0 var(--hl-line); }
/* Same card <-> highlight linking as mark.hl.hub-peek/.active above (see
   that comment), for a whole-unit thread's edge bar instead of a mark: a
   paragraph/image/diagram comment has no mark to brighten, so the bar itself
   carries both states. Peek only brightens the bar's colour (still 3px);
   active also thickens it to 4px, echoing how mark.hl.active adds a full
   background fill on top of its own darker edge -- the strongest cue this
   element can carry without the background fill the big comment above rules
   out. Declared in this order so equal-specificity cascade lets `.active`
   win when one shared bar is both the active thread's and a different
   co-located thread's hover target. */
.hub-unit.has-unit.hub-peek { box-shadow: inset 3px 0 0 var(--hl-active); }
.hub-unit.has-unit.active { box-shadow: inset 4px 0 0 #c99a12; }
/* z-index:1 -- Fix 1 made .comment-rail float (position:absolute) as a LATER
   DOM sibling of #board-doc; neither it nor #board-doc establishes its own
   stacking context, so with no z-index anywhere every .c-card (z-index:auto)
   painted over document content wherever they overlapped, silently covering
   this chip (and .hub-unit-dot below) -- unclickable, no error, no cue.
   Any positive z-index here is enough: it moves this element out of the
   z-index:auto/positioned tier .c-card still occupies into the strictly
   later-painted positive tier, with no need to touch .c-card itself. */
.hub-unit-chip {
  position: absolute; right: 6px; top: 0; z-index: 1; background: var(--hl-line); color: #5a4300;
  font-size: 0.62rem; font-weight: 700; border-radius: 999px; padding: 1px 7px; cursor: pointer;
}
/* Two overlapping precise comments must not render as a darker patch -- but
   this must step aside for :hover/.active, or its higher specificity
   ((0,2,2), from the doubled `mark.hl`) permanently outranks mark.hl:hover
   and mark.hl.active above (both (0,2,1)), and a nested mark could never
   show hover or active feedback again. */
mark.hl mark.hl:not(:hover):not(.active) { background: none; }

/* Mobile focus engine (focus.js): tints whichever unit sits nearest 45% of
   viewport height while scrolling, and the floating bubble that follows it
   to create a whole-unit comment. See docs/mockups/comments-unified.html's
   .focus-target / .bubble. A plain `background` would hug the text tightly
   with .hub-unit carrying no vertical padding (see the note above) -- a
   spread box-shadow reproduces the same soft, bled tint without it, and
   without affecting layout the way padding would. */
.hub-focus-target { box-shadow: 0 0 0 6px var(--accent-soft); }
.hub-unit.has-unit.hub-focus-target { box-shadow: 0 0 0 6px var(--accent-soft), inset 3px 0 0 var(--accent); }
.hub-bubble {
  position: fixed; right: 16px; z-index: 40; width: 48px; height: 48px; border-radius: 50%;
  background: var(--accent); color: #fff; border: 0; font-size: 1.2rem; cursor: pointer;
  box-shadow: 0 6px 20px rgba(15, 109, 179, 0.45); display: flex; align-items: center; justify-content: center;
  transition: top 0.26s cubic-bezier(.2, .75, .3, 1), opacity 0.18s; opacity: 0; pointer-events: none;
}
.hub-bubble.show { opacity: 1; pointer-events: auto; }

/* Desktop-only affordance for creating a whole-unit comment: a small 💬
   button revealed on hover, painted only on images/diagrams now (see
   compose.js:paintUnitDots()) since text units no longer get one. Sits just
   outside the unit's own box so it never overlaps .hub-unit-chip (inside the
   box, at right:6px) or the unit's own content.
   right:-26px (== -width) puts the dot's own LEFT edge exactly flush with
   the unit's right edge -- previously -34px left an 8px dead strip between
   the two boxes that belonged to neither, so the mouse crossing it dropped
   `.hub-unit:hover` (CSS :hover is geometric: it's only true while the
   pointer sits over some box that's actually part of the element, and there
   was no box at all in that 8px gap) before it ever reached the dot, which
   is `display:none` until that same :hover rule turns it on below -- the
   dot vanished mid-reach and could never be clicked. Flush (not merely
   "closer") removes the dead strip entirely rather than shrinking it.
   z-index:1 -- same reason as .hub-unit-chip above: Fix 1's floating rail
   has no stacking context and neither does this dot, so a .c-card drifting
   over a media block (relayout()'s downward stacking has no bound on drift
   from a card's own anchor) painted over the dot with no cue it had
   happened -- reachable in a sparse rail, invisible and unclickable in a
   dense one. */
.hub-unit-dot {
  position: absolute; right: -26px; top: 0; z-index: 1; width: 26px; height: 26px; border-radius: 50%;
  background: var(--card); border: 1px solid var(--line); color: var(--accent); font-size: 0.78rem;
  padding: 0; display: none; align-items: center; justify-content: center; cursor: pointer;
}
/* The complement of the max-width:1499px query above -- see its comment for
   where 1500 comes from, and why the two numbers must differ by 1 rather
   than match. Must stay equal to focus.js's WIDE() (min-width:1500px). */
@media (min-width: 1500px) {
  .hub-bubble { display: none; }
  .hub-unit:hover .hub-unit-dot { display: flex; }
}
