/* ==========================================================================
   Common Ground app shell
   Masthead, rail, and the page surfaces. No shadows, no border radius:
   structure is carried by rules (THEME.md section 1).
   ========================================================================== */

/* ============ MASTHEAD ============ */
header.masthead {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 0 24px;
  height: var(--masthead-h);
  background: var(--paper);
  border-bottom: 3px solid var(--rule);
  position: relative;
}

/* The second rule, 6px below the first. This double rule is the direction's
   signature, the shell is offset to clear it. */
header.masthead::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -6px;
  border-bottom: 1px solid var(--rule);
}

.masthead .brand {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 12px;
  white-space: nowrap;
}
.masthead .brand h1 {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}
.masthead .brand .edition {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
  border-left: 1px solid var(--rule);
  padding-left: 12px;
}

.search-hint {
  /* Prefers 460px and gives way to the brand and profile before it truncates. */
  flex: 0 1 460px;
  min-width: 200px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border: 1px solid var(--rule);
  background: var(--surface);
  padding: 7px 12px;
  color: var(--muted);
  font: inherit;
  text-align: left;
  cursor: text;
  white-space: nowrap;
  overflow: hidden;
}
.search-hint > span:first-child { overflow: hidden; text-overflow: ellipsis; }
.search-hint .kbd { flex: none; }
.search-hint:hover { color: var(--ink-2); border-color: var(--ink); }
.search-hint .kbd {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--ink-2);
  border: 1px solid var(--hairline);
  background: var(--paper);
  padding: 1px 6px;
}

.masthead .spacer { flex: 1; }

/* Health indicator. Ink when the API and database answer, accent when they do not ,
   the accent's only job is status (THEME.md §1). */
.status {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 16px;
  flex: none;
  white-space: nowrap;
}
.status .dot {
  width: 8px;
  height: 8px;
  flex: none;
  background: var(--muted);
}
.status[data-state="ok"] .dot { background: var(--ink); }
.status[data-state="error"] .dot { background: var(--accent); }
.status .label { color: var(--muted); }
.status[data-state="error"] .label { color: var(--accent-text); }

.profile {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: none;
  white-space: nowrap;
  padding-left: 20px;
  border-left: 1px solid var(--rule);
  height: 100%;
}
.avatar {
  width: 34px; height: 34px;
  background: var(--ink);
  color: var(--paper);
  display: grid;
  place-items: center;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.02em;
}
.profile .who .name { font-weight: 700; font-size: 13px; line-height: 1.2; }
.profile .who .role { font-size: 11px; color: var(--muted); line-height: 1.3; }

/* ============ SHELL ============ */
.shell {
  display: grid;
  grid-template-columns: var(--rail-w) 1fr;
  min-height: calc(100vh - var(--masthead-h) - 6px);
  margin-top: 6px;
}

