/* aos-hub — "release-engineering paper" (RFC-0004).
 *
 * One monospace face, ink on paper, tables and rules as layout, color
 * strictly semantic. Every asset is first-party: JetBrains Mono (OFL)
 * is embedded in the binary and served from /_assets/ — no font CDNs.
 */

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

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

:root {
  /* Warm "paper" off-white, not stark white. */
  --paper: #f9f9f6;
  --ink: #1f1d18;
  --rule: #d8d4c8;
  /* A fainter rule for in-table data-row separators, so a table reads as a data
   * grid and never as a run of section dividers. Derived from `--rule`+`--paper`,
   * so it tracks the theme. */
  --rule-faint: color-mix(in srgb, var(--rule) 50%, var(--paper));
  /* A stronger, darker rule reserved for section-heading underlines — clearly
   * distinct from every (light) table rule. ~half ink over paper. */
  --rule-strong: color-mix(in srgb, var(--ink) 45%, var(--paper));
  /* Width of the label column in console forms. Shared by the label grid and
   * the submit-button indent so the action always lines up with the controls. */
  --form-label-col: 11rem;
  --form-col-gap: 0.7rem;
  --dim: #6e6a5e;
  --ok: #1c6f4a;
  --warn: #9c5a00;
  --bad: #b3261e;
  /* A warm clay/terracotta as the primary — complements the cream paper
   * and the warm ink (and isn't blue). Deep enough for AA link contrast. */
  --link: #b0492e;
  --accent: #b0492e;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #121310;
    --ink: #d8d8d0;
    --rule: #3a3b36;
    --dim: #8b8c84;
    --ok: #3fb950;
    --warn: #d29922;
    --bad: #f85149;
    /* Lighter clay for contrast on the dark phosphor scheme. */
    --link: #e08a6a;
    --accent: #e08a6a;
  }
}

* { box-sizing: border-box; }

body {
  font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 14px;
  line-height: 1.65;
  background: var(--paper);
  color: var(--ink);
  margin: 0 auto;
  padding: 2.25rem 1.5rem 5rem;
  max-width: 74rem;
  /* Tint every UA-accented control (focus rings, checkboxes, and the
   * search field's clear "x") with our clay primary instead of UA blue. */
  accent-color: var(--accent);
}

.skip-link {
  position: fixed;
  z-index: 100;
  top: 0.5rem;
  left: 0.5rem;
  padding: 0.55rem 0.75rem;
  border: 2px solid var(--accent);
  background: var(--paper);
  color: var(--ink);
  transform: translateY(-160%);
}
.skip-link:focus { transform: translateY(0); }

p { margin: 0.7rem 0; }

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

header.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.4rem;
  border-bottom: 2px solid var(--ink);
  padding-bottom: 0.7rem;
  margin-bottom: 2rem;
}
header.masthead .brand {
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--ink);
}
header.masthead .tagline { color: var(--dim); margin-left: 0.5rem; font-size: 0.85rem; }
header.masthead .crumbs { color: var(--dim); }
header.masthead .crumbs a { color: var(--ink); }
/* Push the session indicator (which doubles as the primary nav when
 * signed in) to the far right; the flex gap keeps the brand and crumbs
 * spaced apart on the left. */
header.masthead .session { color: var(--dim); margin-left: auto; }
header.masthead .session a { color: var(--ink); }
header.masthead .session .who { color: var(--dim); }

h1, h2 {
  font-size: 1rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  /* A heavier, darker rule than any table line (`--rule-strong` ~half ink, 2px)
   * so a section divider never reads as a table header/row separator — the
   * three weights are: heading (2px, dark) > table header (1px, --rule) >
   * data row (1px, --rule-faint). */
  border-bottom: 2px solid var(--rule-strong);
  padding-bottom: 0.4rem;
  margin: 2.4rem 0 1rem;
}
h1 { font-size: 1.15rem; margin-top: 0.5rem; }
/* Subsection title under a group heading (e.g. "Registries" under "Publishing").
 * Title-case + no rule so it reads as subordinate to the uppercase, ruled h2. */
h3 {
  font-size: 1rem;
  font-weight: 700;
  margin: 1.6rem 0 0.5rem;
}
/* Minor label inside a subsection (e.g. an inline create form's heading). */
h4 {
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--dim);
  margin: 1.2rem 0 0.4rem;
}

