/*
 * Dogfood design tokens and base styles.
 *
 * Ported from the Claude Design project "Dogfood" (Dogfood.dc.html), so the
 * Maud UI matches the design that was signed off.
 *
 * Two accents carry the whole product, and they are not interchangeable:
 *
 *   --identity  gold. Change ids, stack rails, the revision timeline — every
 *               place the UI is pointing at a *thing that keeps its name*
 *               through rewrites. This is the concept the product is about.
 *   --action    teal. Links and the one primary control per view. Ordinary
 *               navigation, never identity.
 *
 * Using identity for a button, or action for a change id, breaks the only
 * colour rule the interface has.
 *
 * NOTE — deviation from spec §1, recorded deliberately: the spec chose Tailwind.
 * This is hand-authored CSS instead, because Tailwind would put a Node
 * toolchain in the release image for what is currently a small, stable set of
 * styles. The tokens below are the same ones the design defines, so moving
 * to Tailwind later is a mechanical change rather than a redesign.
 */

:root {
  color-scheme: dark;

  /* neutrals — cool near-black, dark is primary */
  --bg: #15161a;
  --surface: #1b1d22;
  --surface-raised: #22252b;
  --border: #2c3037;
  --border-strong: #3b4048;
  --text: #e7e9ec;
  --text-dim: #989ea9;
  --text-faint: #686e79;

  /* the two accents */
  --identity: #d9a441;
  --action: #6ba9b8;
  /* Ink for text sitting *on* a filled --action surface. Dark in the dark
     theme, because the action colour is light enough to need it. */
  --on-action: #0e1417;
  /* The identity tint. Marks stacked rows and selected revisions without
     drawing a border — a border would read as a separate object. */
  --identity-wash: rgb(217 164 65 / 0.09);

  /* state */
  --open: #63a06b;
  --merged: #8b7fd4;
  --abandoned: #767d88;
  --conflict: #c77bc4;
  --danger: #d06b6b;

  /* diff */
  --diff-add-bg: #1b2f23;
  --diff-add-text: #7fcb99;
  --diff-del-bg: #33201f;
  --diff-del-text: #e39494;
  --diff-conflict-bg: #2b1f30;
  --diff-conflict-text: #d9a0d6;

  /* syntax highlighting (spec §8), cooled to sit on the near-black ground */
  --hl-comment: #6c727d;
  --hl-keyword: #c78fd0;
  --hl-string: #8fbf94;
  --hl-number: #d9a441;
  --hl-function: #6ba9b8;
  --hl-type: #d9b877;
  --hl-constant: #d9a441;
  --hl-variable: #e7e9ec;
  --hl-operator: #989ea9;
  --hl-tag: #c78fd0;
  --hl-attribute: #6ba9b8;
  --hl-punctuation: #686e79;

  /* shape */
  --radius: 3px;
  --radius-sm: 2px;

  /* Each stack keeps its system fallbacks after the hosted face, so a failed
     or blocked font request degrades to a near-enough system font rather than
     to a default serif. */
  --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    Consolas, "Liberation Mono", monospace;
  --font-condensed: "IBM Plex Sans Condensed", ui-sans-serif,
    "Roboto Condensed", "Arial Narrow", system-ui, sans-serif;

  /* Type scale, base 14/21. The half-pixel 12.5 is the design's, and it is
     load-bearing: metadata has to read as a rank below body text without
     dropping to 12, which goes fuzzy in the condensed face. */
  --text-xs: 11px;
  --text-sm: 12.5px;
  --text-base: 14px;
  --text-md: 16px;
  --text-lg: 20px;
  --text-xl: 24px;
  --text-2xl: 28px;
  --text-3xl: 44px;
}

:root.light,
:root[data-theme="light"] {
  color-scheme: light;

  --bg: #f6f6f4;
  --surface: #ffffff;
  --surface-raised: #ffffff;
  --border: #e0e0dc;
  --border-strong: #c6c7c2;
  --text: #1a1c20;
  --text-dim: #5c616b;
  --text-faint: #8a8f98;

  /* Both accents darken sharply here. They are carrying the same meanings
     against a white ground, which needs far more depth to hold contrast. */
  --identity: #9a6b12;
  --action: #20707f;
  --on-action: #ffffff;
  --identity-wash: rgb(154 107 18 / 0.08);

  --open: #2e7d3a;
  --merged: #5b4fb0;
  --abandoned: #6b7280;
  --conflict: #9b3e96;
  --danger: #b03434;

  --diff-add-bg: #e4f3e7;
  --diff-add-text: #1d6b32;
  --diff-del-bg: #fae7e6;
  --diff-del-text: #9e2b25;
  --diff-conflict-bg: #f6e9f5;
  --diff-conflict-text: #7e2b79;

  --hl-comment: #6f7580;
  --hl-keyword: #7e2b79;
  --hl-string: #2f6b34;
  --hl-number: #8a5e10;
  --hl-function: #20707f;
  --hl-type: #855c14;
  --hl-constant: #8a5e10;
  --hl-variable: #1a1c20;
  --hl-operator: #5c616b;
  --hl-tag: #7e2b79;
  --hl-attribute: #20707f;
  --hl-punctuation: #8a8f98;
}

/* ─── fonts ──────────────────────────────────────────────────────────────── */

/* Self-hosted rather than pulled from a CDN: the CSP is `font-src 'self'`, and
   a forge should not phone a third party on every page load.
   All Latin subsets — see `serve_font` in main.rs for why.
   `font-display: swap` so text is readable while the face is still in flight;
   the fallback stacks are chosen to be close enough in width that the swap
   does not reflow the page badly. */

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/plex-sans-400.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/plex-sans-500.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/plex-sans-600.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans Condensed";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/plex-condensed-500.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans Condensed";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/plex-condensed-600.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/plex-condensed-700.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-400.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-500.woff2") format("woff2");
}

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

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

::selection {
  background: color-mix(in srgb, var(--action) 32%, transparent);
  color: var(--text);
}

a,
button,
input,
select,
textarea,
[role="button"],
[tabindex] {
  transition:
    background-color 80ms ease-out,
    border-color 80ms ease-out,
    color 80ms ease-out,
    box-shadow 120ms ease-out,
    opacity 80ms ease-out;
}

a {
  color: var(--action);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

code,
pre,
.mono {
  font-family: var(--font-mono);
  font-size: 12.5px;
}

/* ─── layout ─────────────────────────────────────────────────────────────── */

/* The design runs a 1440px measure. That is far too wide for prose, so it is
   never the width of a paragraph — long-form text inside it is capped at its
   own `max-width` (see `.prose`, `.measure`). What the 1440 buys is the dense
   multi-column tables the forge is actually made of. */
.masthead-inner,
.subnav-inner,
.wrap {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 24px;
}

/* Sticky so the wordmark and the jump control stay reachable down a long diff.
   Opaque rather than translucent: the rows beneath it are 28-34px hairline
   grids, and sliding them under a blur turns the header into mush. */
.masthead {
  position: sticky;
  top: 0;
  z-index: 30;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

/* Three columns rather than a flex row with a spacer: a grid's centre column
   sizes to its content and stays put between two equal flexible columns, so
   the jump control holds the visual centre of the bar no matter how wide the
   brand/nav on one side or the account controls on the other happen to be. */
.masthead-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: 14px;
  height: 46px;
}

.masthead-group {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.masthead-group-start {
  justify-self: start;
}

.masthead-group-end {
  justify-self: end;
}

/* The wordmark: a bordered `df` tile beside the name, both monospace. The
   product is a command-line tool with a website attached, and the mark says
   so. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--text-base);
}

.brand:hover {
  text-decoration: none;
}

.brand-mark {
  width: 18px;
  height: 18px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--identity);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  color: var(--identity);
}

/* A hairline divider between groups in a horizontal bar. */
.vrule {
  width: 1px;
  height: 18px;
  flex: none;
  background: var(--border);
}

.spacer {
  flex: 1;
}

.masthead nav {
  display: flex;
  gap: 8px;
  align-items: center;
}

.masthead nav a {
  color: var(--text-dim);
  white-space: nowrap;
}

.masthead nav a:hover {
  color: var(--text);
  text-decoration: none;
}

/* The "Jump to…" control. A link, not a button: with scripting off it
   navigates to /search, which is the same destination the palette reaches by
   another route. The keycap is decorative in that case, so it is hidden from
   assistive technology and revealed as meaningful only by palette.js. */
.jump {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 24px;
  padding: 0 6px 0 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text-faint);
  font-size: var(--text-sm);
  cursor: pointer;
}

.jump:hover {
  border-color: var(--action);
  color: var(--action);
  text-decoration: none;
}

/* Condensed uppercase, because it is a state readout ("DARK") rather than a
   verb — pressing it is what changes the state. */
.theme-toggle {
  height: 24px;
  padding: 0 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}

/* ─── repository sub-bar ─────────────────────────────────────────────────── */

/* The second row of chrome, present on every page inside a repository: where
   you are, whether it is public, the four sections, and the repo's vital
   signs. Full-bleed and hairline-ruled so it reads as part of the frame
   rather than as page content. */
.subnav {
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

.subnav-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 40px;
  overflow-x: auto;
  scrollbar-width: none;
}

.subnav-inner::-webkit-scrollbar {
  display: none;
}

.subnav-path {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  white-space: nowrap;
  flex: none;
}

.subnav-path .sep {
  color: var(--text-faint);
}

.subnav-meta {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
  flex: none;
}

/* Revealed by repo-live.js when an SSE `commit` event lands (see
   `data-repo-events`). Starts `hidden`, so a visitor without scripting never
   sees it — the connection that would show it is never opened. */
.live-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 6px 3px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--open) 50%, transparent);
  background: color-mix(in srgb, var(--open) 12%, transparent);
  font-size: var(--text-xs);
  color: var(--open);
  white-space: nowrap;
  flex: none;
}

.live-banner[hidden] {
  display: none;
}

main {
  padding: 14px 0 48px;
}

/* A page built from edge-to-edge bands owns its own vertical rhythm: each band
   carries its padding and its closing rule, so `main` adds neither. */
main.flush {
  padding: 0;
}

/* ─── the ⌘K palette ─────────────────────────────────────────────────────── */

/* Anchored near the top rather than centred: the list grows downward as you
   type, and a vertically-centred panel would shift the row under the cursor
   on every keystroke. */
.palette-backdrop {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  justify-content: center;
  padding: 96px 16px 16px;
  background: rgb(10 11 13 / 0.55);
}

.palette-backdrop[hidden] {
  display: none;
}

.palette {
  width: 560px;
  max-width: 100%;
  height: max-content;
  max-height: calc(100vh - 128px);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-raised);
  overflow: hidden;
}

.palette-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 10px;
  border-bottom: 1px solid var(--border);
  flex: none;
}

.palette-prompt {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--identity);
}

.palette-input {
  flex: 1;
  height: 100%;
  border: none;
  background: transparent;
  padding: 0;
  font-family: var(--font-mono);
  font-size: var(--text-base);
}

.palette-input:focus {
  outline: none;
  border-color: transparent;
}

/* Chrome and Safari draw their own clear button inside a search input; it
   collides with the esc keycap. */
.palette-input::-webkit-search-cancel-button {
  display: none;
}

.palette-results {
  overflow-y: auto;
  padding-bottom: 4px;
}

.palette-group-label {
  padding: 6px 10px 2px;
}

.palette-item {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 26px;
  padding: 0 10px;
  width: 100%;
  border: none;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.palette-item:hover,
.palette-item.is-active {
  background: var(--surface);
  text-decoration: none;
}

.palette-glyph {
  width: 12px;
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--action);
}

.palette-label {
  font-size: var(--text-sm);
  white-space: nowrap;
  flex: none;
}

.palette-label.mono {
  font-family: var(--font-mono);
  color: var(--identity);
  font-weight: 500;
}

/* The context line gives up its space first — the label is what you are
   aiming at. */