/* ============ RAIL ============ */
nav.rail {
  border-right: 1px solid var(--rule);
  padding: 24px 0 32px;
  display: flex;
  flex-direction: column;
  background: var(--paper);
}
.nav-group { padding: 0 20px; margin-bottom: 26px; }
.nav-group .kicker {
  display: block;
  padding-bottom: 8px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--hairline);
}
.nav-group ul { list-style: none; }
.nav-group li a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 8px 6px 12px;
  margin-left: -12px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-2);
  border-left: 3px solid transparent;
}
.nav-group li a:hover {
  color: var(--ink);
  border-left-color: var(--ink);
  background: var(--surface);
}
.nav-group li a[aria-current="page"] {
  color: var(--ink);
  font-weight: 800;
  border-left-color: var(--accent);
}
.nav-group li a .count {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.nav-group li a .count.hot {
  color: var(--accent-ink);
  background: var(--accent-text);
  padding: 0 5px;
}

/* Plugins, pinned, and visibly a different species from the nav above it. */
.plugins {
  margin-top: auto;
  padding: 18px 20px 0;
  border-top: 3px double var(--rule);
}
.plugins .kicker {
  color: var(--ink);
  border-bottom: none;
  margin-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.plugins .kicker .sys { color: var(--muted); font-weight: 600; letter-spacing: 0.1em; }
.plugin-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
  margin: 0 -8px;
  /* A link since it goes to the Plugins page, but it should not read as body copy. */
  color: inherit;
  text-decoration: none;
}
.plugin-item:hover { background: var(--surface); }
.plugin-item .glyph {
  width: 22px; height: 22px;
  border: 1px solid var(--rule);
  display: grid;
  place-items: center;
  font-size: 10px;
  font-weight: 800;
  background: var(--surface);
  flex: none;
}
.plugin-item .meta { min-width: 0; }
.plugin-item .pname {
  display: block;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.plugin-item .pver { display: block; font-size: 10.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.plugin-item .dot { margin-left: auto; width: 7px; height: 7px; background: var(--ink); flex: none; }
.plugin-item .dot.idle { background: transparent; box-shadow: inset 0 0 0 1.5px var(--muted); }

/* ============ MAIN ============ */
main {
  padding: 26px 28px 64px;
  min-width: 0;
}
.page-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 18px;
  gap: 24px;
}
.page-head h2 {
  font-size: 34px;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1;
}
.page-head .dateline {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.page-head .dateline b { color: var(--accent-text); }

/* The board. Gaps over an ink ground become the gridlines between panels. */
.board {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.panel {
  background: var(--surface);
  padding: 18px 20px 20px;
  min-width: 0;
}
.w-4 { grid-column: span 4; }
.w-6 { grid-column: span 6; }
.w-8 { grid-column: span 8; }
.w-12 { grid-column: span 12; }

.panel-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}
.panel-head .kicker { color: var(--ink); }
/* A panel's title is an `h3` so it appears in the document outline (see `disclose` in
   projects/_macros.html). It keeps the kicker's type: `.panel h3` below is the display
   heading used inside a panel's body, which this is not. */
.panel-head h3.kicker {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 0;
}

/* The heading as the control that shuts its panel. It keeps the kicker's weight and colour ,
   a panel head that suddenly looked like a link would read as navigation to somewhere else,
   and this goes nowhere. The chevron is the only new mark, and it is the thing that turns. */
.disclose {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  color: inherit;
  border-bottom: 1px solid transparent;
}
.disclose:hover { color: var(--accent-text); border-bottom-color: var(--accent); }
.disclose .chevron { font-size: 10px; color: var(--muted); }
.disclose:hover .chevron { color: var(--accent-text); }
/* A shut panel is one line: its head, and the summary beside it. The margin under the head
   belonged to the body that is no longer there. */
.panel.shut .panel-head { margin-bottom: 0; }

.panel h3 {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin-bottom: 8px;
}
.panel p { color: var(--ink-2); max-width: 62ch; }
.panel p + p { margin-top: 10px; }
/* Prose after a fact list is a new thought, not the last row's continuation. */
.facts + p { margin-top: 14px; }

/* Reference list, hairline dividers, no bullets. */
.facts { list-style: none; margin-top: 6px; }
.facts li {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 0;
  border-bottom: 1px solid var(--hairline);
  font-size: 13px;
}
.facts li:last-child { border-bottom: none; }
.facts .k { color: var(--muted); }
.facts .v { font-weight: 700; font-variant-numeric: tabular-nums; }
.facts .v a { border-bottom: 1px solid var(--hairline); }
.facts .v a:hover { color: var(--accent-text); border-bottom-color: var(--accent); }

/* Action. Outlined by default; the accent appears on the way to a click. */
.action {
  display: inline-block;
  margin-top: 16px;
  padding: 9px 16px;
  border: 1px solid var(--ink-2);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  background: none;
  cursor: pointer;
  font-family: inherit;
}
.action:hover { border-color: var(--accent); color: var(--accent-text); }

/* Stub page: an unbuilt area, stated plainly (THEME.md §4.1). */
.stub { text-align: center; padding: 64px 20px 72px; }
.stub h3 { font-size: 28px; margin-bottom: 12px; }
.stub p { margin: 0 auto; }
.stub .action { margin-top: 24px; }

/* --------------------------------------------------------------------------
   Integrations. Same panel, same rules: ink for structure, accent for status.
   -------------------------------------------------------------------------- */

.notice {
  border-left: 3px solid var(--ink);
  padding: 10px 14px;
  margin-bottom: 20px;
  background: var(--surface);
  font-size: 13px;
}
.notice-error { border-left-color: var(--accent); }

.connector + .connector { margin-top: 4px; }

.chips { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.chips li {
  border: 1px solid var(--hairline);
  padding: 3px 8px;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-2);
}
/* Operation names are identifiers, not prose, set them monospaced and unshouted. */
.chips.ops li { letter-spacing: 0; }
.chips.ops code { font-size: 11px; }
/* A second labelled chip group needs air above its label, or the two rows read as one. */
.chips + .kicker { display: block; margin-top: 18px; }

.connector .facts li { align-items: center; }
.connector .facts .v { display: flex; gap: 8px; }
.connector .facts .v .action { margin-top: 0; }

/* Status of one connection. Only a broken one earns the accent. */
.state {
  display: inline-block;
  margin-left: 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.state-error,
.state-expired,
.state-requires_reauthorization { color: var(--accent-text); }

.why { display: block; color: var(--muted); font-size: 12px; margin-top: 4px; }

.connect { margin-top: 18px; max-width: 460px; }
.connect label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.connect input {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--hairline);
  background: var(--paper);
  font-family: inherit;
  font-size: 13px;
  color: var(--ink);
}
.connect input:focus { border-color: var(--ink); }

/* --------------------------------------------------------------------------
   PROJECTS
   Structure by rules, never by shadow or radius (THEME.md §1). The accent
   appears on a status that is wrong and on the way to a click, nowhere else.
   -------------------------------------------------------------------------- */

/* ---- Index table ---- */
.ptable { width: 100%; border-collapse: collapse; margin-top: 6px; font-size: 13px; }
.ptable thead th {
  text-align: left;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 12px 8px 0;
  border-bottom: 2px solid var(--rule);
}
.ptable tbody th,
.ptable tbody td {
  text-align: left;
  font-weight: 400;
  padding: 11px 12px 11px 0;
  border-bottom: 1px solid var(--hairline);
  vertical-align: baseline;
}
.ptable tbody tr:last-child th,
.ptable tbody tr:last-child td { border-bottom: none; }
.ptable tbody th { font-weight: 700; }
.ptable tbody th a { border-bottom: 1px solid var(--hairline); }
.ptable tbody th a:hover { color: var(--accent-text); border-bottom-color: var(--accent); }
.ptable .code { font-variant-numeric: tabular-nums; letter-spacing: 0.04em; font-weight: 700; }
.ptable .num { font-variant-numeric: tabular-nums; }
.ptable .unset { color: var(--muted); }
.ptable .setup { font-variant-numeric: tabular-nums; color: var(--accent-text); font-weight: 700; }
.ptable .setup.done { color: var(--muted); font-weight: 400; }

/* ---- The page control under a long table ----
   One row beneath the table it pages, built like the filter row above it and the window
   controls above the chart: a rule on the edge facing the thing it belongs to, uppercase
   labels, plain links. The rule is on top here because this row sits under its table, where
   the filter row's sits under itself for the same reason.

   Where you are on the left, how many rows a page holds in the middle, the two steps on the
   right, reading order, and the steps land nearest the scroll the reader just finished. */
.pager {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 20px;
  padding-top: 12px;
  margin-top: 4px;
  border-top: 1px solid var(--hairline);
  font-size: 12.5px;
}
.pagecount { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.pagecount b { color: var(--ink); font-weight: 700; }

.pagesizes { display: flex; align-items: baseline; gap: 10px; }
/* The sizes reuse the window control's list: a set of mutually exclusive choices where one is
   in play, which is the same thing the time chart's presets are. So it looks the same. */
.pagesizes .ranges { font-variant-numeric: tabular-nums; gap: 10px; }
.pagesizes .ranges .active {
  font-weight: 800;
  color: var(--ink);
  border-bottom: 1px solid var(--ink);
  padding-bottom: 1px;
}

.pagesteps { display: flex; align-items: baseline; gap: 16px; margin-left: auto; }
.pagestep {
  font-weight: 700;
  white-space: nowrap;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
}
.pagestep:hover { color: var(--accent-text); border-bottom-color: var(--accent); }
/* A step with nowhere to go. Muted and not a link, it holds the row's shape between pages
   without pretending to work. */
.pagestep.off { color: var(--muted); font-weight: 400; }

/* A declared status. Only "at risk" earns the vivid, the rest are plain ink. */
.pill {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 3px 8px;
  border: 1px solid var(--rule);
}
.pill.state-at_risk { border-color: var(--accent); color: var(--accent-text); }
.pill.state-paused,
.pill.state-archived { border-color: var(--muted); color: var(--muted); }

/* ---- Stepper ---- */
.stepper {
  list-style: none;
  display: flex;
  gap: 28px;
  align-items: center;
  padding: 0 0 18px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 20px;
}
.stepper li { font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); }
.stepper li .dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  margin-right: 8px;
  border: 1.5px solid var(--muted);
  vertical-align: baseline;
}
.stepper li.done { color: var(--ink-2); }
.stepper li.done .dot { background: var(--ink); border-color: var(--ink); }
.stepper li.current { color: var(--ink); }
.stepper li.current .dot { background: var(--accent); border-color: var(--accent); }
.stepper a:hover { color: var(--accent-text); }

/* ---- Forms ---- */
.form-panel p { max-width: 68ch; }
.form { margin-top: 18px; max-width: 720px; }
.required-note { font-size: 12px; color: var(--muted); margin-bottom: 18px; }

.field { margin-bottom: 22px; }
.field label,
.control-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 6px;
}
.field label.sub { margin-top: 10px; color: var(--muted); }
.field input[type="text"],
.field input[type="number"],
.field select {
  width: 100%;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 9px 10px;
}
/* The native select keeps its own disclosure marker. Stripping it leaves a control that
   looks exactly like a text field, which is a worse trade than one pixel of chrome. */
.field input:focus,
.field select:focus { outline: 2px solid var(--ink); outline-offset: 1px; }
.field input[readonly] {
  background: var(--track);
  color: var(--ink-2);
  border-style: dashed;
}
.field .why { margin-top: 6px; }
.field-error {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  font-weight: 700;
  color: var(--accent-text);
}
.field input[aria-invalid="true"],
.field select[aria-invalid="true"] { border-color: var(--accent); border-width: 2px; }

/* Two columns where the pairs belong together; one below 1180px. */
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 20px; }
@media (max-width: 1180px) { .field-grid { grid-template-columns: 1fr; } }