table {
  border-collapse: collapse;
  width: 100%;
  margin: 0.6rem 0 1.6rem;
}
.table-scroll {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
}
.table-scroll:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
th, td {
  text-align: left;
  padding: 0.45rem 1.1rem 0.45rem 0;
  /* Data rows use the faint rule, distinct from the heavier `--rule` of section
   * headings (which the eye otherwise reads as another table line). */
  border-bottom: 1px solid var(--rule-faint);
  vertical-align: top;
  /* Long unbreakable strings (trust keys, store paths, NAR hashes) must
   * wrap inside the cell rather than widen the table past the page. */
  overflow-wrap: anywhere;
  word-break: break-word;
}
th {
  color: var(--dim);
  font-weight: 400;
  text-transform: uppercase;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  /* The header row keeps the full-weight rule, anchoring the grid. */
  border-bottom: 1px solid var(--rule);
}
td.num { text-align: right; }

/* Storage bindings — a compact stacked list, not a 4-column table: a long
 * object-store endpoint URL gets the full content width to wrap into rather
 * than squeezing the name/kind columns (which forced a name onto two lines and
 * hyphenated the delete button). Each binding is one block separated by the
 * faint data-row rule, matching the "rules as layout" aesthetic. */
.bindings { margin: 0.6rem 0 1.6rem; }
.binding {
  border-bottom: 1px solid var(--rule-faint);
  padding: 0.55rem 0;
}
.binding:first-child { border-top: 1px solid var(--rule); }
/* Name + kind/access chips on one line; the delete control pushed to the end. */
.binding-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem 0.7rem;
}
.binding-head form { margin-left: auto; }
.binding-name { font-weight: 700; }
/* The endpoint/bucket URL on its own full-width line, wrapping anywhere so even
 * a very long object-store URL fits with no horizontal scrollbar. */
.binding-loc { margin-top: 0.35rem; }
.binding-loc code { overflow-wrap: anywhere; word-break: break-all; }

code, pre {
  font-family: inherit;
  background: color-mix(in srgb, var(--ink) 6%, var(--paper));
  border: 1px solid var(--rule);
}
code { padding: 0.05rem 0.3rem; }
pre {
  padding: 0.9rem 1.1rem;
  line-height: 1.5;
  overflow-x: auto;
  margin: 0.7rem 0 1.6rem;
}

/* Action buttons inside a table cell must never break their label
 * character-by-character: the data-cell `word-break: break-word` rule (there to
 * wrap long store-path hashes) would otherwise collapse a narrow action column
 * into "un / pi / n". Keep buttons whole. */
td button { white-space: nowrap; }

/* A dim secondary line stacked under a table cell's primary value — the store
 * hash under a pin's package name, the expiry under its age. Stacking related
 * values keeps a table dense and wide enough to read instead of spreading the
 * data across many narrow, squished columns. */
.subline { color: var(--dim); font-size: 0.85rem; margin-top: 0.15rem; }
.subline code { background: none; border: none; padding: 0; }
/* The pins table keeps each cell's primary line on one row (the sub-lines stack
 * beneath it), so columns size to their content rather than wrapping. */
.pins td { white-space: nowrap; }
/* The inline "expires in N days" editor is a compact field, not a full-width
 * console input. */
.pins input[type="number"] { width: 4.5em; }

.ok { color: var(--ok); }
.warn { color: var(--warn); }
.bad { color: var(--bad); }
.dim { color: var(--dim); }
.good { color: var(--ok); }

/* Destructive controls: delete forms and their headings, flagged so a
 * confirmation-gated removal reads as a deliberate, distinct action. */
h2.danger { color: var(--bad); }
button.danger { color: var(--bad); border-color: var(--bad); }

/* The instance-wide announcement banner (set under instance → branding): a
 * full-width strip above the content on every page. Warm/clay accent so it
 * reads as a notice without alarming. */
.announce {
  border: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--paper));
  color: var(--ink);
  padding: 0.5rem 1rem;
  margin: 0 0 1.4rem;
  border-radius: 2px;
}
/* Configured legal/contact links in the footer, beside the statline. */
.footer-links { margin-left: 0.6rem; }
.footer-links a { color: var(--dim); }
.footer-links a:hover { color: var(--ink); }

/* Unified settings layout: a left sidebar nav beside the content, shared by the
 * registry, org, and instance settings scopes so the management IA is identical
 * across them. Collapses to a horizontal strip on a narrow viewport. */
.settings {
  display: grid;
  grid-template-columns: 12rem 1fr;
  gap: 2rem;
  align-items: start;
  margin-top: 1.2rem;
}
.settings-nav {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  border-right: 1px solid var(--rule);
  padding-right: 1rem;
}
.settings-nav-disclosure > summary { display: none; }
/* Re-expose navigation after a closed mobile disclosure returns to desktop.
 * This overrides the user-agent closed-details rule without resize JS. */