.palette-hint {
  font-size: var(--text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1;
}

.palette-all {
  border-top: 1px solid var(--border);
  margin-top: 4px;
  padding-top: 4px;
}

.palette-empty,
.palette-foot {
  padding: 10px;
  font-size: var(--text-sm);
}

.palette-foot {
  border-top: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  flex: none;
}

/* ─── typography ─────────────────────────────────────────────────────────── */

h1 {
  font-size: var(--text-lg);
  line-height: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 6px;
}

h2 {
  font-size: var(--text-md);
  line-height: 24px;
  font-weight: 600;
  margin: 0 0 10px;
}

.lede {
  color: var(--text-dim);
  margin: 0 0 24px;
  max-width: 60ch;
}

.dim {
  color: var(--text-dim);
}

.faint {
  color: var(--text-faint);
}

/* The eyebrow. Condensed, uppercase, tracked out — used for every section
   label in the design, and never for anything a reader has to actually read. */
.label-condensed {
  font-family: var(--font-condensed);
  font-weight: 500;
  font-size: var(--text-xs);
  line-height: 16px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Display type: the landing headline and the change-detail id. Sans rather
   than condensed, tight tracking, and only ever used at 24px and above. */
.display {
  font-size: var(--text-3xl);
  line-height: 48px;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-wrap: pretty;
  margin: 0;
}

@media (max-width: 800px) {
  .display {
    font-size: 30px;
    line-height: 34px;
  }
}

.tnum {
  font-variant-numeric: tabular-nums;
}

/* Prose caps its own line length regardless of how wide the column is. */
.measure {
  max-width: 72ch;
  text-wrap: pretty;
}

/* A keycap. Bottom border doubled so it reads as a physical key at 11px
   without needing a shadow. */
.kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 16px;
  min-width: 16px;
  padding: 0 4px;
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

/* A key/value row joined by a dotted leader — the design's stat blocks. The
   leader is a flexing border rather than a repeated character so it lands on
   the same baseline whatever the two ends are. */
.dotline {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: var(--text-sm);
}

.dotline > .dotline-key {
  color: var(--text-dim);
}

/* The leader sits in source order after the value but is ordered before it,
   so the markup stays "key, value" and reads correctly to a screen reader. */
.dotline::after {
  content: "";
  flex: 1;
  border-bottom: 1px dotted var(--border);
}

.dotline > .dotline-val {
  order: 3;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.float-shadow {
  box-shadow: 0 8px 28px rgb(0 0 0 / 0.4);
}

:root.light .float-shadow,
:root[data-theme="light"] .float-shadow {
  box-shadow: 0 8px 28px rgb(0 0 0 / 0.12);
}

/* ─── surfaces ───────────────────────────────────────────────────────────── */

/* The generic bordered block, used by the settings, profile, org and form
   pages. 14px rather than 20 — the whole system moved a density step, and a
   panel that still breathes like the old one reads as a different product. */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
}

.panel + .panel {
  margin-top: 16px;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.row {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ─── controls ───────────────────────────────────────────────────────────── */

/* Secondary is the default. Transparent, hairline, and it reaches for
   `--action` only on hover — a page full of filled buttons has no primary. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: var(--text-sm);
  white-space: nowrap;
  cursor: pointer;
}

.btn:hover {
  border-color: var(--action);
  color: var(--action);
  text-decoration: none;
}

/* One per view. Filled `--action` — the single move the page is asking for. */
.btn-primary {
  background: var(--action);
  border-color: var(--action);
  color: var(--on-action);
  font-weight: 500;
}

.btn-primary:hover {
  filter: brightness(1.08);
  color: var(--on-action);
  border-color: var(--action);
}

.btn-danger {
  border-color: var(--danger);
  color: var(--danger);
}

.btn-danger:hover {
  border-color: var(--danger);
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}

/* Sign out is destructive enough to warn about but too routine to shout, so it
   sits quiet in the masthead and only reddens on approach. */
.btn-quiet-danger {
  background: transparent;
  border-color: transparent;
  color: var(--text-dim);
}

.btn-quiet-danger:hover {
  border-color: color-mix(in srgb, var(--danger) 50%, transparent);
  color: var(--danger);
}

.btn[disabled],
.btn[aria-disabled="true"] {
  border-color: var(--border);
  color: var(--text-faint);
  cursor: default;
}

.btn[disabled]:hover,
.btn[aria-disabled="true"]:hover {
  border-color: var(--border);
  color: var(--text-faint);
}

/* The compact monospace button used inside dense bars — blame, replay, edit,
   copy. Small enough that it has to be mono to stay legible. */
.btn-mono {
  height: 24px;
  padding: 0 8px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-dim);
}

.btn-mono[aria-pressed="true"] {
  border-color: var(--action);
  color: var(--action);
}

input[type="text"],
input[type="password"],
input[type="search"],
input[type="email"],
textarea {
  width: 100%;
  padding: 0 8px;
  height: 30px;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--text);
  font: inherit;
  font-size: var(--text-base);
}

textarea {
  height: auto;
  min-height: 64px;
  padding: 6px 8px;
  line-height: 21px;
  resize: vertical;
}

input:focus,
textarea:focus,
select:focus {
  border-color: var(--action);
}

input:focus,
textarea:focus,
.btn:focus-visible,
a:focus-visible {
  outline: 2px solid var(--action);
  outline-offset: 1px;
}

.field {
  margin-bottom: 14px;
}

.field label {
  display: block;
  margin-bottom: 5px;
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.hint {
  margin-top: 5px;
  font-size: var(--text-sm);
  color: var(--text-faint);
}

/* ─── chips and states ───────────────────────────────────────────────────── */

.chip {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-dim);
  white-space: nowrap;
}

/* The change id, rendered inline rather than boxed.
   A jj change id has a *shortest unique prefix* — the part you actually type
   and paste. The design splits it: the prefix carries `--identity` at weight
   500, the remainder drops to `--text-faint`. Both halves are selectable as
   one string, so copying still yields the whole id. This is the single most
   repeated element in the product; everything else defers to it. */
.cid {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.cid > .cid-p {
  color: var(--identity);
  font-weight: 500;
}

.cid > .cid-r {
  color: var(--text-faint);
}

a.cid:hover {
  text-decoration: none;
}

a.cid:hover > .cid-p {
  text-decoration: underline;
}

/* Synthetic ids get no identity colour: a plain-git change has a derived id,
   and presenting it as a real one would be a lie the reader cannot detect. */
.cid-synthetic > .cid-p {
  color: var(--text-dim);
  font-weight: 400;
}

/* State pills are outlined in their state colour, not filled with it. Four
   states appear side by side in listings, and four filled blocks of colour
   read as a chart rather than as metadata. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 20px;
  padding: 0 6px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}

/* The state glyph. Decorative — the pill's text already names the state — so
   call sites mark it `aria-hidden`. */
.badge > .glyph {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1;
}

.badge-open { color: var(--open); border-color: var(--open); }
.badge-merged { color: var(--merged); border-color: var(--merged); }
.badge-abandoned { color: var(--abandoned); border-color: var(--abandoned); }
.badge-conflict { color: var(--conflict); border-color: var(--conflict); }
.badge-draft { color: var(--text-dim); border-color: var(--border-strong); }

/* ─── notices ────────────────────────────────────────────────────────────── */

.banner {
  padding: 10px 12px;
  font-size: var(--text-sm);
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  margin-bottom: 20px;
}

.banner-error {
  border-color: color-mix(in srgb, var(--danger) 50%, transparent);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
}

.banner-ok {
  border-color: color-mix(in srgb, var(--open) 50%, transparent);
  background: color-mix(in srgb, var(--open) 12%, transparent);
}

/* ─── empty states ───────────────────────────────────────────────────────── */

/* An empty state is a solid bordered block like every other listing, not a
   dashed placeholder: on this system a dashed border means "an action goes
   here" (see `.repo-card-new`), and an empty change list is not an action. */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
  padding: 28px 20px;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.empty h2 {
  margin: 0;
  font-size: var(--text-md);
  line-height: 24px;
  color: var(--text);
}

.empty p {
  margin: 0;
  font-size: var(--text-sm);
}

/* ─── code blocks ────────────────────────────────────────────────────────── */


/* Protocol toggle (HTTPS/SSH clone instructions). Two radios drive which
   panel shows via a sibling selector — no script needed, so the choice works
   identically with JavaScript on or off. */
.proto-toggle {
  margin-top: 14px;
}

.proto-toggle input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.proto-tabs {
  display: inline-flex;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.proto-tab {
  padding: 3px 12px;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
}

.proto-tab + .proto-tab {
  border-left: 1px solid var(--border);
}

.proto-tab:hover {
  background: var(--surface-raised);
  color: var(--text);
}

#proto-https:focus-visible ~ .proto-tabs label[for="proto-https"],
#proto-ssh:focus-visible ~ .proto-tabs label[for="proto-ssh"] {
  outline: 2px solid var(--action);
  outline-offset: 2px;
}

.proto-panel {
  display: none;
}

#proto-https:checked ~ #panel-https,
#proto-ssh:checked ~ #panel-ssh {
  display: block;
}

#proto-https:checked ~ .proto-tabs label[for="proto-https"],
#proto-ssh:checked ~ .proto-tabs label[for="proto-ssh"] {
  background: var(--action);
  color: var(--on-action);
}

footer {
  border-top: 1px solid var(--border);
  margin-top: 0;
  padding: 16px 0;
  background: var(--surface);
  color: var(--text-faint);
  font-size: var(--text-sm);
}

footer .wrap {
  display: flex;
  gap: 8px 16px;
  align-items: center;
  flex-wrap: wrap;
}

footer a {
  color: var(--text-faint);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

footer a:hover {
  color: var(--action);
}

.footer-mark {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--text-faint);
}

/* The spacer collapses to nothing once the row wraps, which is what keeps the
   copyright from stranding itself on a line of its own. */
.footer-end {
  font-size: var(--text-xs);
}

/* Respect a user's reduced-motion preference (spec §11 accessibility pass). */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

.live-indicator {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--merged);
  animation: live-pulse 2s ease-in-out infinite;
}

@keyframes live-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}


/* ─── full-bleed bands ─────────────────────────────────────────────────────── */

/* The landing page is a stack of edge-to-edge bands, alternating ground and
   surface, each separated by a single hairline. There are no cards and no
   shadows: the rules do all the dividing. */
.band {
  border-bottom: 1px solid var(--border);
}

/* The footer draws its own top rule, so the last band must not draw one too —
   two hairlines a pixel apart read as a rendering fault. */
main.flush > .band:last-child {
  border-bottom: none;
}

.band-surface {
  background: var(--surface);
}

.band > .wrap {
  padding-top: 32px;
  padding-bottom: 32px;
}

.band-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.band-head h2 {
  margin: 0;
  font-size: var(--text-lg);
  line-height: 28px;
}

.band-note {
  font-size: var(--text-sm);
  color: var(--text-dim);
}

/* ─── hero ─────────────────────────────────────────────────────────────────── */

.hero > .wrap {
  padding-top: 44px;
  padding-bottom: 44px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: 44px;
  align-items: center;
}

.hero-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}

/* An outlined tag, not a filled one. The hero already has a primary button;
   a second saturated block above it would compete with it. */
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 6px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}

/* 15ch is what puts the break after "pointer." — the headline is a two-part
   sentence and the line break is doing the punctuation. */
.hero-title {
  max-width: 15ch;
}

.hero-lede {
  margin: 0;
  font-size: var(--text-md);
  line-height: 24px;
  color: var(--text-dim);
  max-width: 44ch;
  text-wrap: pretty;
}

.hero-lede .mono {
  color: var(--text);
}

.hero-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  padding-top: 4px;
}

.hero-actions .btn {
  height: 32px;
  padding: 0 12px;
  font-size: var(--text-base);
}

/* The clone line. A `$` and a command — the first thing a visitor actually
   needs, so it sits in the hero rather than three scrolls down. */
.clone-box {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 30px;
  max-width: 100%;
  padding: 0 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  overflow-x: auto;
}