.form-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: flex-end;
  padding-top: 18px;
  border-top: 1px solid var(--hairline);
}
.form-actions.wide { max-width: none; margin-top: 4px; padding: 18px 0 0; }

/* The one filled button in the product: the primary action of a form. */
.action.primary {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.action.primary:hover { background: var(--accent-text); border-color: var(--accent-text); color: var(--accent-ink); }
.action.quiet { border-color: transparent; color: var(--ink-2); padding-left: 0; padding-right: 0; }
.action.quiet:hover { border-color: transparent; color: var(--accent-text); }
/* Not colour alone. This is the one button on the screen whose outcome cannot be undone, and
   distinguishing it from its two reversible neighbours by hue is invisible to a reader who
   cannot separate those hues, so it also carries a heavier border and the label is spaced
   wider, which survives greyscale, a colour-vision deficiency and a high-contrast mode. */
.action.destructive {
  border-color: var(--accent);
  border-width: 2px;
  color: var(--accent-text);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.action.destructive:hover { background: var(--accent-text); color: var(--accent-ink); }

/* ---- The seam: which account answered, and which did not ---- */
.wire { border: 1px solid var(--rule); padding: 14px 16px; margin: 16px 0 20px; }
.wire-error { border-left: 3px solid var(--accent); }
.wire-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.wire-head .kicker { color: var(--ink); }
.wire-head .sys { color: var(--muted); }
.wire p { font-size: 13px; max-width: 68ch; }
.wire .action { margin-top: 12px; margin-right: 8px; }
.wire .accounts { list-style: none; font-size: 13px; }
.wire .accounts li { padding: 3px 0; }
.wire .accounts .dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 8px;
  background: var(--ink);
}
.wire .accounts .dot.bad { background: var(--accent); }

/* ---- Option pickers ---- */
/* A fieldset, so the group has a real name in the accessible tree, which means resetting
   the element's own default border, padding and min-width before it draws like the div it
   replaced. `min-width: 0` is the one that matters: a fieldset defaults to min-content, which
   stops it shrinking inside a grid column. */
.picker {
  border: 1px solid var(--rule);
  max-height: 420px;
  overflow-y: auto;
  margin: 0;
  padding: 0;
  min-width: 0;
}
.picker > legend.control-label { padding: 0; margin-bottom: 6px; }
.picker-head {
  position: sticky;
  top: 0;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
  padding: 8px 12px;
}
.option-group {
  background: var(--track);
  padding: 6px 12px;
  border-bottom: 1px solid var(--hairline);
}
.option {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 9px 12px;
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
}
.option:last-child { border-bottom: none; }
.option:hover { background: var(--surface); }
.option input { accent-color: var(--accent); flex: none; }
.option-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.option-name { font-size: 13px; font-weight: 700; }
.option-detail { font-size: 11.5px; color: var(--muted); }

/* ---- Provider chooser ---- */
.segmented { border: none; margin-bottom: 22px; }
.segment { border: 1px solid var(--hairline); margin-top: 10px; }
.segment.chosen { border-color: var(--rule); border-left: 3px solid var(--accent); }
.segment-head { display: flex; gap: 10px; align-items: baseline; padding: 12px 14px; cursor: pointer; }
.segment-head input { accent-color: var(--accent); }
.segment-head b { display: block; font-size: 14px; }
.segment-body { padding: 0 14px 14px; }
.segment-body .wire { margin-top: 0; }

/* ---- Per-board Trello list selection ---- */
/* Three short, mutually exclusive answers: side by side reads as one choice, where the
   stacked provider chooser reads as three things to consider in turn. */
.segmented.compact { margin: 0 0 14px; }
.segmented.compact .segment { margin-top: 0; }
.segmented.compact .segment-head { padding: 9px 12px; }
.segmented.compact .segment-head b { font-size: 13px; }
/* Unconditional, not behind a 720px query. `body` is held at a 1080px minimum (tokens.css)
   and THEME.md defers everything narrower as out of scope, so the query was always true
   inside the supported range, dead weight there, and a real sub-1080 responsive behaviour
   contradicting the written policy anywhere else. */
.segmented.compact {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.segmented.compact legend { grid-column: 1 / -1; }
.board-lists { margin: 0; }
.board-lists .wire .picker { margin-top: 4px; }
.board-lists .form-actions { margin-top: 14px; }
.board-lists .wire-head a { border-bottom: 1px solid var(--hairline); }

/* ---- Settings frame ---- */
.settings-layout { display: grid; grid-template-columns: 236px 1fr; gap: 1px; background: var(--rule); }
.settings-layout > * { background: var(--surface); padding: 20px 22px 24px; }
.subnav ul { list-style: none; }
.subnav li a {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  padding: 9px 10px;
  font-size: 13px;
  border-left: 3px solid transparent;
}
.subnav li a:hover { border-left-color: var(--ink); background: var(--paper); }
.subnav li a[aria-current="page"] { border-left-color: var(--accent); font-weight: 800; }
.subnav li.danger { border-top: 1px solid var(--hairline); margin-top: 10px; padding-top: 10px; }
.subnav .mark { font-size: 12px; color: var(--muted); }
.subnav .mark.todo { color: var(--accent-text); }
.settings-body .tab-description { color: var(--ink-2); margin: 4px 0 4px; max-width: 68ch; }
.settings-body > .panel-head { border-bottom: 1px solid var(--rule); padding-bottom: 10px; }

.danger-zone { border-top: 1px solid var(--hairline); margin-top: 24px; padding-top: 18px; }
.danger-zone h3 { font-size: 16px; font-weight: 800; margin-bottom: 8px; }
.danger-zone p { color: var(--ink-2); max-width: 68ch; margin-bottom: 12px; }
.danger-zone .field { max-width: 320px; }
/* The confirmation on the bulk-summary delete. Wider than the typed-name field it sits
   above, because the sentence is the control here rather than the box. */
.confirm-check {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink-2);
  cursor: pointer;
}
.danger-zone .field:has(.confirm-check) { max-width: 68ch; }
.confirm-check input { accent-color: var(--accent); }

/* ---- Setup checklist ---- */
.checklist { list-style: none; margin-top: 6px; }
.checklist li {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 11px 0;
  border-bottom: 1px solid var(--hairline);
}
.checklist li:last-child { border-bottom: none; }
.checklist .mark { font-weight: 700; color: var(--accent-text); flex: none; width: 14px; }
.checklist li.done .mark { color: var(--ink); }
.checklist .body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.checklist .title { font-size: 13px; font-weight: 700; }
.checklist .summary { font-size: 11.5px; color: var(--muted); }
.checklist .action { margin-top: 0; flex: none; }

.page-head .dateline a { border-bottom: 1px solid var(--hairline); }
.page-head .dateline a:hover { color: var(--accent-text); border-bottom-color: var(--accent); }

/* An action or a trailing label in a panel head sits at the far end of the rule. */
.panel-head .action { margin-top: 0; margin-left: auto; }
.panel-head .sys { margin-left: auto; color: var(--muted); }

/* ---- Outstanding pull requests ---- */
.prtable th[scope="row"] { max-width: 44ch; }
.prtable .repo { color: var(--ink-2); font-size: 12.5px; }
.prtable .when { white-space: nowrap; font-variant-numeric: tabular-nums; }
.prtable .prmeta { display: block; font-size: 11px; color: var(--muted); }

/* A reviewer who has not answered is outlined; one who has is filled. The difference is the
   whole signal, so it is carried by weight rather than by colour alone. */
.reviewers { list-style: none; display: flex; flex-wrap: wrap; gap: 4px; }
.reviewers li {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 2px 6px;
  background: var(--ink);
  color: var(--paper);
}
.reviewers li.pending {
  background: none;
  color: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--hairline);
}