@media (min-width: 48.0625rem) {
  .settings-nav-disclosure:not([open]) > .settings-nav { display: flex; }
}
.settings-nav-group {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.settings-nav-label {
  padding: 0 0.7rem 0.2rem;
  color: var(--dim);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.settings-nav a {
  display: block;
  padding: 0.3rem 0.7rem;
  color: var(--ink);
  border-left: 2px solid transparent;
}
.settings-nav a:hover {
  background: color-mix(in srgb, var(--ink) 5%, var(--paper));
  text-decoration: none;
}
.settings-nav a.active,
.settings-nav a[aria-current="page"] {
  border-left-color: var(--accent);
  color: var(--accent);
  font-weight: 700;
}
.local-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  margin: 0 0 1rem;
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--rule);
}
.local-nav a { padding: 0.45rem 0; }
.local-nav a[aria-current="page"] {
  color: var(--accent);
  font-weight: 700;
  text-decoration-thickness: 2px;
}
/* The content column; min-width:0 lets wide tables/pre scroll instead of
 * blowing out the grid track. The first heading aligns with the nav's top. */
.settings-body { min-width: 0; }
.settings-body > :first-child { margin-top: 0; }
.settings-overview-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 0.8rem;
  margin: 1rem 0 1.4rem;
}
.settings-overview-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 0.8rem;
  border: 1px solid var(--rule);
  color: var(--ink);
  text-decoration: none;
}
.settings-overview-card:hover {
  border-color: var(--accent);
  text-decoration: none;
}
.settings-overview-card strong {
  overflow-wrap: anywhere;
  font-size: 1.05rem;
}
.settings-overview-card span { color: var(--dim); }
@media (max-width: 48rem) {
  .settings { grid-template-columns: 1fr; gap: 0.6rem; }
  .settings-nav-disclosure {
    border-bottom: 1px solid var(--rule);
    padding-bottom: 0.5rem;
  }
  .settings-nav-disclosure > summary {
    display: list-item;
    min-height: 2.75rem;
    padding: 0.65rem 0;
    color: var(--ink);
    font-weight: 700;
    cursor: pointer;
  }
  .settings-nav {
    flex-direction: column;
    border-right: none;
    padding-right: 0;
  }
  .settings-nav-group {
    flex-direction: column;
  }
  .settings-nav-label { padding-top: 0.3rem; }
  .settings-nav a {
    min-height: 2.75rem;
    box-sizing: border-box;
    padding-block: 0.7rem;
  }
}

/* The 16x16 channel partition grid: glyph + color per release, so the
 * encoding survives monochrome and colorblind rendering. r0..r5 follow
 * the legend's frontier-first release order; the glyph alone is
 * sufficient — color is reinforcement, never the only channel. */
pre.partition-grid { line-height: 1.15; letter-spacing: 0.18em; }
.r0 { color: var(--ok); }
.r1 { color: var(--link); }
.r2 { color: var(--warn); }
.r3 { color: var(--bad); }
.r4 { color: var(--accent); }
.r5 { color: var(--dim); }

/* The bucket-calculator's grid cell marker: inverse video, so the hit
 * survives monochrome rendering too. */
.hit { background: var(--ink); }
.hit span { color: var(--paper); }

.statline {
  margin-top: 2.5rem;
  border-top: 1px solid var(--rule);
  padding-top: 0.4rem;
  color: var(--dim);
  font-size: 0.8rem;
}

/* Rollout / progress meter: a bordered track with a solid fill, drawn as a
 * styled box (not tiled block glyphs, which leave hairline gaps). The fill
 * width is a discrete `pct-N` class because the strict CSP forbids the inline
 * `style="width:…"` a continuous value would need. */
.meter {
  display: inline-block;
  width: 9rem;
  height: 0.7rem;
  border: 1px solid var(--rule);
  background: var(--paper);
  vertical-align: middle;
  overflow: hidden;
}
.meter-fill { display: block; height: 100%; background: var(--ok); }
.pct-0 { width: 0; }
.pct-5 { width: 5%; }
.pct-10 { width: 10%; }
.pct-15 { width: 15%; }
.pct-20 { width: 20%; }
.pct-25 { width: 25%; }
.pct-30 { width: 30%; }
.pct-35 { width: 35%; }
.pct-40 { width: 40%; }
.pct-45 { width: 45%; }
.pct-50 { width: 50%; }
.pct-55 { width: 55%; }
.pct-60 { width: 60%; }
.pct-65 { width: 65%; }
.pct-70 { width: 70%; }
.pct-75 { width: 75%; }
.pct-80 { width: 80%; }
.pct-85 { width: 85%; }
.pct-90 { width: 90%; }
.pct-95 { width: 95%; }
.pct-100 { width: 100%; }