.clone-box .prompt {
  color: var(--text-faint);
  flex: none;
}

.clone-box code {
  color: var(--text);
  white-space: nowrap;
  background: none;
  padding: 0;
  border: 0;
}

/* The block form of the clone box, used on the empty-repository page where the
   command sits on its own line rather than inline in a hero. */
.proto-panel .clone-box {
  height: auto;
  padding: 8px 10px;
}

/* ─── the terminal illustration ────────────────────────────────────────────── */

/* A transcript, not a live console. It is captioned as an example and it never
   animates: a fake terminal that types at you is indistinguishable from one
   showing real state, and this one is showing neither. */
.term {
  margin: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg);
  overflow: hidden;
}

.term-bar,
.term-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  background: var(--surface-raised);
  flex: none;
}

.term-bar {
  height: 28px;
  border-bottom: 1px solid var(--border);
}

.term-foot {
  padding: 6px 12px;
  border-top: 1px solid var(--border);
  font-size: var(--text-sm);
  color: var(--text-dim);
}

.term-dots {
  display: flex;
  gap: 4px;
  flex: none;
}

.term-dots span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--border-strong);
}

.term-host {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

.term-body {
  margin: 0;
  padding: 10px 12px;
  min-height: 300px;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 20px;
  overflow-x: auto;
  white-space: pre;
}

.term-cmd {
  color: var(--text);
}

.term-cmd::before {
  content: "$ ";
  color: var(--identity);
}

.term-out { color: var(--text-dim); }
.term-out.is-faint { color: var(--text-faint); }
.term-out.is-add { color: var(--diff-add-text); }
.term-out.is-action { color: var(--action); }
.term-out.is-open { color: var(--open); }
.term-out.is-conflict { color: var(--conflict); }
.term-out.is-identity { color: var(--identity); }

/* ─── terminal animation ───────────────────────────────────────────────────── */

/* While animating, the body keeps its min-height so it does not collapse. */
.term-body.term-animating {
  min-height: 300px;
}

/* Lines start hidden and are revealed by adding .term-line-visible, rather
   than via inline styles: the CSP's style-src has no 'unsafe-inline'. */
.term-body.term-animating [data-term-line] {
  visibility: hidden;
  height: 0;
  overflow: hidden;
}

.term-body.term-animating [data-term-line].term-line-visible {
  visibility: visible;
  height: auto;
  overflow: visible;
}

/* The blinking cursor for the line currently being typed. */
.term-typing::after {
  content: "";
  display: inline-block;
  width: 0.55em;
  height: 1.15em;
  margin-left: 1px;
  vertical-align: text-bottom;
  background: var(--identity);
  animation: term-blink 0.6s steps(2, start) infinite;
}

@keyframes term-blink {
  to { opacity: 0; }
}

/* Once animation is done, no residual style. */
.term-body.term-ready [data-term-line] {
  visibility: visible !important;
  height: auto !important;
  overflow: visible !important;
}

/* ─── ticker ───────────────────────────────────────────────────────────────── */

/* The design scrolls this. It does not scroll here: it is decoration carrying
   six words of copy, and an infinite marquee is a permanent moving object on
   the page for anyone who did not ask for one. It wraps instead. */
.ticker {
  display: flex;
  flex-wrap: wrap;
  overflow: hidden;
}

.ticker-item {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 34px;
  padding: 0 14px;
  white-space: nowrap;
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.ticker-dot {
  font-family: var(--font-mono);
  color: var(--identity);
}

/* ─── two-column body ──────────────────────────────────────────────────────── */

.columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 260px;
  gap: 32px;
  align-items: start;
}

.columns-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.columns-aside {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

/* Each aside block is separated by a rule rather than boxed. The last one
   drops its rule so the column does not end on a line to nowhere. */
.aside-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}

.aside-block:last-child {
  padding-bottom: 0;
  border-bottom: none;
}

.aside-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ─── activity feed ────────────────────────────────────────────────────────── */

.feed {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  margin: 0;
  padding: 0;
  list-style: none;
}

.feed-row {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding: 0 10px 0 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--text-sm);
}

.feed-row:last-child {
  border-bottom: none;
}

.feed-row:hover {
  background: var(--surface-raised);
}

/* The event glyph, in the colour of what happened. */
.feed-glyph {
  width: 22px;
  flex: none;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.feed-glyph.is-push { color: var(--action); }
.feed-glyph.is-review { color: var(--open); }
.feed-glyph.is-merge { color: var(--merged); }
.feed-glyph.is-conflict { color: var(--conflict); }
.feed-glyph.is-abandon { color: var(--abandoned); }
.feed-glyph.is-open { color: var(--identity); }

.feed-repo {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  white-space: nowrap;
  flex: none;
  color: var(--text-faint);
}

.feed-repo .owner {
  color: var(--identity);
  font-weight: 500;
}

.feed-verb {
  color: var(--text-dim);
  white-space: nowrap;
  flex: none;
}

/* The one element allowed to be cut off. Everything else in the row is a
   fixed-width fact; the title is the part that can be finished by clicking. */
.feed-title {
  font-size: var(--text-base);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1;
}

.feed-age {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
  flex: none;
  width: 56px;
  text-align: right;
}

.feed-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-sm);
  color: var(--text-faint);
}

/* A live-ish dot. It does not blink — the page is server-rendered and does not
   update itself, so a pulsing indicator would be claiming something false. */
.live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--open);
  display: inline-block;
}

.live-indicator {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

/* ─── compact aside rows ───────────────────────────────────────────────────── */

/* A change in an aside list: rail, glyph, id, title. The rail is what marks it
   as part of a stack. */
.mini-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
  font-size: var(--text-sm);
  min-width: 0;
}

.mini-row:hover {
  background: var(--surface);
  text-decoration: none;
}

.mini-rail {
  width: 2px;
  align-self: stretch;
  background: var(--identity);
  flex: none;
}

.mini-glyph {
  width: 14px;
  flex: none;
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.mini-title {
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.mini-age {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  margin-left: auto;
  flex: none;
}

/* ─── bookmark rows ────────────────────────────────────────────────────────── */

.bookmark-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-sm);
  color: var(--text-dim);
}

.bookmark-line:hover {
  text-decoration: none;
}

.bookmark-line:hover .chip {
  border-color: var(--action);
  color: var(--action);
}

.bookmark-flag {
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.bookmark-flag.is-diverged {
  color: var(--conflict);
}

/* ─── why switch ───────────────────────────────────────────────────────────── */

.compare {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Each card is literally a two-line diff: the thing you put up with, then the
   thing you get. The pitch is stated in the product's own notation. */
.compare-item {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  overflow: hidden;
}

.compare-them,
.compare-us {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 8px 10px;
  font-size: var(--text-base);
  text-wrap: pretty;
}

.compare-them {
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
}

.compare-us {
  background: var(--identity-wash);
  color: var(--text);
}

.compare-sign {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  flex: none;
}

.compare-them .compare-sign { color: var(--danger); }
.compare-us .compare-sign { color: var(--identity); }

/* ─── repo cards ───────────────────────────────────────────────────────────── */

.repo-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.repo-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

.repo-card:hover {
  border-color: var(--action);
  text-decoration: none;
}

.repo-card-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: var(--text-base);
  color: var(--action);
}

.repo-card-desc {
  font-size: var(--text-sm);
  line-height: 18px;
  color: var(--text-dim);
  text-wrap: pretty;
  overflow-wrap: anywhere;
}

.repo-card-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 2px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

.repo-card-meta .is-open { color: var(--open); }
.repo-card-meta .is-conflict { color: var(--conflict); }
.repo-card-meta .at-end { margin-left: auto; }

/* A dashed slot rather than a filled card: it is an action in a list of
   objects, and it should not look like one of the objects. */
.repo-card-new {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 84px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--text-dim);
}

.repo-card-new:hover {
  border-color: var(--action);
  color: var(--action);
  text-decoration: none;
}

/* The dashboard sidebar is a scan-first list rather than the card grid used
   on the landing page. Keep one shared repository item, but join the items
   into a single compact surface with separators. */
.repo-list {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.repo-list .repo-card {
  gap: 4px;
  padding: 10px 12px;
  border: 0;
  border-radius: 0;
  background: transparent;
}

.repo-list .repo-card + .repo-card {
  border-top: 1px solid var(--border);
}

.repo-list .repo-card:hover {
  background: var(--surface-raised);
}

.repo-list .repo-card-new {
  justify-content: flex-start;
  min-height: 0;
  padding: 9px 12px;
  border: 0;
  border-top: 1px dashed var(--border-strong);
  border-radius: 0;
}

/* ─── call to action ───────────────────────────────────────────────────────── */

.cta > .wrap {
  padding-top: 40px;
  padding-bottom: 40px;
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.cta-title {
  margin: 0;
  font-size: var(--text-xl);
  line-height: 32px;
  font-weight: 600;
  text-wrap: pretty;
}

.cta-lede {
  margin: 0;
  font-size: var(--text-base);
  color: var(--text-dim);
  text-wrap: pretty;
}

/* ─── dashboard ────────────────────────────────────────────────────────────── */

.dash-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 14px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

.dash-title {
  margin: 0;
  font-size: var(--text-xl);
  line-height: 32px;
  font-weight: 600;
}

.dash-name {
  color: var(--identity);
}

.dash-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.dash-section + .dash-section {
  margin-top: 20px;
}

.section-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.section-note {
  margin: 0;
  font-size: var(--text-sm);
}

/* ─── watched activity ─────────────────────────────────────────────────────── */

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

/* A left rule instead of a box: these are one-line statements, and forty of
   them in forty boxes is unreadable. */
.activity-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 8px;
  border-left: 2px solid var(--border);
  font-size: var(--text-sm);
  color: var(--text-dim);
  flex-wrap: wrap;
}

.activity-actor {
  color: var(--text);
  font-weight: 500;
}

.activity-when {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
}

/* ─── responsive ───────────────────────────────────────────────────────────── */

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

  .compare {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .repo-cards {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 800px) {
  .hero > .wrap {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
  }

  .compare,
  .repo-cards {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The feed loses its verb and repo columns before it loses the title. */
  .feed-verb,
  .feed-repo {
    display: none;
  }
}
/* ─── design system pages ──────────────────────────────────────────────────── */

.design-head,
.rationale > header {
  padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
}

.design-title {
  margin: 0 0 6px;
  font-size: var(--text-xl);
  line-height: 32px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* The type specimen table: a fixed spec column and the sample beside it, so
   the sizes line up down the page and can be compared. */
.type-specimens {
  display: flex;
  flex-direction: column;
}

.type-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding-bottom: 6px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--border);
}

.type-spec {
  width: 96px;
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

.state-list {
  display: flex;
  flex-direction: column;
  max-width: 560px;
}

.state-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 6px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--border);
  font-size: var(--text-sm);
}

.design-section {
  padding: 28px 0;
  border-bottom: 1px solid var(--border);
}

.design-section:last-child {
  border-bottom: none;
}

.design-section-title {
  margin: 0 0 16px;
  font-size: var(--text-md);
}

.design-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}

.swatch-group-title {
  margin: 16px 0 8px;
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px;
}

.swatch {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: var(--text-xs);
}

/* Bordered so a swatch whose colour is close to the surface it sits on is
   still legible as a swatch. */
.swatch-chip {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}

.swatch-name {
  font-size: var(--text-xs);
}

.type-specimens > * {
  margin: 0 0 12px;
}

.rationale {
  max-width: 68ch;
}

.rationale section {
  margin-top: 28px;
}

.rationale h2 {
  font-size: var(--text-md);
  margin-bottom: 8px;
}

.rationale p {
  line-height: 1.6;
  margin: 0 0 12px;
}

/* ─── page headings ────────────────────────────────────────────────────────── */

/* Everything that used to live in a boxed repository header now lives in the
   sub-bar (see `.subnav`), so all that is left here is the heading block a
   full-width page opens with. */

/* ─── sign in ──────────────────────────────────────────────────────────────── */