/* A three-valued cell. The accent marks the answer worth acting on, whichever word that
   happens to be, "review started: no" and "conflicts: yes" are both the bad news. */
.flag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.flag.bad { color: var(--accent-text); }
.flag.ok { color: var(--muted); }
.flag.unknown { color: var(--muted); }

/* A sortable column head. The label is the link, so the whole thing is one target; the
   arrow marks the column in play and takes ink, because it reports state rather than
   inviting a click. Declared for every table on the page: two of them sort now, and a third
   should not have to bring its own copy of this. */
.ptable thead .sortlink {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  border-bottom: 1px solid transparent;
}
.ptable thead .sortlink:hover { color: var(--accent-text); border-bottom-color: var(--accent); }
.ptable thead .sortlink.active { color: var(--ink); }
.ptable thead .arrow { font-size: 11px; }

/* ---- Open issues and cards ---- */

/* The same column treatments as the review queue, which is the point: a repository name and
   a board name are the same kind of thing to a reader, and a second visual language for the
   second table would make the page read as two products. */
.itable th[scope="row"] { max-width: 46ch; }
.itable .repo { color: var(--ink-2); font-size: 12.5px; }
.itable .when { white-space: nowrap; font-variant-numeric: tabular-nums; }
.itable .prmeta { display: block; font-size: 11px; color: var(--muted); }
.itable .code { white-space: nowrap; }