/* Pagination nav (shared by every paginated list). The page-of-page label is
 * dimmed so the prev/next/first/last links read as the actionable elements. */
p.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 1rem;
  margin: 0.4rem 0 1.6rem;
}
p.pager .of { color: var(--dim); }

/* Package browser: the prominent lede description, the platform chips, the
 * dependency lists, and the raw-metadata disclosure — all monochrome-first,
 * matching the release-engineering paper aesthetic. */
p.lede {
  font-size: 1.05rem;
  margin: 0.4rem 0 0.8rem;
}

/* The registry README preamble: a readable multi-paragraph block above the
 * registry home, set off from the surrounding terminal-style metadata. */
.readme {
  max-width: 60rem;
  margin: 1rem 0 1.8rem;
  padding-left: 1rem;
  border-left: 2px solid var(--rule);
}
.readme p { margin: 0.55rem 0; }
p.chips { margin: 0.2rem 0 0.8rem; }
.chip {
  display: inline-block;
  border: 1px solid var(--rule);
  padding: 0.05rem 0.45rem;
  margin: 0 0.2rem 0.2rem 0;
  font-size: 0.85rem;
}
ul.deps {
  margin: 0.3rem 0 0.8rem;
  padding-left: 1.2rem;
  columns: 18rem;
}
ul.deps li { break-inside: avoid; }
details.raw-metadata { margin-top: 1.5rem; }
details.raw-metadata summary {
  cursor: pointer;
  color: var(--link);
}
form.pkg-search { margin-bottom: 0.4rem; }
form.pkg-search select {
  font: inherit;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
}
/* The Wireshark-style filter box. The plain <input> is the no-JS floor; the
 * widget wrapper adds a syntax-highlight overlay and a custom autocomplete. */
.filter-field {
  position: relative;
  display: inline-block;
  width: 100%;
  max-width: 52rem;
  vertical-align: top;
  margin-bottom: 0.4rem;
}
input.filter-box {
  width: 100%;
  font: inherit;
}
/* The highlight overlay is hidden until app.js enhances the widget. Once
 * enhanced, the <input> stays in flow and defines the box; the highlight <pre>
 * is layered exactly behind it (so the colored tokens line up under the
 * transparent typed text). */
.filter-field .filter-highlight { display: none; }
.filter-field.enhanced .filter-highlight,
.filter-field.enhanced input.filter-box {
  margin: 0;
  border: 1px solid var(--rule);
  padding: 0.25rem 0.5rem;
  font: inherit;
  font-size: 14px;
  line-height: 1.5;
  white-space: pre;
  overflow: hidden;
  box-sizing: border-box;
}
.filter-field.enhanced .filter-highlight {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: var(--paper);
  color: var(--ink);
}
.filter-field.enhanced .filter-highlight code {
  font: inherit;
  background: none;
  border: none;
  padding: 0;
}
.filter-field.enhanced input.filter-box {
  position: relative;
  z-index: 1;
  background: transparent;
  color: transparent;
  -webkit-text-fill-color: transparent;
  caret-color: var(--ink);
}

/* Grey-shade syntax highlighting: fields darkest, then values, operators, and
 * connectives progressively dimmer — monochrome, matching the paper theme. */
.ftok-field { color: var(--ink); font-weight: 700; }
.ftok-value { color: var(--ink); }
.ftok-string { color: var(--dim); }
.ftok-op { color: var(--dim); }
.ftok-bool { color: var(--dim); font-style: italic; }
.ftok-paren { color: var(--dim); }

/* The custom autocomplete dropdown (no native <datalist> popup). */
.filter-suggest {
  position: absolute;
  left: 0;
  top: 100%;
  z-index: 20;
  min-width: 14rem;
  max-width: 100%;
  max-height: 16rem;
  overflow-y: auto;
  margin-top: 2px;
  background: var(--paper);
  border: 1px solid var(--ink);
}
.filter-suggest .fs-item {
  padding: 0.2rem 0.6rem;
  cursor: pointer;
  white-space: pre;
}
.filter-suggest .fs-item:hover,
.filter-suggest .fs-item.active {
  background: color-mix(in srgb, var(--ink) 10%, var(--paper));
}

/* The field/operator legend under the filter box. */
p.filter-help { font-size: 0.8rem; margin: 0.2rem 0 0.8rem; }
p.filter-help code { font-size: 0.8rem; padding: 0 0.25rem; }

/* Click-to-sort column headers. Each sortable <th> holds a link that cycles
 * unsorted → descending → ascending; the glyph shows the current state and is
 * dim until the column is the active sort key. */