.signin {
  max-width: 380px;
  margin: 48px auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.signin-intro {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.signin-title {
  margin: 0;
  font-size: var(--text-xl);
  line-height: 32px;
  text-wrap: balance;
}

/* The live methods are boxed together; anything outside the box is context,
   not a way in. */
.signin-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.btn-block {
  display: flex;
  justify-content: center;
  width: 100%;
  height: 34px;
  font-size: var(--text-base);
}

/* A control that is present but not offered. Not a `<button disabled>` because
   it is a `<span>` — there is nothing to submit, and nothing to focus. */
.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.is-disabled:hover {
  border-color: var(--border-strong);
  color: var(--text);
}

.signin-or {
  display: flex;
  align-items: center;
  gap: 8px;
}

.signin-rule {
  flex: 1;
  height: 1px;
  background: var(--border);
}

.signin-note {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  font-size: var(--text-sm);
}

.signin-note code {
  font-size: 12px;
  color: var(--text);
  background: none;
  border: 0;
  padding: 0;
  overflow-x: auto;
}

/* ─── segmented control ────────────────────────────────────────────────────── */

/* Two links styled as one control. Links rather than buttons because each half
   is a real, addressable URL — see `blob_view`. */
.segmented {
  display: inline-flex;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.segmented-item {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-dim);
}

.segmented-item + .segmented-item {
  border-left: 1px solid var(--border);
}

.segmented-item:hover {
  background: var(--surface-raised);
  color: var(--text);
  text-decoration: none;
}

.segmented-item.is-on {
  background: var(--action);
  color: var(--on-action);
}

.segmented-item.is-on:hover {
  background: var(--action);
  color: var(--on-action);
}

/* ─── file listing ─────────────────────────────────────────────────────────── */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  text-transform: uppercase;
  user-select: none;
}

.tree-crumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

/* ─── bookmark switcher ────────────────────────────────────────────────────── */

/* A `<details>`, so it opens and closes with no JavaScript at all. */
.switcher {
  position: relative;
  flex: none;
}

.switcher > summary {
  list-style: none;
  cursor: pointer;
}

.switcher > summary::-webkit-details-marker {
  display: none;
}

.switcher-menu {
  position: absolute;
  z-index: 20;
  top: calc(100% + 4px);
  left: 0;
  min-width: 220px;
  max-height: 320px;
  overflow-y: auto;
  padding-bottom: 4px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-raised);
  box-shadow: 0 8px 28px rgb(0 0 0 / 0.4);
}

.switcher-label {
  padding: 6px 10px 2px;
}

.switcher-item {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 26px;
  padding: 0 10px;
  color: var(--text);
  font-size: var(--text-sm);
}

.switcher-item:hover {
  background: var(--surface);
  text-decoration: none;
}

.switcher-item.is-current {
  background: var(--identity-wash);
}

.switcher-item.is-current .mono {
  color: var(--identity);
}

/* A single-bookmark repository has nothing to switch to, so the control
   becomes a readout. */
.btn-mono.is-static {
  cursor: default;
  color: var(--text-dim);
}

.btn-mono.is-static:hover {
  border-color: var(--border-strong);
  color: var(--text-dim);
}

/* ─── the listing ──────────────────────────────────────────────────────────── */

/* The tip-of-branch bar. Inside the listing's border rather than stacked on
   top of it, so the two are literally one object. */
.commit-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-raised);
  font-size: var(--text-sm);
}

/* The identity rail. Two pixels of gold saying "this row is about a change",
   the same mark the stack rails and revision timeline use. */
.commit-bar-rail {
  width: 2px;
  height: 14px;
  flex: none;
  background: var(--identity);
}

.commit-bar-author {
  font-weight: 500;
  flex-shrink: 0;
}