/* People on an item, and the tags on it. Outlined rather than filled: the filled chip in the
   review queue means "this person has answered", and reusing the fill here would claim
   something these names do not say. */
.people,
.tags { list-style: none; display: flex; flex-wrap: wrap; gap: 4px; }
.people li,
.tags li {
  font-size: 11px;
  letter-spacing: 0.03em;
  padding: 2px 6px;
  color: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
.people li { font-weight: 700; }

/* The filter row. One line above the table it scopes, built like the window controls above
   the chart, same rule beneath, same uppercase labels, because they do the same job. */
.filterbar {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 10px 16px;
  padding-bottom: 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--hairline);
}
.filter { display: flex; flex-direction: column; gap: 4px; }
.filter label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.filter select,
.filter input {
  font: inherit;
  font-size: 12.5px;
  font-family: var(--sans);
  padding: 4px 6px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  color: var(--ink);
  min-width: 150px;
}
.filter select:focus,
.filter input:focus { border-color: var(--ink); }
.filterbar .action { margin-top: 0; }
/* A yes/no menu does not need the width of a repository name, and the review queue has three
   of them beside three long ones, at full width they wrap to a second row for no reason. */
.filter.narrow select { min-width: 110px; }

/* The project's identity, now that it has the row to itself. The list is capped rather than
   stretched: a key on the far left and its value on the far right of a 1200px panel is a
   long way for an eye to travel for five facts. */
.identity .facts { max-width: 720px; }

/* --------------------------------------------------------------------------
   Time on this project, a stacked bar per person, a segment per task.

   The whole chart is HTML and CSS. There is no chart library and no canvas: the
   bars are boxes whose widths are percentages the server worked out, which is
   what makes them readable with no JavaScript, printable, and selectable.

   Bold Editorial applies to the marks as much as to the panel. Square ends, no
   shadow, no radius (THEME.md §1); tones of ink rather than hues (§2.1); and the
   gridlines are hairlines, one step off the surface, never dashed.
   -------------------------------------------------------------------------- */

.timepanel { position: relative; }

/* ---- The window controls ---- */

/* One row, above the chart it scopes. The presets are links and the custom range is a
   GET form, so every window this panel can show is an address. */
.rangebar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 16px;
  padding-bottom: 12px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--hairline);
}
.rangebar > .kicker { flex: none; }

.ranges { list-style: none; display: flex; gap: 14px; }
.ranges a {
  font-size: 12.5px;
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
}
.ranges a:hover { color: var(--accent-text); border-bottom-color: var(--accent); }
/* The window in play is stated by weight and a rule, not by a filled chip: a selected
   state that paints a box is the card UI the direction is avoiding. */
.ranges a.active {
  font-weight: 800;
  color: var(--ink);
  border-bottom-color: var(--ink);
}

.customrange {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.customrange label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.customrange input[type="date"] {
  font: inherit;
  font-size: 12.5px;
  font-family: var(--sans);
  padding: 4px 6px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  color: var(--ink);
}
.customrange input[type="date"]:focus { border-color: var(--ink); }
.customrange input[aria-invalid="true"] { border-color: var(--accent); }
.customrange .action { margin-top: 0; }

/* ---- The chart ---- */

.tchart { margin-top: 4px; }

/* Name, bar, total. One grid, so every bar starts and ends on the same two lines and the
   lengths can be compared down the column. */
.trow,
.taxis {
  display: grid;
  grid-template-columns: 190px 1fr 62px;
  align-items: center;
  gap: 14px;
}
.trow { padding: 5px 0; }

.tname {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 13px;
  font-weight: 700;
  /* A long name is cut with an ellipsis rather than wrapping: a two-line name would set
     its own row taller than the rest and the bars would stop reading as one series. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tinitials {
  flex: none;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 2px 4px;
  background: var(--ink);
  color: var(--paper);
}

/* The plot. Gridlines are painted here, behind the bar, from the tick spacing the server
   worked out, so they land on the round numbers the axis labels name. */
.ttrack {
  position: relative;
  display: flex;
  align-items: stretch;
  height: 18px;
  background-image: repeating-linear-gradient(
    to right,
    var(--hairline) 0,
    var(--hairline) 1px,
    transparent 1px,
    transparent var(--tick)
  );
}
/* The axis's own line at the far right, so the plot is closed. */
.ttrack::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 1px;
  height: 100%;
  background: var(--hairline);
}

/* One task's share of one person's bar.

   The 2px gap between touching segments is painted inside the segment's own box as a strip
   of surface, not added as a border or a margin: a border would eat 2px of the value and a
   margin would push the bar past its length. The box stays exactly as long as the hours it
   stands for. */
.tseg {
  position: relative;
  flex: none;
  min-width: 2px;
  background-color: var(--tone-1);
  background-image: linear-gradient(
    to right,
    transparent calc(100% - 2px),
    var(--surface) calc(100% - 2px)
  );
}
.tseg:last-child { background-image: none; }

.tone-1 { background-color: var(--tone-1); }
.tone-2 { background-color: var(--tone-2); }
.tone-3 { background-color: var(--tone-3); }
.tone-4 { background-color: var(--tone-4); }
.tone-5 { background-color: var(--tone-5); }
.tone-6 { background-color: var(--tone-6); }
.tone-7 { background-color: var(--tone-7); }

/* The hit target is taller than the mark, so a thin segment is still reachable with a
   pointer, and the segment answers to the keyboard the same way it answers to a hover. */
.tseg::before {
  content: "";
  position: absolute;
  inset: -6px 0;
}
.tseg:hover,
.tseg:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; z-index: 3; }