th a { color: var(--dim); }
th a:hover { color: var(--ink); }
th a.sorted { color: var(--ink); }
.sort-glyph { font-size: 0.85em; }
th a:not(.sorted) .sort-glyph { opacity: 0.4; }

/* Form controls — styled GLOBALLY so every button and input looks the
 * same on every page (search boxes, the bucket calculator, passkey
 * buttons, and the producer console alike), not just inside .console. */
button,
input[type="submit"],
input[type="button"] {
  font: inherit;
  padding: 0.25rem 0.9rem;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  line-height: 1.2;
  vertical-align: baseline;
  /* A control's label is a single token — never hyphenate it across lines
   * ("dele-te") when its cell or flex track is narrow. */
  white-space: nowrap;
}
button:hover,
input[type="submit"]:hover,
input[type="button"]:hover {
  background: color-mix(in srgb, var(--ink) 8%, var(--paper));
}
button:disabled { color: var(--dim); border-color: var(--rule); cursor: default; }

/* Every text-like input plus selects — matched by exclusion so a new field
 * type (url, number, tel, …) is styled automatically and never slips through
 * looking like an unstyled browser default. Buttons/checkboxes/radios opt out. */
input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="file"]),
textarea,
select {
  font: inherit;
  padding: 0.2rem 0.4rem;
  border: 1px solid var(--rule);
  background: var(--paper);
  color: var(--ink);
}

/* Focus: the clay accent, never the UA blue. A matching border plus a thin
 * accent outline reads as "active" without shifting layout. */
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
button:focus-visible,
a:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
button:focus-visible { border-color: var(--accent); }

/* The search field's clear control: WebKit/Blink draw a fixed gray-blue "x"
 * that ignores `accent-color`. Replace it with an accent-tinted X via a mask
 * so it tracks the theme (Firefox already honors `accent-color` above). */
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  height: 0.8em;
  width: 0.8em;
  cursor: pointer;
  background-color: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 3l10 10M13 3L3 13' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 3l10 10M13 3L3 13' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Producer console: the no-JS tier is the floor, so every mutation is a
 * plain form + redirect. Layout only — the control styling is global. */
form.console { margin: 0.9rem 0 1.8rem; }
form.console fieldset {
  max-width: 44rem;
  margin: 0;
  padding: 0.8rem 1rem 1rem;
  border: 1px solid var(--rule);
}
form.console legend { padding: 0 0.35rem; font-weight: 700; }
/* Label + control as a two-column grid: the label text occupies a fixed-width
 * first column and the control the second, so every control shares one left
 * edge regardless of label length. (The old `display:block` put each input
 * inline right after its own label text, leaving the column of inputs ragged.)
 * Each label is its own grid, but they all share the same label-column width,
 * so the controls line up across the whole form. */
form.console label {
  display: grid;
  grid-template-columns: var(--form-label-col) 1fr;
  align-items: baseline;
  column-gap: var(--form-col-gap);
  row-gap: 0.2rem;
  margin: 0.5rem 0;
  max-width: 42rem;
}
/* The submit row of a stacked (block) console form is a distinct action region:
 * clearly separated from the fields above. Inline action forms (delete/role/GC,
 * style="display:inline") are excluded so their buttons stay compact in place. */
form.console:not([style]) > button { margin-top: 1.5rem; }
/* In a label-grid form, align the submit with the control column — not the label
 * column — so it reads as the action for the inputs (the horizontal-form
 * convention). Only forms that actually have a label grid are indented, so a
 * button-only form (e.g. "sign out everywhere") isn't pushed off to the right. */