.commit-bar-msg {
  min-width: 0;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.commit-bar-when {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

@media (max-width: 620px) {
  .commit-bar .cid {
    display: none;
  }
}

/* ─── one commit ─── */

/* The header of the commit page: the message, who wrote it, and where the
   commit sits in history. Three bands separated by rules, because they answer
   three different questions and a reader is usually after exactly one. */
.commit-meta {
  padding: 0;
  margin-bottom: 10px;
}

.commit-msg {
  padding: 12px 14px;
}

.commit-msg h1 {
  margin: 0;
  font-size: var(--text-lg);
  line-height: 1.35;
}

/* The trailing body of a commit message is preformatted text — wrapped rather
   than scrolled, since a paragraph someone hard-wrapped at 72 columns is still
   prose and should read like it. */
.commit-body {
  margin: 8px 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-dim);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.commit-byline,
.commit-ids {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  font-size: var(--text-sm);
  color: var(--text-dim);
}

.commit-ids {
  background: var(--surface-raised);
  border-radius: 0 0 var(--radius) var(--radius);
}

.commit-parent {
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

.commit-parent:hover { color: var(--action); }

/* The bordered container shared by the file listing, the bookmarks table and
   the README panel. */
.filelist {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.filelist + .filelist {
  margin-top: 12px;
}

/* Fixed layout is what makes the `max-width: 0` ellipsis trick on
   `.filelist-message` / `.bookmark-title` actually hold: under the default auto
   layout the browser sizes columns from content instead, so a long commit
   message pushes the row wider rather than truncating. */
.filelist table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

.filelist thead th {
  padding: 6px 8px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.filelist tbody tr {
  border-bottom: 1px solid var(--border);
}

.filelist tbody tr:last-child {
  border-bottom: none;
}

.filelist tbody tr:hover {
  background: var(--surface-raised);
}

.filelist td {
  padding: 0 8px;
  height: 28px;
  vertical-align: middle;
}

.icon-dir {
  color: var(--action);
}

.icon-file {
  color: var(--text-faint);
}

/* The icon shares the name's cell rather than owning a column of its own.
   As its own column it was a fixed 28px, and any slack the table had to hand
   out widened it, so the icon stayed pinned left while the filename started
   further and further right — worst below the breakpoints that drop the
   message and change columns. In one cell the two are always 8px apart. */
.filelist-name {
  width: calc(32% + 28px);
}

.filelist-entry {
  display: flex;
  align-items: center;
  min-width: 0;
}

.filelist-entry-link {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  color: var(--text);
}

.filelist-entry-link svg {
  flex: none;
}

/* `line-height: 1` here, not just on `.filelist-entry-link`: the body default
   of 1.5 gives the name span extra leading that JetBrains Mono splits
   unevenly above/below the glyphs, so the text visibly hangs below the
   icon's center instead of sitting beside it. */
.filelist-entry-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1;
}

.filelist-entry-link.is-dir {
  font-weight: 500;
}

/* The underline goes on the text, not the link: on the link the rule would
   run under the icon as well. */
.filelist tr:hover .filelist-entry-link {
  color: var(--action);
}

.filelist tr:hover .filelist-entry-name {
  text-decoration: underline;
}

.filelist-note {
  flex: none;
  margin-left: 8px;
  font-size: var(--text-xs);
}

/* The commit message column. `max-width: 0` is the "shrink to let the sibling
   ellipsis rule take over" trick — without it an auto-layout table lets a long
   message push the row wider instead of truncating. */
.filelist-message {
  max-width: 0;
  font-size: var(--text-xs);
}

.filelist-message-link,
.filelist-message-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
  max-width: 100%;
  vertical-align: bottom;
  color: var(--text-dim);
}

.filelist-message-link:hover {
  color: var(--action);
  text-decoration: underline;
}

.filelist-change {
  width: 132px;
  white-space: nowrap;
  font-size: var(--text-xs);
}

.filelist-when {
  width: 72px;
  text-align: right;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

/* The blanket `thead th` rule left the header hugging the left edge of a
   column whose ages are set flush right. Follow the cells. */
.filelist thead th.filelist-when {
  text-align: right;
}

/* Below the wide breakpoint the listing keeps only what a filename needs. */
@media (max-width: 1120px) {
  .filelist-change {
    display: none;
  }
}

@media (max-width: 800px) {
  .filelist-message {
    display: none;
  }

  /* The message column was the one soaking up the table's leftover width;
     with it gone, hand the slack to the name instead of letting fixed layout
     split it across every remaining column. */
  .filelist-name {
    width: auto;
  }
}

/* ─── the repository sidebar ───────────────────────────────────────────────── */

/* Narrower than the landing page's aside: it holds stat lines and clone
   commands, not feed rows. */
.columns-repo {
  grid-template-columns: minmax(0, 1fr) 240px;
  gap: 24px;
}

.aside-about {
  font-size: var(--text-sm);
  line-height: 18px;
  color: var(--text-dim);
  text-wrap: pretty;
}

.aside-clone {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* The command scrolls inside its own box rather than widening the column —
   an SSH URL is longer than 240px and always will be. */
.aside-clone code {
  padding: 4px 6px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text);
  white-space: nowrap;
  overflow-x: auto;
}

/* ─── bookmarks page ───────────────────────────────────────────────────────── */

.bookmark-table td {
  height: 30px;
  font-size: var(--text-sm);
}

/* Explicit width so fixed table layout does not split the remaining space
   between this column and `.bookmark-title` — the title is the one column
   meant to flex and truncate. */
.bookmark-name {
  width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bookmark-name .mono {
  color: var(--text);
}

.bookmark-points {
  width: 132px;
  white-space: nowrap;
}

.bookmark-title {
  max-width: 0;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bookmark-table thead th:nth-child(4) {
  text-align: right;
}

.bookmark-when {
  width: 72px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

/* The heading block above a full-width page table. */
.page-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

/* ─── readme ───────────────────────────────────────────────────────────────── */

.readme-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-raised);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1;
}

/* A README is prose. The listing above it can use the full 1440; this cannot,
   so it caps its own measure regardless of how wide the column is. */
.readme-body {
  padding: 16px;
  max-width: 76ch;
}

.readme-body > :first-child {
  margin-top: 0;
}

.readme-body > :last-child {
  margin-bottom: 0;
}

/* Typography for rendered markdown: READMEs, issue/change descriptions, and
   comments all go through the same comrak pipeline and land here via
   `PreEscaped`, so one set of tag selectors styles every one of them. */
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
  font-family: var(--font-condensed);
  font-weight: 600;
  color: var(--text);
  line-height: 1.25;
}

.markdown-body h1 {
  margin: 24px 0 12px;
  font-size: var(--text-xl);
  font-weight: 700;
}

.markdown-body h2 {
  margin: 28px 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
  font-size: var(--text-lg);
}

.markdown-body h3 {
  margin: 20px 0 8px;
  font-size: var(--text-md);
}

.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
  margin: 16px 0 6px;
  font-size: var(--text-base);
  font-weight: 500;
}

.markdown-body p {
  margin: 12px 0;
  line-height: 1.6;
  color: var(--text-dim);
}

.markdown-body a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-color: var(--border-strong);
  text-underline-offset: 2px;
}

.markdown-body a:hover {
  text-decoration-color: var(--action);
  color: var(--action);
}

.markdown-body strong {
  font-weight: 600;
  color: var(--text);
}

.markdown-body code {
  padding: 0.1em 0.35em;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--action);
  font-family: var(--font-mono);
  font-size: 0.85em;
}

.markdown-body pre {
  margin: 16px 0;
  padding: 12px;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.markdown-body pre code {
  padding: 0;
  border: none;
  background: none;
  color: var(--text-dim);
  font-size: var(--text-sm);
  line-height: 1.6;
}

.markdown-body blockquote {
  margin: 16px 0;
  padding-left: 16px;
  border-left: 2px solid var(--action);
  color: var(--text-dim);
  font-style: italic;
}

.markdown-body hr {
  margin: 24px 0;
  border: none;
  border-top: 1px solid var(--border);
}

.markdown-body ul,
.markdown-body ol {
  margin: 12px 0;
  padding-left: 22px;
  color: var(--text-dim);
  line-height: 1.6;
}

.markdown-body li + li {
  margin-top: 4px;
}

.markdown-body li:has(> input[type="checkbox"]) {
  list-style: none;
  margin-left: -22px;
}

.markdown-body table {
  width: 100%;
  margin: 16px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.markdown-body th {
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  text-align: left;
  font-family: var(--font-condensed);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
}

.markdown-body td {
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
  vertical-align: top;
}

.markdown-body tr:last-child td {
  border-bottom: none;
}

.markdown-body img {
  max-width: 100%;
}

/* ─── code listings ────────────────────────────────────────────────────────── */

.codeblock {
  overflow-x: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.codetable {
  border-collapse: collapse;
  width: 100%;
}

.codetable .lineno {
  text-align: right;
  padding: 0 10px;
  user-select: none;
  width: 1%;
  vertical-align: top;
}

.codetable .lineno a {
  color: inherit;
}

.codetable .codeline {
  padding: 0 10px;
  white-space: pre;
}

/* The link target for a permalinked line. `:target` needs no JavaScript, which
   is what keeps line links working with scripting disabled (spec §7). */
.codetable tr:target {
  background: color-mix(in srgb, var(--identity) 14%, transparent);
}

/* ─── syntax tokens ────────────────────────────────────────────────────────── */

/* Class names come from df-render's HIGHLIGHT_NAMES table: `function.builtin`
   emits both `hl-function` and `hl-function-builtin`, so a family can be themed
   once here and refined where it is worth distinguishing. */

.hl-comment { color: var(--hl-comment); font-style: italic; }
.hl-keyword { color: var(--hl-keyword); }
.hl-string { color: var(--hl-string); }
.hl-number,
.hl-boolean,
.hl-character { color: var(--hl-number); }
.hl-constant { color: var(--hl-constant); }
.hl-function { color: var(--hl-function); }
.hl-constructor { color: var(--hl-type); }
.hl-type { color: var(--hl-type); }
.hl-variable { color: var(--hl-variable); }
.hl-variable-parameter { color: var(--hl-variable); font-style: italic; }
.hl-property { color: var(--hl-attribute); }
.hl-operator { color: var(--hl-operator); }
.hl-punctuation { color: var(--hl-punctuation); }
.hl-tag { color: var(--hl-tag); }
.hl-attribute { color: var(--hl-attribute); }
.hl-label,
.hl-module { color: var(--hl-type); }
.hl-escape,
.hl-string-escape,
.hl-string-special { color: var(--hl-number); }
.hl-embedded { color: var(--hl-variable); }

/* Builtins read as slightly more emphatic than user-defined names of the same
   kind — the distinction jj's own CLI colours make, and worth keeping. */
.hl-keyword,
.hl-function-builtin,
.hl-type-builtin,
.hl-variable-builtin,
.hl-constant-builtin {
  font-weight: 500;
}

/* In a high-contrast context, colour alone must not be the only signal.
   Comments and strings keep their shape cues; everything else falls back to
   the body colour rather than a low-contrast tint (spec §11 accessibility). */
@media (prefers-contrast: more) {
  [class^="hl-"],
  [class*=" hl-"] {
    color: var(--text);
  }
  .hl-comment { color: var(--text-dim); font-style: italic; }
  .hl-string { color: var(--text); font-style: italic; }
  .hl-keyword { font-weight: 700; }
}


/* ─── settings ─────────────────────────────────────────────────────────────── */

/* The tab strip, shared by repository sections, change-detail sections and
   settings. An inset box-shadow rather than a border-bottom, so activating a
   tab does not displace its label by two pixels. */
.subtabs {
  display: flex;
  gap: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.subtabs::-webkit-scrollbar {
  display: none;
}

.subtabs a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  color: var(--text-dim);
  font-size: var(--text-sm);
  white-space: nowrap;
}

.subtabs a:hover {
  color: var(--text);
  text-decoration: none;
}

.subtabs a.active {
  color: var(--text);
  font-weight: 500;
  box-shadow: inset 0 -2px 0 var(--action);
}

/* The count beside a tab label. Always monospace and always faint: it is a
   quantity you glance at, never the thing you are clicking. */
.subtabs .tab-count {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  font-weight: 400;
}

/* Outside the repository sub-bar the strip needs its own rule and some air. */
.subtabs.ruled {
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}

.listing {
  width: 100%;
  border-collapse: collapse;
  margin: 12px 0;
}

.listing th {
  text-align: left;
  font-family: var(--font-condensed);
  font-weight: 500;
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 6px 10px 6px 0;
  border-bottom: 1px solid var(--border);
}

.listing td {
  padding: 6px 10px 6px 0;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  font-size: var(--text-sm);
}

.listing tr:last-child td {
  border-bottom: none;
}

.kv {
  display: grid;
  grid-template-columns: minmax(120px, max-content) 1fr;
  gap: 6px 16px;
  margin: 0;
}

.kv dt {
  color: var(--text-dim);
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  align-self: center;
}

.kv dd {
  margin: 0;
}

/* The danger zone reads as dangerous without relying on colour alone — the
   border weight and the heading carry it for anyone who cannot see the hue. */
.panel-danger {
  border-color: color-mix(in srgb, var(--danger) 55%, var(--border));
}

.panel-danger h2,
.panel-danger h3 {
  color: var(--danger);
}

.panel-danger hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 20px 0;
}

textarea,
select {
  width: 100%;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px 10px;
  font-family: inherit;
  font-size: 14px;
}

textarea {
  font-family: var(--font-mono);
  resize: vertical;
}

textarea:focus,
select:focus {
  outline: 2px solid var(--action);
  outline-offset: -1px;
}

/* A select inside a table row should not stretch to the full column. */
.listing select {
  width: auto;
  min-width: 120px;
}

.field label input[type="checkbox"] {
  width: auto;
  margin-right: 6px;
}



/* ─── the revset bar ───────────────────────────────────────────────────────── */

/* One control, not three: the label, the field, the verdict and the submit
   button share a single border so the whole thing reads as the query box it
   is. */
.revset-bar {
  display: flex;
  align-items: stretch;
  margin-bottom: 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.revset-tag {
  display: flex;
  align-items: center;
  padding: 0 8px;
  border-right: 1px solid var(--border);
  background: var(--surface-raised);
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--identity);
  flex: none;
}

/* The field is monospace because a revset is code. The design paints a
   syntax-highlighted layer over a transparent input, which needs a script to
   stay in sync with what is being typed; a stale highlight over live text is
   worse than none, so the field is plainly monospace instead and the colour
   goes on the saved expressions in the aside, where it is always correct. */
.revset-bar input[type="text"] {
  flex: 1;
  min-width: 0;
  height: 30px;
  border: none;
  border-radius: 0;
  background: transparent;
  font-family: var(--font-mono);
}

.revset-bar input[type="text"]:focus {
  outline: none;
  border: none;
}

.revset-status {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
  flex: none;
}

.revset-status.is-bad {
  color: var(--danger);
  white-space: normal;
  max-width: 40ch;
}

.revset-bar .btn {
  height: auto;
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  flex: none;
}

/* ─── filter tabs ──────────────────────────────────────────────────────────── */

.filter-tabs {
  margin-bottom: 0;
}

.filter-glyph {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

.list-summary {
  margin: 0;
  height: 22px;
  display: flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

/* ─── the change list ──────────────────────────────────────────────────────── */

/* Five columns. Four are fixed-width facts and one — the title — takes what is
   left, so a long title truncates instead of pushing the diffstat off the
   right edge. */
.changelist {
  --cl-cols: 148px minmax(240px, 1fr) 96px 120px 72px;
}

.changelist-head,
.changelist-row {
  display: grid;
  grid-template-columns: var(--cl-cols);
  align-items: center;
  border-bottom: 1px solid var(--border);
}

.changelist-head {
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.changelist-head > div {
  padding: 6px 8px;
}

.changelist-head > .at-end {
  text-align: right;
}

.changelist-row {
  height: 32px;
  font-size: var(--text-sm);
}

.changelist-row:hover {
  background: var(--surface);
}

/* A stacked row carries the identity tint even at rest, so the group is
   visible without reading the rail. */
.changelist-row.in-stack {
  background: var(--identity-wash);
}

.changelist-row.in-stack:hover {
  background: var(--surface);
}

.cl-change {
  display: flex;
  align-self: stretch;
  align-items: center;
  min-width: 0;
}

.cl-indent {
  flex: none;
  align-self: stretch;
}

/* The rail runs the full height of the row so consecutive stacked rows join
   into one continuous line. */
.cl-rail {
  width: 2px;
  flex: none;
  align-self: stretch;
  background: var(--identity);
}

.cl-glyph {
  width: 20px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
}

.cl-title {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  min-width: 0;
}

.cl-title > a {
  font-size: var(--text-base);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cl-title > a:hover {
  color: var(--action);
}

.cl-byline,
.cl-revs {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
  flex: none;
}

.cl-byline a {
  color: var(--text-faint);
}

.cl-review {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 8px;
}

/* A reviewer's mark. The ring says what their verdict is and the fill says
   whether it still applies to the current revision — a stale approval gets a
   conflict-coloured ring, because on a change that has been rewritten it is
   no longer an approval of anything you can see. */
.cl-avatar {
  width: 18px;
  height: 18px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--surface-raised);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  user-select: none;
}

.cl-comments {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

.cl-diff {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  padding: 0 8px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.cl-bars {
  display: inline-flex;
  gap: 1px;
  flex: none;
}

.cl-bars > span {
  width: 3px;
  height: 10px;
}

.cl-add { color: var(--diff-add-text); }
.cl-del { color: var(--diff-del-text); }

.cl-when {
  padding: 0 8px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

/* ─── stack banner ─────────────────────────────────────────────────────────── */

.stack-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 26px;
  padding: 0 8px;
  background: var(--identity-wash);
  border-bottom: 1px solid var(--border);
}

.stack-banner-rail {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--identity);
}

.stack-banner-label {
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--identity);
}

.stack-banner-note {
  font-size: var(--text-sm);
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ─── saved revsets ────────────────────────────────────────────────────────── */

.saved-revset {
  display: block;
  height: 22px;
  padding: 0 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 20px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.saved-revset:hover {
  border-color: var(--action);
  color: var(--action);
  text-decoration: none;
}

.saved-revset.is-current {
  border-color: var(--action);
  color: var(--action);
}

/* ─── responsive ───────────────────────────────────────────────────────────── */

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

  /* Review and diff go before the title does. */
  .changelist {
    --cl-cols: 148px minmax(0, 1fr) 72px;
  }

  .cl-review,
  .cl-diff,
  .changelist-head > div:nth-child(3),
  .changelist-head > div:nth-child(4) {
    display: none;
  }
}

@media (max-width: 800px) {
  .cl-byline,
  .cl-revs {
    display: none;
  }
}

/* ─── diffs and review ─────────────────────────────────────────────────────── */

/* The diff is read in three passes, and the layout is built for that order:
   the bar says what is being compared and how big it is, the index says which
   files, and only then the code. Each of the first two stays out of the way of
   the third — the bar is one line, the index folds. */

/* ─── the sticky diff bar ─── */

/* `top` clears the 46px masthead. z-index sits under the masthead (30) and over
   the file headers (10), which is the order they overlap in. */
.diffbar {
  position: sticky;
  top: 46px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 7px 12px;
  margin-bottom: 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-raised);
  font-size: var(--text-sm);
  color: var(--text-dim);
}

/* Opening the compare form makes the bar two rows tall, which would leave the
   file headers sticking to a height that no longer exists. While it is open the
   bar is an ordinary block and scrolls away. */
.diffbar:has(.cmpbox[open]) {
  position: static;
}

.diffbar-stat {
  display: flex;
  align-items: center;
  gap: 8px;
}

.diffbar-stat strong {
  color: var(--text);
}

.diffbar-link {
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* The compare control: closed it is a label reading "Comparing v2 → v3", which
   is information the reviewer needs on every screen. Open it is the form. */
.cmpbox > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 2px 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--text);
  white-space: nowrap;
}

.cmpbox > summary::-webkit-details-marker { display: none; }
.cmpbox > summary:hover { border-color: var(--action); color: var(--action); }

.cmpform {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 0 2px;
}

/* ─── the changed-file tree, beside the diff ─── */

/* Two columns: the tree, and the diff. The tree is fixed-width because it is a
   reference the eye returns to at a known place, and `minmax(0, 1fr)` is what
   stops a long unbroken code line from widening the grid track and pushing the
   tree off the screen. */
.difflayout {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
}

/* A diff with no files renders no tree, and a one-column grid should not leave
   a 250px hole where it would have been. */
.difflayout:not(:has(.difftree)) {
  grid-template-columns: minmax(0, 1fr);
}

.diffmain {
  min-width: 0;
}

/* `top` clears the masthead (46px) and the sticky diff bar under it, so the
   tree comes to rest just below the bar rather than behind it. Scrolls
   independently once it outgrows the viewport — a 200-file commit must not
   make the sidebar taller than the page. */
.difftree {
  position: sticky;
  top: 92px;
  max-height: calc(100vh - 104px);
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: var(--text-sm);
}

.dt-head {
  position: sticky;
  top: 0;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-raised);
  color: var(--text-dim);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.dt-body {
  padding: 6px 8px 8px;
}

/* Each level indents by one step and draws a rule down its own children, which
   is what makes the nesting readable at a glance rather than countable. */
.dt-kids {
  margin-left: 5px;
  padding-left: 7px;
  border-left: 1px solid var(--border);
}

.dt-row,
.dt-file {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 4px;
  border-radius: 3px;
  font-family: var(--font-mono);
  line-height: 1.5;
  text-decoration: none;
  color: var(--text);
}

.dt-row {
  cursor: pointer;
  list-style: none;
  color: var(--text-dim);
}

.dt-row::-webkit-details-marker { display: none; }
.dt-row:hover,
.dt-file:hover { background: var(--surface-raised); }
.dt-file:hover .dt-name { color: var(--action); }

.dt-caret {
  width: 0;
  height: 0;
  flex: none;
  border-left: 5px solid var(--text-faint);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 90ms ease;
}

details[open] > .dt-row .dt-caret {
  transform: rotate(90deg);
}

/* The directory name is context and the file name is the thing, so only the
   directory dims — the same rule the file index and the file headers follow. */
.dt-dirname,
.dt-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dt-dirname {
  font-size: var(--text-xs);
}

/* The counts are a magnitude, not a figure to read: small, and never allowed to
   squeeze the name they sit beside. */
.dt-file .cl-add,
.dt-file .cl-del {
  flex: none;
  font-size: var(--text-xs);
}

/* Under about 900px there is no room for a column beside the diff, so the tree
   becomes a capped block above it rather than disappearing. */
@media (max-width: 900px) {
  .difflayout {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Stacked, the tree is no longer a column and stretching it the full width
     of the page strands the counts a screen away from their filenames. */
  .difftree {
    position: static;
    max-width: 560px;
    max-height: 260px;
    margin-bottom: 10px;
  }
}

/* On a change's files tab the diff already has the reviewers aside to its
   right, so the tree makes three columns and needs the room for it. It gives
   up its column earlier than on the commit page — the media query measures the
   viewport, but what is actually short here is the main column inside it. */
@media (max-width: 1280px) {
  .columns-main .difflayout {
    grid-template-columns: minmax(0, 1fr);
  }

  .columns-main .difftree {
    position: static;
    max-width: 560px;
    max-height: 260px;
    margin-bottom: 10px;
  }
}

/* ─── the file index ─── */

.fileindex {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  margin-bottom: 10px;
}

.fileindex > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  font-size: var(--text-sm);
  color: var(--text-dim);
}

.fileindex > summary::-webkit-details-marker { display: none; }

.fx-caret,
.fd-caret {
  width: 0;
  height: 0;
  border-left: 5px solid var(--text-faint);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 90ms ease;
  flex: none;
}

details[open] > summary .fx-caret,
details[open] > summary .fd-caret {
  transform: rotate(90deg);
}

/* Capped and scrolled: a forty-file index should not push the diff off the
   first screen, which is the thing it exists to get you to. */
.fx-list {
  max-height: 300px;
  overflow-y: auto;
  border-top: 1px solid var(--border);
}

.fx-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 12px;
  font-size: var(--text-sm);
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
}

.fx-row:last-child { border-bottom: 0; }
.fx-row:hover { background: var(--surface-raised); }

/* The directory is context, the basename is the thing. Dimming the leading path
   is what makes a column of deep paths scannable, and truncating only the
   directory means the basename — the part being looked for — never disappears
   into an ellipsis. */
.fx-path,
.fd-path {
  display: flex;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.fx-dir,
.fd-dir {
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
}

.fx-name,
.fd-name {
  color: var(--text);
  flex: none;
}

.fx-bin,
.fd-note {
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

/* ─── the status letter ─── */

.fkind {
  flex: none;
  width: 16px;
  height: 16px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  color: var(--on-action);
}

.fkind.is-add { background: var(--diff-add-text); }
.fkind.is-mod { background: var(--identity); }
.fkind.is-del { background: var(--diff-del-text); }
.fkind.is-ren { background: var(--merged); color: #fff; }

/* ─── the add/delete ratio bar ─── */

.statbar {
  flex: none;
  display: flex;
  width: 44px;
  height: 7px;
  border-radius: 1px;
  overflow: hidden;
  background: var(--border);
}

.statbar-add { background: var(--diff-add-text); }
.statbar-del { background: var(--diff-del-text); }

/* ─── one file ─── */

/* The base box is shared with the interdiff and the conflict view, which build
   it out of plain divs and scroll horizontally. Only the foldable version in
   the files tab gets the sticky, clickable header below. */
.filediff {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 10px;
  background: var(--surface);
  overflow-x: auto;
}

.filediff-head {
  padding: 8px 10px;
  background: var(--surface-raised);
  border-bottom: 1px solid var(--border);
}

/* ─── the foldable file, files tab only ─── */

details.filediff {
  /* Wrapping the code removes the reason to scroll, and a scroll container
     would stop the header sticking to the viewport. */
  overflow-x: visible;
  /* Jumping from the index must not land the header under the sticky bar. */
  scroll-margin-top: 92px;
}

/* Sticky, so the path stays readable a thousand lines into the file. It is the
   summary itself rather than a child, because `details` is the containing
   block here and a sticky grandchild would stop at its padding box. */
summary.filediff-head {
  position: sticky;
  top: 84px;
  z-index: 10;
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-radius: var(--radius) var(--radius) 0 0;
}

summary.filediff-head::-webkit-details-marker { display: none; }
summary.filediff-head:hover .fd-name { color: var(--action); }
details.filediff:not([open]) > .filediff-head { border-bottom-color: transparent; }

/* A folded large file still has to look like something you can open, not like
   something that failed to render. */
details.filediff.is-big:not([open]) > .filediff-head {
  border-left: 2px solid var(--identity);
}

.fd-old {
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

/* "View file" — only the commit page sets it, where the revision is one the
   browse routes can serve. Quiet until the header is hovered: it is the answer
   to a question most hunks never raise. */
.fd-view {
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

summary.filediff-head:hover .fd-view { color: var(--action); }

/* ─── the lines ─── */

.difftable {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
  line-height: 19px;
}

/* Two number columns and a sign column, all fixed and all unselectable, so a
   copy of the diff is a copy of the code. */
.difftable .lineno {
  position: relative;
  width: 1%;
  min-width: 44px;
  text-align: right;
  padding: 0 8px;
  user-select: none;
  white-space: nowrap;
  color: var(--text-faint);
  font-size: var(--text-xs);
  background: color-mix(in srgb, var(--bg) 60%, transparent);
}

.difftable .lineno + .lineno {
  border-right: 1px solid var(--border);
}

.difftable .dl-mark {
  width: 1%;
  padding: 0 0 0 6px;
  user-select: none;
  text-align: center;
  font-weight: 600;
}

.difftable .codeline {
  padding: 0 10px 0 4px;
  /* Wrap rather than scroll: a horizontal scrollbar per file makes a long diff
     unreadable, and a scroll container would also break the sticky header.
     Continuation lines are hung two characters in so a wrap is visibly a wrap
     and not a new line of code. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: normal;
  text-indent: -2ch;
  padding-left: calc(4px + 2ch);
}

.difftable .hunkhead td {
  padding: 3px 10px;
  color: var(--text-faint);
  background: var(--bg);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  font-size: var(--text-xs);
}

.difftable tbody tr:first-child .hunkhead td,
.difftable .hunkhead:first-child td {
  border-top: 0;
}

/* In the commentable table the wash carries the add/delete signal on its own
   and the text stays body colour. Colouring whole lines green and red — as this
   used to — costs contrast on every character and drowns the word-level
   emphasis that says what actually changed on the line. The 2px rule is what
   survives a monochrome rendering.

   The `.diffline` variant (interdiff, design specimens) keeps the coloured text
   it had: it is read in short bursts, and it has no gutter to carry the colour
   instead. */
.diffline.line-add { background: var(--diff-add-bg); color: var(--diff-add-text); }
.diffline.line-del { background: var(--diff-del-bg); color: var(--diff-del-text); }

.diff-sign {
  display: inline-block;
  width: 1ch;
}

.line-add > .lineno,
.line-add > .dl-mark,
.line-add > .codeline { background: var(--diff-add-bg); }
.line-del > .lineno,
.line-del > .dl-mark,
.line-del > .codeline { background: var(--diff-del-bg); }

.line-add > .dl-mark { color: var(--diff-add-text); }
.line-del > .dl-mark { color: var(--diff-del-text); }

.line-add > .lineno:first-child { box-shadow: inset 2px 0 0 var(--diff-add-text); }
.line-del > .lineno:first-child { box-shadow: inset 2px 0 0 var(--diff-del-text); }

.dl:hover > .lineno { color: var(--text-dim); }

/* The comment affordance. Absolutely positioned so it costs no layout, hidden
   until the row is hovered or it is focused — which is what lets the diff be a
   wall of code again instead of a wall of "Comment on line N". */
.dl-add {
  position: absolute;
  left: 2px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  background: var(--action);
  color: var(--on-action);
  font-size: 12px;
  line-height: 1;
  text-decoration: none;
  opacity: 0;
  pointer-events: none;
}

.dl:hover .dl-add,
.dl-add:focus-visible {
  opacity: 1;
  pointer-events: auto;
}

@media (hover: none) {
  /* No hover to reveal it with, so it is always there, just quiet. */
  .dl-add { opacity: 0.45; pointer-events: auto; }
}

/* A diff line outside the commentable table: the interdiff and the design
   sheet's specimen rows. A grid rather than a table, because there is no
   comment column to align against — just a gutter and the code.

   The 2px left rule is what carries the add/delete signal in a monochrome or
   high-contrast rendering, where the background washes disappear. */
.diffline {
  display: grid;
  grid-template-columns: 44px auto;
  min-width: max-content;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 21px;
  border-left: 2px solid transparent;
}

.diffline.line-add { border-left-color: var(--diff-add-text); }
.diffline.line-del { border-left-color: var(--diff-del-text); }

.diffline.diff-hunk {
  background: var(--surface-raised);
  color: var(--text-dim);
}

.diffline.line-ctx {
  color: var(--text);
}

.diff-ln {
  padding: 0 6px;
  text-align: right;
  font-size: var(--text-xs);
  color: var(--text-faint);
  user-select: none;
}

.diff-text {
  padding: 0 6px;
  white-space: pre;
}

/* Word-level intra-line changes (spec §8). Rendered as a background wash
   rather than a colour change, so the line's add/delete colour still reads and
   the emphasis survives a high-contrast or monochrome display. */
.word-changed {
  background: color-mix(in srgb, currentColor 22%, transparent);
  border-radius: var(--radius-sm);
}

.inline-thread {
  padding: 10px 14px;
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  white-space: normal;
}

/* The comment form exists in the markup for every commentable line and is shown
   for exactly one: the one named in the fragment. `:target` does the work, so
   there is no per-line chrome in the resting diff, no script, and an open form
   has a URL that can be linked at somebody. */
.inline-form {
  display: none;
}

.inline-form:target {
  display: table-row;
  scroll-margin-top: 120px;
}

.inline-form > td {
  padding: 10px 14px;
  background: var(--surface);
  border-top: 2px solid var(--action);
  border-bottom: 1px solid var(--border);
  white-space: normal;
}

.inline-form textarea {
  width: 100%;
}

/* A comment is a boxed object; an event is a line. That difference is the
   timeline's whole structure — somebody wrote the boxes, the system recorded
   the lines. */
.comment {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 12px;
  background: var(--surface);
}

.comment-body {
  margin-top: 6px;
}

.comment-body > :first-child { margin-top: 0; }
.comment-body > :last-child { margin-bottom: 0; }

/* The line a comment was written about, retained when the anchor goes stale
   (spec §5 step 4). */
.anchor-context {
  margin: 6px 0;
  padding: 6px 10px;
  background: var(--bg);
  border-left: 2px solid var(--identity);
  overflow-x: auto;
  font-size: 12px;
}

/* An event: one line, hung off a left rule. No box and no rule between
   events — a run of them reads as a log, which is what it is. */
.timeline-event {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 3px 8px;
  border-left: 2px solid var(--border);
  font-size: var(--text-sm);
}

.timeline-glyph {
  width: 12px;
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}


/* ─── change detail header ─────────────────────────────────────────────────── */

.change-head {
  margin-bottom: 4px;
}

.change-head-top {
  display: flex;
  align-items: stretch;
  gap: 12px;
  min-width: 0;
  margin-bottom: 10px;
}

/* Three pixels of identity down the left of the whole header. The page is
   about one change; this is the change. */
.change-head-rail {
  width: 3px;
  flex: none;
  background: var(--identity);
}

.change-head-id {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.change-head-line {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* The id, larger than the title. Deliberate: the title is prose somebody can
   retype, the id is what every review and permalink is attached to. */
.change-id-display {
  font-family: var(--font-mono);
  font-size: var(--text-2xl);
  line-height: 34px;
  font-variant-numeric: tabular-nums;
}

.change-id-display > .cid-p {
  font-weight: 600;
}

.change-id-display.is-synthetic {
  font-size: var(--text-lg);
  color: var(--text-dim);
}

.change-title {
  margin: 0;
  font-size: var(--text-lg);
  line-height: 28px;
  font-weight: 600;
  text-wrap: pretty;
}

.change-byline {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  color: var(--text-dim);
}

.change-byline .sep {
  color: var(--text-faint);
}

/* The aside follows the reader down a long diff. `top` clears the 46px
   masthead plus a hairline of air. */
.columns-aside.is-sticky {
  position: sticky;
  top: 62px;
}

.reviewer-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-sm);
}

.reviewer-glyph {
  width: 10px;
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.reviewer-meta {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.mini-row.is-current {
  background: var(--identity-wash);
}

/* ─── revisions timeline ───────────────────────────────────────────────────── */

.revtimeline {
  display: flex;
  flex-direction: column;
  margin: 12px 0;
}

.revrow {
  display: flex;
  gap: 12px;
  border-bottom: 1px solid var(--border);
}

.revrow.is-selected {
  background: var(--identity-wash);
}

/* The rail is two flexing segments with the dot between them, so the line
   joins consecutive revisions and stops cleanly at both ends. */
.revrail {
  width: 24px;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: stretch;
}

.revrail-seg {
  width: 2px;
  flex: 1;
}

/* Square, not round: a revision is a discrete recorded state, and the shape
   is doing that work — round would read as a generic bullet. */
.revdot {
  width: 9px;
  height: 9px;
  flex: none;
  border: 1px solid var(--identity);
  border-radius: 1px;
}

.revbody {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 8px 8px 0;
}

.revline {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.revlabel {
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.revnote {
  font-size: var(--text-base);
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.revwhen {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
}

.revmeta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

.revmeta a {
  color: var(--text-faint);
}

.revmeta a:hover {
  color: var(--action);
}

/* The A/B pickers. Links, not radios: each pair is a URL, so a reviewer can
   paste "rev 2 → rev 4" into a comment and it resolves for everyone. */
.abpick {
  width: 16px;
  height: 16px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1;
  color: var(--text-faint);
}

.abpick:hover {
  border-color: var(--action);
  color: var(--action);
  text-decoration: none;
}

.abpick.is-on {
  border-color: var(--action);
  background: var(--action);
  color: var(--on-action);
}

.interdiff-file + .interdiff-file {
  border-top: 1px solid var(--border);
}

.interdiff-path {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-raised);
  font-size: var(--text-sm);
}

/* ─── stack page ───────────────────────────────────────────────────────────── */

.stack-chain {
  font-size: var(--text-sm);
  color: var(--text-faint);
}

.stackrow {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 38px;
  padding-right: 10px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-size: var(--text-sm);
}

.stackrow:last-child {
  border-bottom: none;
}

.stackrow:hover {
  background: var(--surface-raised);
  text-decoration: none;
}

.stackrow.is-current {
  background: var(--identity-wash);
}

.stackrow-glyph {
  width: 22px;
  flex: none;
  text-align: center;
  font-family: var(--font-mono);
}

.stackrow-title {
  font-size: var(--text-base);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.stackrow-meta {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  white-space: nowrap;
  flex: none;
}

/* The bottom of the graph: what the chain sits on. Not a link, because a
   bookmark has no page — it is a pointer, not a thing. */
.stackrow-base {
  padding-left: 8px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
}

.stack-cmd {
  margin-top: 12px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  font-size: var(--text-sm);
  color: var(--text-dim);
  white-space: pre-wrap;
}

/* ─── conflicts ────────────────────────────────────────────────────────────── */

.conflict-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1px;
  background: var(--border);
}

.conflict-side {
  background: var(--surface);
  padding: 10px 12px;
  min-width: 0;
}

/* A base is the common ancestor, not another candidate resolution. Dimming it
   says so without needing a legend. */
.conflict-base {
  background: var(--bg);
}

.conflict-side pre {
  margin: 6px 0 0;
  overflow-x: auto;
  font-size: 12px;
  max-height: 420px;
}

details summary {
  cursor: pointer;
}


/* ─── issues ───────────────────────────────────────────────────────────────── */

/* A label's colour comes from the repository, so it is validated as a hex
   triple before it reaches the inline style. When it is not one, the chip falls
   back to these theme colours rather than being dropped. */
.label-chip {
  border-width: 1px;
  border-style: solid;
}

.issue-row {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 10px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-size: var(--text-sm);
}

.issue-row:last-child {
  border-bottom: none;
}

.issue-row:hover {
  background: var(--surface-raised);
  text-decoration: none;
}

/* A closed issue is history. It stays fully legible — dimming it to 50% would
   make the archive unreadable — but it steps back a little. */
.issue-row.is-closed {
  opacity: 0.85;
}

.issue-glyph {
  width: 14px;
  flex: none;
  font-family: var(--font-mono);
}

.issue-num {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  flex: none;
}

.issue-title {
  font-size: 13.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.issue-row:hover .issue-title {
  color: var(--action);
}

.issue-meta {
  font-size: var(--text-sm);
  color: var(--text-faint);
  white-space: nowrap;
  flex: none;
}

.issue-comments {
  width: 34px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  flex: none;
}

.issue-when {
  width: 56px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-faint);
  flex: none;
}

/* ─── issue detail ─────────────────────────────────────────────────────────── */

.backlink {
  display: inline-block;
  margin-bottom: 10px;
  font-size: var(--text-sm);
}

.issue-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}

.issue-body {
  margin: 12px 0;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  max-width: 76ch;
}

.issue-ref {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0;
  padding: 2px 8px;
  border-left: 2px solid var(--border);
  font-size: var(--text-sm);
  color: var(--text-dim);
}

.issue-ref-rail {
  font-family: var(--font-mono);
  color: var(--identity);
}

.aside-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* ─── filter bar ───────────────────────────────────────────────────────────── */

/* Tabs and a filter form on one line. The tabs are links (each is a URL) and
   the form is a form — they only look like one control. */
.filterbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.filterbar-tabs {
  display: flex;
  gap: 2px;
}

.filterbar select,
.filterbar input[type="text"] {
  height: 24px;
  width: auto;
  min-width: 120px;
  padding: 0 6px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font-size: var(--text-sm);
}

.btn-mono.is-on {
  border-color: var(--action);
  color: var(--action);
}

/* The search bar reuses the revset bar's shell, so a select inside it has to
   lose its own border and sit flush with the field beside it. */
.revset-bar select {
  height: 30px;
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  background: var(--surface-raised);
  color: var(--text-dim);
  font-size: var(--text-sm);
  padding: 0 6px;
  flex: none;
}

/* ─── search results ───────────────────────────────────────────────────────── */

.search-group + .search-group {
  margin-top: 20px;
}

.search-group > h2 {
  margin: 0 0 6px;
}

.search-hit {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
  padding: 0 10px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-size: var(--text-sm);
}

.search-hit:last-child {
  border-bottom: none;
}

.search-hit:hover {
  background: var(--surface-raised);
  text-decoration: none;
}

.search-kind {
  width: 60px;
  flex: none;
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.search-title {
  font-size: var(--text-base);
  white-space: nowrap;
  flex: none;
}

.search-hit:hover .search-title {
  color: var(--action);
}

/* The context gives up its space first — the title is what you are aiming at. */
.search-context {
  flex: 1;
  min-width: 0;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ─── masthead groups ──────────────────────────────────────────────────────── */

.masthead-nav,
.masthead-account {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.masthead-nav a {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 9px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: var(--text-sm);
}

.masthead-nav a:hover {
  border-color: var(--border-strong);
  background: var(--surface-raised);
}

.masthead-account a {
  font-size: var(--text-sm);
}

/* The trigger sits in the right-hand grid column alongside the theme toggle;
   a long handle should truncate rather than push that column wide enough to
   fight the centred jump control for space. The caret truncates separately so
   it always stays visible. */
.account-handle {
  display: inline-block;
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

.theme-toggle:hover {
  border-color: var(--action);
  color: var(--action);
}

/* The account menu opens under the handle, right-aligned rather than the
   bookmark switcher's left: the trigger sits at the end of the bar, and a
   left-aligned panel would hang off the edge of the viewport. */
.switcher-menu-end {
  left: auto;
  right: 0;
}

.switcher-menu form {
  margin: 0;
}

.switcher-item-danger {
  width: 100%;
  border: none;
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
  color: var(--text);
}

.switcher-item-danger:hover {
  color: var(--danger);
}

/* Narrow screens shed the chrome from the middle outward: the section links
   go first, then the wordmark's "dogfood" text, leaving the jump control,
   the handle and the theme readout as the last things standing. */
@media (max-width: 900px) {
  .masthead-nav,
  .masthead-group-start > .vrule {
    display: none;
  }
}

@media (max-width: 620px) {
  .jump > span:first-child {
    display: none;
  }

  .jump {
    padding: 0 6px;
  }

  .brand > span:last-child {
    display: none;
  }
}

@media (max-width: 400px) {
  .theme-toggle {
    display: none;
  }
}


/* ─── accessibility (spec §11) ─────────────────────────────────────────────── */

/* The skip link is the first thing in the tab order and invisible until it has
   focus. Positioned rather than hidden — `display:none` would take it out of
   the tab order entirely, which defeats the point. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 10px 16px;
  background: var(--surface-raised);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 0 0 var(--radius) 0;
}

.skip-link:focus {
  left: 0;
}

/* A visible focus indicator on everything interactive. `:focus-visible` so a
   mouse click does not leave a ring behind, but a keyboard user always sees
   where they are. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--action);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* `main` is focusable only as the skip link's target; it should never draw a
   ring of its own. */
main:focus {
  outline: none;
}

/* Windows high-contrast mode replaces our colours wholesale; make sure the
   focus ring survives it. */
@media (forced-colors: active) {
  a:focus-visible,
  button:focus-visible,
  input:focus-visible,
  select:focus-visible,
  textarea:focus-visible {
    outline: 2px solid CanvasText;
  }
}

/* Badges and chips must not rely on colour alone. They already carry their
   meaning as text; this keeps the border in high-contrast mode so the shape
   still reads as a distinct token. */
@media (forced-colors: active) {
  .badge,
  .chip {
    border: 1px solid CanvasText;
  }
}

/* Visually hidden but announced. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}


/* ─── the in-browser editor ────────────────────────────────────────────────── */

/* The editor is one bordered object: a title bar, the code, and a commit bar
   along the bottom. The commit message and the submit button live inside that
   frame rather than as separate fields below it, because they are part of the
   same act. */
.editor-shell {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.editor-bar,
.editor-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  background: var(--surface-raised);
  font-size: var(--text-sm);
}

.editor-bar {
  border-bottom: 1px solid var(--border);
}

.editor-foot {
  border-top: 1px solid var(--border);
}

.editor-foot input[type="text"] {
  flex: 1;
  min-width: 0;
  height: 28px;
}

.editor-status {
  font-size: var(--text-xs);
  color: var(--text-faint);
}

/* The textarea is the editor until CodeMirror replaces it, so it has to look
   like one on its own — not like a form field that happens to hold code. */
.editor-host textarea {
  display: block;
  width: 100%;
  min-height: 420px;
  border: none;
  border-radius: 0;
  padding: 8px 10px;
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 21px;
  tab-size: 4;
  white-space: pre;
  overflow: auto;
}

.editor-host textarea:focus {
  outline: none;
  border: none;
}

/* CodeMirror sizes itself; the host must not fight it. */
.editor-host .cm-editor {
  height: auto;
}

/* ─── code view three-pane layout ──────────────────────────────────────────── */

/* The last commit that modified this file, shown above the pane layout. */
.file-commit-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  margin-bottom: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: var(--text-sm);
}

/* Three-column grid: file tree | code | symbol outline */
.code-view-layout {
  display: grid;
  grid-template-columns: 220px 1fr 200px;
  gap: 0;
  align-items: start;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

/* When the symbol sidebar is absent, the layout collapses to two columns. */
.code-view-layout:not(:has(.code-sidebar-right)) {
  grid-template-columns: 220px 1fr;
}

/* ─── left sidebar (file tree) ──────────────────────────────────────────── */
.code-sidebar-left {
  background: var(--surface);
  border-right: 1px solid var(--border);
  max-height: 80vh;
  overflow-y: auto;
  padding: 0;
}

.sidebar-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: var(--text-sm);
  line-height: 1;
}

.file-tree {
  display: flex;
  flex-direction: column;
}

/* `line-height: 1`: without it the body default of 1.5 gives the label extra
   leading that JetBrains Mono splits unevenly above/below the glyphs, so the
   text hangs below the icon instead of sitting beside it (same fix as
   `.filelist-entry-name`). */
.file-tree-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  font-size: var(--text-sm);
  font-family: var(--font-mono);
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1;
}

.file-tree-item:last-child {
  border-bottom: none;
}

.file-tree-item:hover {
  background: var(--surface-raised);
}

.file-tree-item.is-current {
  background: var(--surface-raised);
  font-weight: 500;
  border-left: 2px solid var(--action);
  padding-left: 10px;
}

.file-tree-item.is-dir {
  color: var(--text-dim);
}

.sidebar-header svg,
.file-tree-item svg {
  flex: none;
}

.file-tree-parent {
  color: var(--text-dim);
}

/* ─── center code panel ─────────────────────────────────────────────────── */
.code-panel {
  min-width: 0;
  overflow-x: auto;
}

.code-panel .panel {
  border: none;
  border-radius: 0;
}

/* ─── right sidebar (symbol outline) ────────────────────────────────────── */
.code-sidebar-right {
  background: var(--surface);
  border-left: 1px solid var(--border);
  max-height: 80vh;
  overflow-y: auto;
  padding: 0;
}

.symbol-list {
  display: flex;
  flex-direction: column;
}

.symbol-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  font-size: var(--text-sm);
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid var(--border);
}

.symbol-item:last-child {
  border-bottom: none;
}

.symbol-item:hover {
  background: var(--surface-raised);
}

.symbol-kind {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: 3px;
  flex-shrink: 0;
  background: var(--border);
  color: var(--text-dim);
}

/* Symbol kinds borrow the syntax palette, so an outline entry is the same
   colour as the token it points at in the code beside it. `color-mix` on the
   token itself rather than a hard-coded rgb, so both themes stay correct. */
.sk-fn { color: var(--hl-function); background: color-mix(in srgb, var(--hl-function) 12%, transparent); }
.sk-type { color: var(--hl-type); background: color-mix(in srgb, var(--hl-type) 12%, transparent); }
.sk-enum { color: var(--hl-constant); background: color-mix(in srgb, var(--hl-constant) 12%, transparent); }
.sk-trait { color: var(--hl-keyword); background: color-mix(in srgb, var(--hl-keyword) 12%, transparent); }
.sk-const { color: var(--hl-constant); background: color-mix(in srgb, var(--hl-constant) 12%, transparent); }
.sk-mod { color: var(--text-dim); background: var(--border); }

.symbol-name {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ─── blame gutter ──────────────────────────────────────────────────────── */
.blame-cell {
  padding: 0 8px;
  white-space: nowrap;
  font-size: 11px;
  color: var(--text-faint);
  border-right: 1px solid var(--border);
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  user-select: none;
}

.blame-author {
  font-family: var(--font-mono);
}

/* Small blame toggle button */
.btn-sm {
  padding: 3px 10px;
  font-size: var(--text-xs);
}

/* ─── responsive: collapse sidebars on narrow screens ───────────────────── */
@media (max-width: 900px) {
  .code-view-layout {
    grid-template-columns: 1fr;
  }

  .code-view-layout:not(:has(.code-sidebar-right)) {
    grid-template-columns: 1fr;
  }

  .code-sidebar-left {
    display: none;
  }

  .code-sidebar-right {
    display: none;
  }
}

@media (min-width: 901px) and (max-width: 1100px) {
  .code-view-layout {
    grid-template-columns: 180px 1fr;
  }

  .code-sidebar-right {
    display: none;
  }
}


/* ─── responsive hardening ────────────────────────────────────────────────── */

/* A terminal is rendered as a figure for its caption semantics. Browser
   figure margins cost 80px of horizontal room unless explicitly reset, which
   is especially destructive on a phone. */
figure.term {
  margin: 0;
}

/* Generic surfaces may contain grids, tables, or long rendered prose. Let the
   component own any necessary scrolling instead of making the document wide. */
.panel,
.markdown-body {
  min-width: 0;
}

/* The interdiff heading contains independent label, explanation, and summary
   spans. Give them real layout and let the explanation move to a second line
   rather than running directly into the comparison label. */
.interdiff > .filediff-head {
  display: flex;
  align-items: center;
  gap: 4px 8px;
  flex-wrap: wrap;
}

@media (max-width: 900px) {
  /* `.masthead nav` has greater specificity than a bare `.masthead-nav`.
     Qualifying this rule ensures the intended mobile chrome actually sheds
     the nonessential design-system link. */
  .masthead .masthead-nav {
    display: none;
  }
}

@media (max-width: 620px) {
  /* Sixteen pixels is a useful phone gutter; the desktop 24px gutter was
     taking almost a sixth of a 320px viewport before content even began. */
  .masthead-inner,
  .subnav-inner,
  .wrap {
    padding-left: 16px;
    padding-right: 16px;
  }

  /* On phones the centre control uses the room between the two edge groups.
     Equal outer grid tracks can overlap when an account handle is wider than
     the brand. */
  .masthead-inner {
    grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: 10px;
  }

  .jump {
    justify-self: center;
  }

  .account-handle {
    max-width: 88px;
  }

  /* Repository identity gets one compact row and the four primary sections
     get a full-width row. Previously the tab strip was flex-shrunk to as little
     as 16px and then asked to host its own 273px scroll area. */
  .subnav-inner {
    min-height: 60px;
    height: auto;
    align-content: start;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 10px;
    padding-top: 6px;
    padding-bottom: 0;
  }

  .subnav-inner > .vrule,
  .subnav-inner > .spacer,
  .subnav-inner > .subnav-meta {
    display: none;
  }

  .subnav-inner > .subtabs {
    order: 1;
    flex: 0 0 100%;
    width: 100%;
  }

  /* On a narrow bookmark table the title and age are secondary; retaining all
     four columns made the bookmark and change id paint over one another. */
  .bookmark-table th:nth-child(3),
  .bookmark-table th:nth-child(4),
  .bookmark-title,
  .bookmark-when {
    display: none;
  }

  .bookmark-table td {
    height: 44px;
  }

  .bookmark-name {
    width: 45%;
    white-space: normal;
  }

  .bookmark-name > a {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .bookmark-name .bookmark-flag {
    display: inline-block;
    margin-right: 4px;
  }

  .bookmark-points {
    width: 55%;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .bookmark-points .cid {
    display: block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* The context is supporting information. On phones the result title gets
     the flexible space and truncates cleanly if even that is not enough. */
  .search-context,
  .palette-hint {
    display: none;
  }

  .search-title,
  .palette-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .issue-meta {
    display: none;
  }

  /* Settings and rendered Markdown can contain genuinely tabular information.
     Preserve every column and contain the exceptional width inside the table
     instead of introducing a page-level scroll bay. */
  .listing,
  .markdown-body table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
  }

  .kv {
    grid-template-columns: minmax(0, 1fr);
    gap: 3px;
  }

  .row {
    flex-wrap: wrap;
  }
}

@media (max-width: 400px) {
  /* Five change-detail tabs fit a 320px viewport at this density, avoiding an
     almost-complete last label and a hard-to-discover tiny horizontal scroll. */
  .subtabs a {
    padding-left: 8px;
    padding-right: 8px;
  }
}

/* ─── checks ─────────────────────────────────────────────────────────────── */

.checks-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.checks-table th,
.checks-table td {
  padding: 8px 10px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.checks-table th {
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 500;
}

/* The verdict word carries the colour. Deliberately text and not an icon: a
   coloured dot alone fails for anyone who cannot distinguish the hues, and a
   check result is exactly the thing that must not be ambiguous. */
.check-pass { color: var(--open); font-weight: 500; }
.check-fail { color: var(--danger); font-weight: 500; }
.check-running { color: var(--action); font-weight: 500; }
.check-pending { color: var(--text-dim); font-weight: 500; }
/* Neutral, cancelled and skipped share a colour because they share a meaning
   for the reader: it did not fail, and it also did not pass. */
.check-neutral { color: var(--text-dim); font-weight: 500; }

/* ─── callouts ───────────────────────────────────────────────────────────── */

.callout {
  padding: 12px 14px;
  margin-bottom: 20px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
}

.callout h3 {
  margin: 0 0 6px;
  font-size: var(--text-sm);
}

.callout ul {
  margin: 0 0 6px;
  padding-left: 20px;
}

.callout.warn {
  border-color: color-mix(in srgb, var(--danger) 50%, transparent);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
}

p.warn { color: var(--danger); }

/* ─── notification inbox ─────────────────────────────────────────────────── */

.inbox-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

/* Unread is marked with weight and a rule, not with a background wash: a list
   where most rows are unread should not be a solid block of colour. */
.inbox-row.unread {
  border-left: 2px solid var(--action);
  padding-left: 10px;
}

.inbox-row.unread .inbox-title { font-weight: 600; }

.inbox-reason {
  font-family: var(--font-condensed);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}

.inbox-meta {
  margin-left: auto;
  color: var(--text-dim);
  font-size: var(--text-xs);
  white-space: nowrap;
}

/* The unread count in the masthead. Hidden entirely at zero rather than shown
   as "0" — a permanent zero trains people to ignore the control. */
.inbox-count {
  display: inline-block;
  min-width: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--action);
  color: var(--on-action);
  font-size: var(--text-xs);
  font-weight: 600;
  text-align: center;
}