/* The readout. A rule and a paper ground rather than a shadow, the direction has no
   shadows, and a 1px rule reads as more of a broadsheet callout than a floating card. */
.tip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%);
  z-index: 5;
  display: none;
  min-width: 150px;
  max-width: 260px;
  padding: 8px 10px;
  background: var(--surface);
  border: 1px solid var(--rule);
  text-align: left;
  pointer-events: none;
}
.tseg:hover .tip,
.tseg:focus-visible .tip,
.tseg:focus .tip { display: block; }
/* The value leads and the task follows: the reader already knows which segment they are
   pointing at and wants the number. */
.tip b {
  display: block;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.1;
}
.tip .tiptask {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
}
.tip .tipwho {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--muted);
}
/* The last segment of a long bar sits at the right edge of the panel, where a centred
   readout would hang off it. Those anchor to their own right edge instead. */
.tseg:last-child .tip { left: auto; right: 0; transform: none; }
.tseg:first-child .tip { left: 0; right: auto; transform: none; }
/* The top row has nothing above it but the window controls, so its readout opens downwards
   rather than over them. */
.trow:first-child .tip { top: calc(100% + 8px); bottom: auto; }

.ttotal {
  text-align: right;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* The axis, inside the chart's own box so its labels cannot be clipped. */
.taxis { padding-top: 6px; }
.tticks { position: relative; height: 14px; }
.ttick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
/* The ends read against the plot's edges rather than hanging over them. */
.ttick:first-child { transform: none; }
.ttick:last-child { transform: translateX(-100%); }
.taxisunit {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: right;
}

/* ---- The key ---- */

/* Always present past one task: a tone is never the only thing carrying which task a
   segment is. Swatches are rectangles, matching the mark they stand for. */
.tkey {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 2px 20px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}
.tkey li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12.5px;
  min-width: 0;
}
/* No background here: the tone class on the element carries it, and a colour declared on a
   two-part selector would outrank the single-class tone and paint every swatch the same. */
.tkey .tswatch {
  flex: none;
  width: 12px;
  height: 12px;
  transform: translateY(1px);
}
.tkey .tkeyname {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The count under "Other tasks" belongs on the same line as the label at this density. */
.tkey .tkeyname .why { display: inline; margin-left: 6px; }
.tkey .tkeyhours {
  margin-left: auto;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---- The numbers ---- */

/* Not a fallback for the chart: the way to read a figure a segment is too short to hold,
   and the way to read any of them without a pointer. */
.tnumbers { margin-top: 18px; }
.tnumbers summary {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
}
.tnumbers summary:hover { color: var(--accent-text); }
.tnumbers[open] summary { color: var(--ink); margin-bottom: 6px; }
/* A task column per task: with seven of them and long names this is wider than the panel, so
   it scrolls in its own box rather than making the whole page scroll sideways. */
.tscroll { overflow-x: auto; }
/* Focusable, so it needs to look focused: this is the only way to scroll it from a keyboard. */
.tscroll:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.ttable { font-size: 12.5px; min-width: 100%; width: auto; }
.ttable thead th.num,
.ttable td.num { text-align: right; font-variant-numeric: tabular-nums; }
.ttable td.total { font-weight: 700; }
.ttable tfoot th,
.ttable tfoot td {
  border-top: 1px solid var(--rule);
  padding: 8px 10px 0 0;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ttable tfoot td { text-align: right; }
.ttable tfoot th { text-align: left; }

/* ---- The change summary panel (DESIGN.md Slice 2) ---- */

/* The model's output can only ever become three elements, a repository heading, a
   paragraph, a bullet list, and these are their clothes. The heading is monospaced and
   unshouted like every other identifier in the product, raised to ink weight because it is
   doing a section's job. */
.summary-body { max-width: 72ch; }
.summary-body h4.repo {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-top: 18px;
  margin-bottom: 4px;
}
.summary-body p + p { margin-top: 10px; }
.summary-bullets { list-style: none; margin: 6px 0 0; }
.summary-bullets li {
  padding: 2px 0 2px 16px;
  position: relative;
  color: var(--ink-2);
}
/* An en-dash marker, not a disc: every mark in this product is a rule, not a dot. */
.summary-bullets li::before {
  content: "–";
  position: absolute;
  left: 0;
  color: var(--muted);
}

/* Provenance: hairline-ruled, quiet, and monospaced where it names identifiers. The SHA
   range is the exact claim "this is the diff that was summarized" (§S2.3). */
.provenance {
  margin-top: 16px;
  padding-top: 10px;
  border-top: 1px solid var(--hairline);
}
.provenance .prov-line { color: var(--muted); font-size: 12px; }
.provenance code,
.changespanel .facts code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11.5px;
  letter-spacing: 0;
}
.provenance .facts { max-width: 720px; }
.provenance .facts .v { font-weight: 400; color: var(--ink-2); }

.summary-actions {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-top: 14px;
}
.summary-actions .action { margin-top: 0; }

/* The redirect fragments land focus here (tabindex="-1", §S2.7). `:focus-visible` rather
   than `:focus`, so the ring is absent for the programmatic landing and present when
   somebody arrived by keyboard, this is the slowest, most consequential action in the app,
   and it was the one place focus went with nothing at all to show where it had gone. */
.changespanel:focus { outline: none; }
/* The in-flight announcement (static/js/summary.js). Empty on render, and an empty paragraph
   should take no room; it only becomes visible once there is something to say. */
.summary-progress:empty { display: none; }
.summary-progress {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink-2);
  margin-bottom: 12px;
}
.changespanel:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

/* The repository picker: a folded question, not a form section. Closed it is one quiet
   line; open it borrows the settings pickers' .option rows so a ticked repository looks
   the same here as where it was first chosen. */
.repopick { margin: 14px 0; max-width: 560px; }
.repopick summary {
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink-2);
  list-style-position: inside;
}
.repopick summary:hover { color: var(--accent-text); }
.repopick[open] summary { margin-bottom: 8px; }
.repopick .option code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  letter-spacing: 0;
}
.repopick .picker { border: 1px solid var(--hairline); background: var(--surface); }