form.console:not([style]):has(label) > button {
  margin-left: calc(var(--form-label-col) + var(--form-col-gap));
}
/* The control fills the second column (any text-like input, select, textarea). */
form.console label input:not([type="checkbox"]):not([type="radio"]),
form.console label select,
form.console label textarea {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
/* A terse field hint (e.g. "optional", "permanent") sits in the control column,
 * just under its input — not floating after a ragged input. */
form.console label > span.dim {
  grid-column: 2;
  justify-self: start;
  font-size: 0.85rem;
}
/* A textarea aligns its top edge with the label rather than its baseline. */
form.console label:has(textarea) { align-items: start; }
/* Checkbox rows keep the grid (label left, toggle in the control column, like a
 * text field) — just vertically centered against the toggle. */
form.console label:has(> input[type="checkbox"]) { align-items: center; }
/* Radio rows stay control-first and inline (mutually-exclusive choices read as a
 * cluster, not a stack of label/value rows). */
form.console label:has(> input[type="radio"]) {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 42rem;
}

/* iOS-style toggle: the native checkbox restyled — empty (paper) when off, green
 * when on, with a sliding thumb. Click/keyboard/focus/submit all stay native, so
 * it is a pure presentational upgrade over the box. Sits left-aligned in the
 * control column (justify-self start) so it lines up with the text inputs. */
form.console input[type="checkbox"],
.linktable input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  justify-self: start;
  width: 2.3rem;
  height: 1.25rem;
  flex: none;
  margin: 0;
  border: 1px solid var(--rule-strong);
  border-radius: 1rem;
  background: var(--paper);
  position: relative;
  cursor: pointer;
  vertical-align: middle;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
form.console input[type="checkbox"]::after,
.linktable input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0.18rem;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  background: var(--dim);
  transform: translateY(-50%);
  transition: left 0.15s ease, background-color 0.15s ease;
}
form.console input[type="checkbox"]:checked,
.linktable input[type="checkbox"]:checked {
  background: var(--ok);
  border-color: var(--ok);
}
form.console input[type="checkbox"]:checked::after,
.linktable input[type="checkbox"]:checked::after {
  left: calc(100% - 1.03rem);
  background: var(--paper);
}
/* A toggle that can't be enabled (e.g. advertising a less-visible cache) reads
 * as greyed-out and not-allowed. */
form.console input[type="checkbox"]:disabled,
.linktable input[type="checkbox"]:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Linked-cache table: an aligned grid where each row is a <form> flattened via
 * display:contents so its cells share the grid's columns. Header row + one form
 * per cache (name · publication state · retention policy · actions). */
.linktable {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  column-gap: 1.5rem;
  row-gap: 0.55rem;
  max-width: 42rem;
  margin: 0.6rem 0 1rem;
}
.linktable form.linkrow {
  display: contents;
}
.linktable-h {
  color: var(--dim);
  text-transform: uppercase;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.35rem;
  white-space: nowrap;
  /* Bottom-align all header cells so their underlines form one continuous rule
   * — without this the empty "actions" header centers in the row and its border
   * floats above the text cells'. */
  align-self: end;
}
.linkrow-name {
  overflow-wrap: anywhere;
}
.linkrow-actions {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
}

/* A standalone field caption (used where a tall control would baseline-align
 * an inline <label> to its bottom edge). Matches the table-header treatment. */
.field-label {
  display: block;
  margin: 0.9rem 0 0.3rem;
  color: var(--dim);
  text-transform: uppercase;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
}

/* The TOML config editor: a plain <textarea> (the no-JS floor) plus a
 * highlight overlay that app.js fills in and reveals by adding `.enhanced`.
 * Until then the textarea is a normal, full-width, resizable control. */
.code-editor textarea {
  display: block;
  width: 100%;
  font: inherit;
  line-height: 1.5;
  tab-size: 2;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--rule);
  background: color-mix(in srgb, var(--ink) 6%, var(--paper));
  color: var(--ink);
  resize: vertical;
}
.code-editor .code-highlight { display: none; }

/* Enhanced mode: the highlight <pre> sits in flow and sizes the box; the
 * textarea is layered on top with transparent text and a visible caret, so
 * what you type lines up exactly over the colored tokens beneath it. */
.code-editor.enhanced { position: relative; }
.code-editor.enhanced .code-highlight,
.code-editor.enhanced textarea {
  margin: 0;
  border: 1px solid var(--rule);
  padding: 0.9rem 1.1rem;
  font: inherit;
  font-size: 14px;
  line-height: 1.5;
  tab-size: 2;
  white-space: pre;
  overflow: auto;
  box-sizing: border-box;
}
.code-editor.enhanced .code-highlight {
  display: block;
  min-height: 18rem;
  background: color-mix(in srgb, var(--ink) 6%, var(--paper));
  color: var(--ink);
  pointer-events: none;
}
.code-editor.enhanced .code-highlight code {
  font: inherit;
  background: none;
  border: none;
  padding: 0;
}
.code-editor.enhanced textarea {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  resize: none;
  background: transparent;
  color: transparent;
  -webkit-text-fill-color: transparent;
  caret-color: var(--ink);
}

/* TOML token palette — drawn entirely from the theme variables so the editor
 * tracks light/dark and never introduces an off-scheme color. */
.code-highlight .t-c { color: var(--dim); font-style: italic; } /* comment */
.code-highlight .t-h { color: var(--accent); font-weight: 700; } /* [table] */
.code-highlight .t-k { color: var(--ink); }                      /* key */
.code-highlight .t-s { color: var(--ok); }                       /* string */
.code-highlight .t-n { color: var(--warn); }                     /* number */
.code-highlight .t-b { color: var(--link); }                     /* boolean */
.code-highlight .t-a { color: var(--ink); }                      /* array/inline table */

/* The repeatable cache-stack editor in the structured config form. Each row is a
 * URL + priority + remove button; the block is indented to the control column
 * (like the submit button) so it lines up under the other fields. app.js clones
 * the trailing row to add more; with no JS the rendered rows stay editable. */
.cache-rows {
  margin-left: calc(var(--form-label-col) + var(--form-col-gap));
  max-width: 42rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.cache-row {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}
.cache-row input[name="cache_url"] { flex: 1 1 auto; min-width: 0; }
.cache-row input.cache-prio { flex: 0 0 6rem; width: 6rem; }
.cache-row .row-del {
  flex: 0 0 auto;
  line-height: 1;
  padding: 0.25rem 0.6rem;
}
.row-add {
  margin: 0.5rem 0 0 calc(var(--form-label-col) + var(--form-col-gap));
}

p.notice {
  border-left: 3px solid var(--ok);
  padding: 0.4rem 0.8rem;
  margin: 0.6rem 0;
  background: color-mix(in srgb, var(--ok) 6%, var(--paper));
}
p.confirm {
  border-left: 3px solid var(--warn);
  padding: 0.4rem 0.8rem;
  margin: 0.6rem 0;
  background: color-mix(in srgb, var(--warn) 6%, var(--paper));
}
.secret {
  display: block;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--bad);
  background: color-mix(in srgb, var(--bad) 5%, var(--paper));
  word-break: break-all;
}

/* -- Attached help (web/help.rs) ------------------------------------------
 * Level 1: a small dim glance one-liner placed after a control. In a console
 * form grid it sits in the control column under its input. */
.hint { color: var(--dim); font-size: 0.85rem; }
form.console label > .hint { grid-column: 2; justify-self: start; }

/* Level 2: a `?` affordance that opens a segmented detail card. The card is
 * shown by `app.js` (adds `.open` + sets fixed coordinates); without JS the
 * affordance still carries a `title` tooltip with the summary. */
.help { position: relative; display: inline-block; margin-left: 0.3rem; }
.help-mark {
  font: inherit;
  font-size: 0.7rem;
  line-height: 1;
  width: 1.05rem;
  height: 1.05rem;
  padding: 0;
  border: 1px solid var(--rule-strong);
  border-radius: 50%;
  background: var(--paper);
  color: var(--dim);
  cursor: help;
  vertical-align: middle;
}
.help-mark:hover,
.help-mark:focus { color: var(--ink); border-color: var(--ink); }
.help-card {
  display: none;
  position: fixed;
  z-index: 50;
  width: 22rem;
  max-width: calc(100vw - 1rem);
  background: var(--paper);
  border: 1px solid var(--ink);
  box-shadow: 0 2px 0 var(--rule);
  padding: 0.6rem 0.75rem;
  font-size: 0.85rem;
  line-height: 1.45;
  text-transform: none;       /* never inherit a heading's uppercase */
  letter-spacing: normal;
  font-weight: 400;
  color: var(--ink);
  white-space: normal;
}
.help-card.open { display: block; }
.help-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-weight: 700;
  padding-bottom: 0.35rem;
  border-bottom: 1px solid var(--rule);
}
.help-tag {
  margin-left: auto;
  color: var(--dim);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.help-sum { display: block; margin: 0.4rem 0; }
.help-seg {
  display: block;
  padding: 0.3rem 0;
  border-top: 1px solid var(--rule-faint);
}
.help-seg b { color: var(--ink); }

/* ── Change-request review surface (RFC-0004 web change requests) ──────────
 * A GitHub pull-request-style surface in the paper idiom: flat, ruled,
 * semantic color, monospace. Everything below works with no JavaScript. */

/* An anchor styled as a button (the global rule styles the <button> element;
 * the "Propose a change" link and the copy button reuse this look). */
a.button,
.button {
  display: inline-block;
  font: inherit;
  padding: 0.25rem 0.9rem;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  line-height: 1.2;
  cursor: pointer;
}
a.button:hover,
.button:hover { background: color-mix(in srgb, var(--ink) 8%, var(--paper)); }
/* A lower-emphasis action (close/reopen): ruled, not inked. */
.button-quiet { border-color: var(--rule-strong); color: var(--dim); }

/* Status badge — glyph + word carry the state (monochrome-safe); color is
 * only reinforcement. Flat bordered, never a pill. */
.badge {
  display: inline-block;
  padding: 0 0.4rem;
  border: 1px solid currentColor;
  font-size: 0.8rem;
  white-space: nowrap;
  vertical-align: baseline;
}
.badge-open { color: var(--ok); }
.badge-merged { color: var(--accent); }
.badge-closed { color: var(--dim); }
.badge-reverted { color: var(--bad); }

/* The Open/Closed/All and Conversation/Diff/Checks tab strips. Mirrors the
 * settings-nav active treatment: an accent underline, no color fills. */
.change-tabs {
  display: flex;
  gap: 1.2rem;
  margin: 0.8rem 0 1.1rem;
  border-bottom: 1px solid var(--rule);
}
.change-tabs .tab {
  padding: 0.3rem 0.1rem;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  text-decoration: none;
  color: var(--ink);
}
.change-tabs .tab:hover { border-bottom-color: var(--rule-strong); }
.change-tabs .tab.active { border-bottom-color: var(--accent); font-weight: 700; }

/* List page: header row (counts + action) and the dense change table. */
.change-list-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}
.change-list-head p { margin: 0; }
.change-table { width: 100%; border-collapse: collapse; }
.change-table td {
  padding: 0.5rem 0.4rem;
  border-bottom: 1px solid var(--rule-faint);
  vertical-align: top;
}
.change-table .change-status { width: 1%; white-space: nowrap; }
.change-table .change-title a { font-weight: 700; }