/* The in-flight vocabulary: the mode toggle under the picker, the ↳ margin in the
   provenance block, and the model's `~ ` lines under their template-drawn kicker. All of
   it quieter than the landed material it annotates. */
.unmerged-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 14px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink-2);
  cursor: pointer;
  max-width: fit-content;
}
.unmerged-toggle input { accent-color: var(--accent); }

.facts li.branchline { border-bottom: none; padding-top: 0; }
.facts li.branchline .k { padding-left: 14px; font-weight: 400; }
.facts li.branchline .v { font-weight: 400; }

.inflight-kicker { display: block; margin-top: 10px; }
.summary-bullets.inflight li { color: var(--muted); }

/* ---- Folded help ---- */

/* A definition somebody needs once. Shut, it is one quiet line under the table it explains;
   the ten lines of answer are still on the page, just not in the way of it. Styled like
   `.tnumbers`, because it is the same gesture, a footnote you open, and two disclosure
   idioms on one panel would read as two different kinds of thing. */
.help { margin-top: 18px; }
.help summary {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
}
.help summary:hover { color: var(--accent-text); }
.help[open] summary { color: var(--ink); margin-bottom: 8px; }
.helpbody p {
  color: var(--muted);
  font-size: 12px;
  max-width: 88ch;
  margin-top: 6px;
}
.helpbody p:first-child { margin-top: 0; }

/* ---- Searching a picker ---- */

/* The box sits in the picker's sticky head, so it stays reachable while the list under it
   scrolls. Its form is declared outside the form that saves, HTML forms cannot nest, and
   the input joins it by `form=`. */
.picker-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.picker-find { display: flex; align-items: center; gap: 6px; }
.picker-find input[type="search"] {
  font: inherit;
  font-size: 12.5px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 4px 8px;
  width: 200px;
  min-width: 0;
}
.picker-find input[type="search"]:focus { outline: 2px solid var(--ink); outline-offset: 1px; }
.picker-find .action { padding: 3px 9px; font-size: 10px; }
/* A term that matched nothing. Inside the picker, under the box it was typed into, because
   that is where the reader is looking and where the way back is. */
.option-empty { padding: 14px 12px; margin: 0; }

/* A label the page does not need to draw but a screen reader does. Not `display: none`,
   which would take it out of the accessibility tree along with the layout. */
.offscreen {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- Which Clockify projects the chart covers ---- */

/* A second row of toggles under the window, in the window's own vocabulary: both scope this
   chart and nothing else, and giving the second one a different shape would suggest it
   scoped something different. Each toggle is an address. */
.sourcebar { margin-top: -4px; }
.sourcebar .ranges { flex-wrap: wrap; }
.sourcebar .ranges a[aria-pressed="true"] {
  font-weight: 800;
  color: var(--ink);
  border-bottom-color: var(--ink);
}
/* The per-source totals inside `All the numbers`. The task key's rows without the swatch:
   these rows carry no tone, because a Clockify project is not a shade in the chart. */
.tkey.tsources { margin-top: 14px; }


/* ---- Focus ---- */

/* One rule for everything that takes focus. Before it, only five inputs had a designed ring
   and every link, `.action` button, picker checkbox and panel heading fell back to the user
   agent's, which on these inked and vermilion surfaces is easy to lose. `:focus-visible`, so
   a mouse click does not draw it; the per-control rules above still win where a control wants
   its own treatment. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* The stub page centres its head; the original used an inline style for this. */
.stub .stub-head { justify-content: center; }

/* ============ SIGN-IN ============ */
/* No rail before sign-in: the shell becomes one column. */
.shell.solo { grid-template-columns: 1fr; }

/* The profile block gains a sign-out form. The action loses its top margin, which was
   set for a button under a paragraph, not one sitting in a flex row. */
.profile .action { margin-top: 0; }
.profile .signout { margin-left: 8px; }

/* The one panel on the sign-in page. Same stub geometry, a little more room. */
.login p { max-width: 48ch; }
.login code {
  font-size: 12px;
  border: 1px solid var(--hairline);
  padding: 1px 5px;
}

/* --------------------------------------------------------------------------
   Sources. The admin table carries its row actions inline; the add form is
   the connect form with a select that dresses like its inputs.
   -------------------------------------------------------------------------- */

.panel-head form.inline { margin-left: auto; }
.action.small { margin-top: 0; padding: 5px 10px; font-size: 11px; }
.action:disabled { color: var(--muted); border-color: var(--hairline); cursor: default; }
.rowactions { display: flex; gap: 6px; }
.rowactions form { display: inline; }
.ptable .why code { font-size: 11px; }
.ptable th .state { margin-left: 0; }
.facts .v.hot { color: var(--accent-text); }

.connect select {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--hairline);
  background: var(--paper);
  font-family: inherit;
  font-size: 13px;
  color: var(--ink);
}
.connect select:focus { border-color: var(--ink); }
.connect input + label,
.connect select + label { margin-top: 12px; }
.connect .action { margin-top: 18px; }