/* Detail page header. */
.change-head { margin-bottom: 0.6rem; }
.change-h1 { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }
.change-head p { margin: 0.2rem 0 0; }
.change-body {
  margin: 0 0 1rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--rule);
  background: color-mix(in srgb, var(--ink) 3%, var(--paper));
}

/* The conversation timeline: a left rail of events. */
.timeline { list-style: none; margin: 0 0 1.2rem; padding: 0; }
.timeline .tl-item {
  padding: 0.35rem 0 0.35rem 0.4rem;
  border-left: 2px solid var(--rule);
  margin-left: 0.4rem;
}
.tl-item.tl-approve { border-left-color: var(--ok); }
.tl-item.tl-reject { border-left-color: var(--warn); }
.tl-item.tl-merged { border-left-color: var(--accent); }
.tl-item.tl-reverted { border-left-color: var(--bad); }
.tl-glyph { display: inline-block; width: 1.3rem; color: var(--dim); }
.tl-item.tl-approve .tl-glyph { color: var(--ok); }
.tl-item.tl-reject .tl-glyph { color: var(--warn); }
.tl-item.tl-merged .tl-glyph { color: var(--accent); }
.tl-note {
  margin: 0.3rem 0 0 1.7rem;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--rule-faint);
  background: color-mix(in srgb, var(--ink) 3%, var(--paper));
  white-space: pre-wrap;
}

/* The merge box: promotion is CLI-only, so this is a copy-paste command. */
.merge-box {
  margin: 1rem 0;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--rule);
}
.merge-box.merged { border-color: var(--ok); }
.merge-box p { margin: 0 0 0.5rem; }
.copy-row { display: flex; align-items: stretch; gap: 0.5rem; }
.merge-cmd { flex: 1; margin: 0; }
.copy-btn { align-self: stretch; }

/* The per-page action forms (comment, review, close). */
.change-action { margin: 1rem 0; }
.change-action textarea { width: 100%; }
label.inline {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
  margin-right: 1rem;
}

/* The recomputed checks list. */
.checks { list-style: none; margin: 0 0 1rem; padding: 0; }
.checks li { padding: 0.25rem 0; }
.checks .ok { color: var(--ok); font-weight: 700; }
.checks .bad { color: var(--bad); font-weight: 700; }

/* The syntax-highlighted, no-JS diff: each line is a full-width block with a
 * +/-/space marker and TOML token coloring (the t-* classes, shared with the
 * live editor). Selectable and copy-pasteable as a real unified diff. */
.diff {
  margin: 0.3rem 0 1rem;
  border: 1px solid var(--rule);
  overflow-x: auto;
}
.diff .dl {
  display: block;
  white-space: pre;
  padding: 0 0.5rem;
}
.diff .dl-add { background: color-mix(in srgb, var(--ok) 12%, var(--paper)); }
.diff .dl-del { background: color-mix(in srgb, var(--bad) 12%, var(--paper)); }
.diff .dl-meta { color: var(--dim); }
.diff-file { margin: 1rem 0 0.2rem; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    transition-duration: 0.01ms !important;
  }
}

@media (forced-colors: active) {
  .settings-nav a[aria-current="page"],
  .local-nav a[aria-current="page"] {
    border-color: Highlight;
    color: Highlight;
  }
  .chip,
  .settings-overview-card,
  form.console fieldset { border: 1px solid CanvasText; }
}