/* The lean headings on the public sources page. */
.lean-head {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-top: 22px;
  margin-bottom: 0;
}
.lean-head:first-of-type { margin-top: 0; }
.lean-head .count { color: var(--muted); font-weight: 600; margin-left: 6px; font-variant-numeric: tabular-nums; }
.lean-head + .ptable { margin-bottom: 8px; }

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

/* --------------------------------------------------------------------------
   The feed. A story is an ordinary panel: the headline is the panel's display
   heading, the summary its prose, and the outlets it rests on are chips. Only
   the three small lines around them need rules of their own.
   -------------------------------------------------------------------------- */

.story h3 a { border-bottom: 1px solid var(--hairline); }
.story h3 a:hover { color: var(--accent-text); border-bottom-color: var(--accent); }

/* Topics sit under the headline, quiet enough not to compete with it. */
.story-topics {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: lowercase;
  color: var(--muted);
  margin: -4px 0 10px;
}
.story-topics a { color: var(--muted); border-bottom: 1px solid var(--hairline); }
.story-topics a:hover { color: var(--accent-text); border-bottom-color: var(--accent); }

/* The divergence note. Set apart by a rule on its left rather than by colour:
   it is the other half of the story, not a warning about it. */
.story-differ {
  border-left: 2px solid var(--rule);
  padding-left: 12px;
  margin-top: 6px;
}

/* The spread of the outlets consulted, stated as a fact under the chips. */
.story-spread {
  margin-top: 10px;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   The installation page. Two forms in panels beside the run history, so the
   connect form grows a textarea, a checkbox and a radio pair that dress like
   its inputs, and a run's stored output gets somewhere to sit.
   -------------------------------------------------------------------------- */

.connect textarea {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--hairline);
  background: var(--paper);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink);
  resize: vertical;
}
.connect textarea:focus { border-color: var(--ink); }
.connect textarea + label { margin-top: 12px; }

/* A checkbox or radio reads as a line, not as a field: the control sits
   beside its words rather than under a kicker of its own. */
.connect .check {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
  margin-bottom: 0;
}
.connect .check input { width: auto; accent-color: var(--accent); }
.connect .check + label,
.connect .choices + label { margin-top: 14px; }

/* The label above a group of choices, dressed like the field labels around it. */
.connect .label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.connect .choices { display: flex; gap: 18px; flex-wrap: wrap; }

/* A run's error and raw output. Monospace, scrolled rather than wrapped past
   its height, and ruled like a table so it belongs to the panel it is in. */
.rawout {
  max-height: 420px;
  overflow: auto;
  padding: 10px 12px;
  border: 1px solid var(--hairline);
  background: var(--paper);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* When the feed was last rebuilt, to the left of the status dot. Quiet: it is
   a fact about the installation, not a state to watch. */
.masthead .updated {
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin-right: 14px;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Preferences, and the feed once a reader has stated some. A term is a chip
   with its own remove form, the two feed sections are named by a kicker over
   the board, and a matched term is a pill on the card that says why the story
   is where it is.
   -------------------------------------------------------------------------- */

/* A term carries the button that removes it, so the chip is a row rather than
   a word. The form has no shape of its own inside it. */
.chips.terms li { display: flex; align-items: center; gap: 8px; padding-right: 4px; }
.chips.terms form { display: inline-flex; }
.chips.terms .drop {
  border: 0;
  background: none;
  padding: 0 2px;
  font-family: inherit;
  font-size: 13px;
  line-height: 1;
  color: var(--muted);
  cursor: pointer;
}
.chips.terms .drop:hover { color: var(--accent-text); }

/* The kicker over a board, naming the section it heads. It sits outside the
   board so the gridlines stay the board's own. */
.feed-section { margin: 26px 0 10px; }
.feed-section:first-of-type { margin-top: 0; }
.feed-section .kicker { display: block; }

/* The reader's own terms that this story matched. Pills, set between the
   headline and the summary, quiet enough to read past. */
.story-matched { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.story-matched .pill { text-transform: none; letter-spacing: 0.04em; color: var(--ink-2); }

/* --------------------------------------------------------------------------
   Spoken audio. A story and the whole feed can be listened to, so a card and
   the page head both carry a native player. The browser's own control is kept,
   dressed to the shell: full width, ruled, square, and quiet enough to sit
   under the prose rather than over it.
   -------------------------------------------------------------------------- */

.story-audio { margin-top: 14px; }
.story-audio .kicker,
.page-audio .kicker { display: block; margin-bottom: 6px; }
.story-audio .unset,
.page-audio .unset { margin: 0; font-size: 12px; }

.story-audio audio,
.page-audio audio {
  display: block;
  width: 100%;
  height: 36px;
  border: 1px solid var(--hairline);
  border-radius: 0;
  background: var(--paper);
}

/* The briefing above the boards, between the dateline and the first panel. */
.page-audio {
  max-width: 460px;
  margin: 0 0 20px;
}
