/* Single stylesheet for the platform.
   Colour and spacing live in custom properties, and templates use semantic
   HTML with few classes, so swapping in a CSS framework later is a change to
   one <link> element rather than to every template. */

:root {
  --colour-bg: #f5f6f8;
  --colour-surface: #ffffff;
  --colour-text: #1c1f23;
  --colour-muted: #5b6572;
  --colour-accent: #1f6feb;
  --colour-error: #b3261e;
  --colour-warning: #a15c00;
  /* A message is a tint, an edge and a text colour, per level. Named here so that "what does a
     warning look like" has one answer, and so a level cannot be half-defined at a call site. */
  --colour-error-tint: #fdecea;
  --colour-error-edge: #f2b8b5;
  --colour-warning-tint: #fff8e6;
  --colour-warning-edge: #f0d9a8;
  --colour-notice: #14425e;
  --colour-notice-tint: #e7f2f8;
  --colour-notice-edge: #b3d4e5;
  --colour-border: #d7dbe0;
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  /* The two widths this layout has. Named because the second one was written twice: the button
     row's cap had to match the field column's, and a comment saying "matches `.form-field`'s
     own" is two numbers agreeing by hand -- change one and the buttons stop ending where the
     fields end, silently and only on wide windows. One declaration, referenced twice, and the
     alignment cannot come apart.

     `ch`, not `rem`, and that is the whole of what could be made native here. `ch` is the width
     of the font's own "0" glyph, so the browser derives the pixels and the column follows the
     typeface instead of being nailed to it. It was `34rem`; measured in this deployment's
     system-ui at 16px, 34rem is 544px is 59.4ch, so 60ch is the same column said in the unit
     that explains it -- "about sixty characters" is WHY a field is that wide, where 544px is
     only how wide it ended up.

     What this does NOT do is remove the choice, and no unit can. Every other number this
     stylesheet has removed had something to derive from: two buttons that must match take the
     width of the wider one (a shared grid column), two texts on a line take the browser's
     baseline, a footer pushed right takes what is left (`margin-left: auto`). Nothing in the
     content says how wide a text input should be. Sixty is still somebody's decision -- it is
     now a decision in the units of the thing being decided, made ONCE, rather than a size.

     The structural alternative -- put the cap on a container and give the children no width at
     all, so the alignment comes from the DOM instead of from a shared token -- was measured
     against the actual markup and rejected. It cannot sit on `<form>`: the subtitles translate
     form holds BOTH capped fields and `.workbox-advanced-fields`, a grid that must stay full
     width. It would need a wrapper class around every capped run of fields on ten templates,
     turning two `max-width: none` escape hatches into ten opt-ins. More places to forget, for a
     relationship one token already makes unbreakable. */
  --form-width: 60ch;
  --page-width: 60rem;
  --radius: 8px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font);
  color: var(--colour-text);
  background: var(--colour-bg);
  line-height: 1.5;
}

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--colour-surface);
  border-bottom: 1px solid var(--colour-border);
}

.site-title {
  font-weight: 600;
  color: var(--colour-text);
  text-decoration: none;
}

.site-user {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--colour-muted);
  font-size: 0.9rem;
}

.site-main {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: var(--space-4) var(--space-3);
}

h1 { font-size: 1.5rem; margin-top: 0; }
h2 { font-size: 1.1rem; }

.card-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: var(--space-2);
}

.card {
  background: var(--colour-surface);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius);
  padding: var(--space-3);
}

.card a {
  color: var(--colour-accent);
  font-weight: 600;
  text-decoration: none;
}

.card a:hover { text-decoration: underline; }

.empty-state { color: var(--colour-muted); }

/* Everything this platform tells the reader, at three levels. One base and three tints rather
   than six near-identical rules differing in the colour of a left border -- which is what was
   here, and two of the six were already wrong: one duplicated verbatim in two templates, one
   with no rule at all.

   A tint and a text colour, not only an edge. A bordered white box is what every input on the
   page already looks like, so a message wearing one is furniture: measured by screenshot, the
   bordered-white notice was as easy to miss as the unbordered grey text it replaced. */
.message {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid;
  border-radius: var(--radius);
  font-size: 0.95rem;
}

.message > :first-child { margin-top: 0; }
.message > :last-child { margin-bottom: 0; }
.message-text { margin: 0; }

/* Heading-sized only in weight: it names the message, it does not open a section of the page. */
.message-title {
  margin: 0 0 var(--space-1);
  font-size: 1rem;
  font-weight: 600;
}

.message ul { margin: 0; padding-left: var(--space-3); }
.message code { background: rgba(0, 0, 0, 0.05); padding: 0 0.2em; border-radius: 3px; }

/* Red means one thing: this will not happen until you change it. */
.message-error {
  color: var(--colour-error);
  background: var(--colour-error-tint);
  border-color: var(--colour-error-edge);
}

/* It happened, and something about it needs attention -- a shadowed route, a watch that will
   run and mail nobody. Not the failure colour: nothing is refusing to work. */
.message-warning {
  color: var(--colour-warning);
  background: var(--colour-warning-tint);
  border-color: var(--colour-warning-edge);
}

/* This is what happened, and nothing is wrong. Blue rather than grey, because grey is what the
   page's own prose is and the message was invisible in it. */
.message-notice {
  color: var(--colour-notice);
  background: var(--colour-notice-tint);
  border-color: var(--colour-notice-edge);
}



.login-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

.login-card {
  width: min(22rem, 92vw);
  background: var(--colour-surface);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius);
  padding: var(--space-4) var(--space-3);
}

.login-card form { display: flex; flex-direction: column; gap: var(--space-1); }

label { font-size: 0.85rem; color: var(--colour-muted); margin-top: var(--space-1); }

/* Every control, not only the two the login card happens to use. Before this, a number
   input, a textarea and a select were unstyled while the text inputs beside them were not,
   so a form read as three different forms.

   Named by what is EXCLUDED, not by what is included, and the difference is not stylistic.
   The included form -- `input[type="text"], input[type="password"], input[type="number"],
   input[type="email"], input[type="url"]` -- is a list that has to be remembered, and it was
   not: `type="search"` was missing, so the subtitles filter's Search sat at the browser's own
   2px border and 2px padding while the Group and Show selects beside it took this rule's 1px
   and 0.6rem. Measured over CDP, 2026-08-13: 21px tall against 45.2px, on a row whose three
   controls were already the same 240px wide -- the owner's "twice as narrow" was the height.

   An enumeration cannot be completed here even in principle. core/templates/jobs/
   _param_field.html renders `type="{{ kind }}"`, a value its CALLER supplies, so the type of
   a parameter field is not knowable from this file: a plugin declaring a `tel` or `date`
   parameter would silently get an unstyled control, exactly as search did, and nothing would
   fail. An input with no `type` at all defaults to text and the enumeration missed that too.
   Excluding instead means a control is styled unless it is one of the few whose native
   appearance IS the control -- a tick, a radio, a file picker, a slider, a colour swatch --
   which is a closed list that does not grow with what callers invent. */
input:not([type="checkbox"], [type="radio"], [type="file"], [type="submit"],
          [type="button"], [type="reset"], [type="image"], [type="range"],
          [type="color"], [type="hidden"]), textarea, select {
  padding: 0.6rem var(--space-1);
  font: inherit;
  border: 1px solid var(--colour-border);
  border-radius: var(--radius);
  background: var(--colour-surface);
  color: inherit;
}

/* A field is a label above its control. The login card got this from its own flex column;
   every other form had no layout at all, so labels sat beside their controls and each row
   started wherever the previous one ended. */
.form-field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-bottom: var(--space-2);
  max-width: var(--form-width);
}

.form-field label { margin-top: 0; }
.form-field input, .form-field textarea, .form-field select { width: 100%; }

/* A checkbox is the one control narrower than its label, and stretching it to the column
   width would make a tick the size of a paragraph. */
.form-field input[type="checkbox"] { width: auto; align-self: flex-start; }

/* Any list field -- notification addresses, or the pages a batch will watch: one grid for
   the whole list, two columns, every row in the
   same tracks. Each row was its own grid at first, and that is why the buttons did not line
   up -- an `auto` column is sized by its own row's content, so the narrow x and the wider Add
   produced two different column widths and ended their inputs at two different places. One
   grid sizes that column once, from the widest button in the list, with no fixed width to
   guess at and nothing to revisit when a label changes. */
.value-list {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0.35rem 0.5rem;
}

/* The things that are not a row: they span both columns rather than landing in column one and
   leaving a hole beside them. */
.value-list > label,
.value-list > .value-none,
.value-list > .field-help { grid-column: 1 / -1; }

.value-item {
  padding: 0.35rem 0.5rem;
  background: #f4f5f7;
  border: 1px solid var(--colour-border);
  border-radius: 4px;
  font-family: ui-monospace, monospace;
  font-size: 0.9rem;
  overflow-wrap: anywhere;
}

/* Destructive, and shaped like it -- but quiet until pointed at, because it sits beside
   every address and a row of red is a warning about nothing. */
.value-drop {
  padding: 0.2rem 0.55rem;
  line-height: 1;
  font-size: 1.1rem;
  background: transparent;
  color: var(--colour-muted);
  border: 1px solid var(--colour-border);
}

.value-drop:hover:enabled, .value-drop:focus-visible:enabled {
  background: #fdeaea;
  border-color: #c0392b;
  color: #c0392b;
}

.value-none {
  margin: 0;
  color: var(--colour-muted);
  font-style: italic;
}

textarea { min-height: 6rem; resize: vertical; font-family: ui-monospace, monospace; }

/* D34 shows a field the caller may not set rather than hiding it, carrying the value that
   will apply. Nothing said so visually: a disabled control looked like an ordinary one that
   silently refused to submit. */
input:disabled, textarea:disabled, select:disabled {
  background: #eef0f3;
  color: var(--colour-muted);
  cursor: not-allowed;
}

.field-help { font-size: 0.8rem; color: var(--colour-muted); }

input:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--colour-accent);
  outline-offset: 2px;
}

/* `.button` alongside the element, so a LINK can wear the same shape. "Upload a file" leads
   somewhere and must stay an <a> -- a form's button that navigates is a lie to anything reading
   the page without rendering it -- but as bare link text beside a filled `Translate selected`
   it was, in the owner's words, "very unnoticeable". A link that is one of a box's two actions
   should look like one. */
button, .button {
  padding: 0.6rem var(--space-2);
  font: inherit;
  font-weight: 600;
  color: #ffffff;
  background: var(--colour-accent);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
}

/* Spacing belongs to the layout, not to the element type. `button { margin-top }` moved every
   button in every flex line and grid cell it ever landed in: grid aligns the *margin* box, so
   in the address list a 16px top margin put the visible button 16px below the field beside it
   -- measured, y=157.1 against the input's 148.1, with align-items:center correctly applied
   the whole time. The margin is wanted in one situation only, and this says which: a button
   that submits a form, following the stack of fields above it. Every button in this
   application is a form's direct child except the two inside the address list, which is
   exactly the distinction. */
form > button { margin-top: var(--space-2); }

/* Not in the row's action cell, where the flex line's own gap does the spacing and the margin
   only pushed the buttons below the text they belong beside. */
.schedule-actions form > button { margin-top: 0; }

.link-button {
  margin: 0;
  padding: 0;
  color: var(--colour-accent);
  background: none;
  font-weight: 500;
}


.back-link { display: inline-block; margin-top: var(--space-3); color: var(--colour-accent); }

/* --- the jobs pages -------------------------------------------------------------------
   These classes existed in the templates and had no rules at all, which is why every list
   was a run of unspaced lines and every form ran into the next one. */

/* A panel: a bordered box holding one thing a person does. It was four selectors listing the
   boxes that happened to exist, which is a list the fifth panel joins by somebody remembering.
   One class, carried by the templates, and the rule below can then say something about panels
   in general rather than about these four. */
/* A closed disclosure is a button, not a box: there is nothing inside it to bound yet, and a
   card drawn around a lone summary reads as an empty panel somebody forgot to fill. */
details.panel:not([open]) {
  background: none;
  border: 0;
  padding: 0;
}

.panel, .run-detail section {
  background: var(--colour-surface);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}


.schedule-create h3 { margin-top: 0; }

/* The row's three controls -- enable/disable, delete, edit -- sit in one cell and each of the
   first two is its own <form>, which is block-level. Left alone they stacked into a column as
   tall as the row. A flex line puts them side by side and lets the edit panel drop below.
   `.listing-actions` is the same shape for the library groups page's own action cell -- a
   group's Edit (a link), Empty and Delete (each its own <form>), for the identical reason.
   The library listing's action cell was the same shape once too, but this branch removed it
   from there entirely -- binning moved to the edit page -- so this rule now serves only
   groups.html. */
.schedule-actions, .listing-actions {
  display: flex;
  align-items: flex-start;
  gap: var(--space-1);
}

/* nowrap, and it is load-bearing: with wrapping allowed, the flex line's min-content is one
   button, so the compact columns beside it squeezed this one until the two buttons stacked
   again. Without it the min-content is both buttons and the column keeps them on one line. */
.schedule-actions { flex-wrap: nowrap; }

/* The groups page's own action cell carries three controls (a group's Edit, Empty and Delete)
   beside a column with no compact width of its own to squeeze -- wrapping here is what keeps
   a third control from being forced onto a widening single line instead of simply dropping to
   a second one. */
.listing-actions { flex-wrap: wrap; }

.schedule-actions form, .listing-actions form { margin: 0; }

/* Card treatment on the disclosure itself made a white box float beside the row's buttons.
   The card belongs to the panel that opens, which is why it is on [open] and not on the
   element -- a closed disclosure is one word of text and needs no box.

   `.field-disclosure` shares both rules rather than getting its own: it is the identical
   collapsed-by-default reveal, used by core/templates/library/edit.html and
   core/templates/library/upload.html for their own "Create a new group" -- the same control on
   two pages that must not diverge, so its look must not either. */
/* An open disclosure IS a panel -- it had its own copy of the card look, identical to `.panel`
   down to the token names, which is how the two drifted apart on width: the panel's form filled
   it and this one's did not. The templates now say `panel`, so the look comes from one place and
   the width rule that follows it applies here too. What stays here is only what is TRUE of a
   disclosure and not of a panel: the space above it. */
.schedule-edit[open], .field-disclosure[open] {
  margin-top: var(--space-2);
}

.schedule-edit > summary, .field-disclosure > summary {
  cursor: pointer;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--colour-border);
  border-radius: 4px;
  background: var(--colour-surface);
  width: max-content;
}

/* The field that follows it on both pages (Visibility) is a `.form-field` and carries its own
   `margin-bottom`; a `<details>` is not one and had none of its own, closed or open, so it sat
   flush against whatever came next. */
.field-disclosure { margin-bottom: var(--space-2); }

/* A parameter and its value on one line each, rather than a value indented onto the next.
   The name column is sized by the widest label in the cell, so the values line up.

   `.record` (core/templates/library/edit.html, plugins/subtitles/templates/subtitles/review.html)
   shares this rule rather than getting its own: both are a plain `<dl>` of short facts beside
   their labels, and a `<dt>`/`<dd>` pair with no layout at all renders as two lines per fact --
   label, then value on its own line below it -- which is unreadable once there are more than
   two or three (found on review.html: eight lines of "style / formal / ctx / 5 / ..." with
   nothing showing which value went with which label). */
.schedule-params, .record {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem 0.6rem;
  margin: 0;
}

/* The label takes the width it needs and the value takes the rest. Left to wrap, a two-word
   label broke across two lines beside a one-line value. */
.schedule-params dt, .record dt {
  color: var(--colour-muted);
  white-space: nowrap;
}

/* The editor's own row. No bottom border, because it belongs to the row above it rather than
   being a schedule of its own -- a line between them would read as a separator. */
.schedule-editor > td { border-bottom: none; padding-top: 0; }

.schedule-params dd, .record dd {
  margin: 0;
  /* break-word rather than anywhere: it splits a token only when the line cannot hold it,
     where `anywhere` was breaking a URL mid-word while there was still room. */
  overflow-wrap: break-word;
}

/* A column holding one short fact takes what that fact needs and no more, so the parameters
   column keeps the rest -- it holds URLs, and a narrow column breaks them mid-word. `1%` is
   the standard way to say "as narrow as your content allows" to a table: it is a minimum the
   content overrides, not a width. */
th.compact, td.compact {
  width: 1%;
  white-space: nowrap;
}

/* A wrapped column heading reads as a two-line label. They are short enough not to need it. */
.schedule-list th, .run-list th, .all-runs th, .all-schedules th { white-space: nowrap; }

/* The machine name of a kind, kept beside its title rather than instead of it: an operator
   needs it to name the thing in a script, and nobody needs it as a heading. */
.kind-name {
  display: block;
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--colour-muted);
  font-family: ui-monospace, monospace;
}

.schedule-list, .run-list, .all-runs, .all-schedules, .settings-list, .listing {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--space-2);
}

.all-runs th, .all-schedules th, .run-list th, .schedule-list th, .listing th {
  text-align: left;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--colour-muted);
  border-bottom: 1px solid var(--colour-border);
  padding: var(--space-1);
}

.all-runs td, .all-schedules td, .run-list td, .schedule-list td, .listing td {
  padding: var(--space-1);
  border-bottom: 1px solid var(--colour-border);
  vertical-align: top;
}

code { font-family: ui-monospace, monospace; font-size: 0.85em; }

/* `align-items: baseline`, and it is the whole fix: a <dt> at 0.8rem beside a <dd> at 1.25rem
   are two flex items of different text size, and stretched (the default) each sits at the top
   of its own box -- so the number reads as dropped below its label. Aligning the baselines is
   what makes a label and its value one line to the eye. A keyword, not a nudge: any pixel that
   lined these up would be wrong again the moment either font size changed. */
.run-states {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
}
.run-states dt { font-size: 0.8rem; color: var(--colour-muted); }
.run-states dd { margin: 0 var(--space-3) 0 0; font-size: 1.25rem; font-weight: 600; }

.run-log-body, .run-params-body {
  background: var(--colour-surface);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius);
  padding: var(--space-2);
  overflow-x: auto;
}

/* A group heading spans the listing; its files are indented by the first cell's padding so
   they read as inside it. The indent is on the cell rather than on the row, because only the
   first column is nested -- indenting the row would move every column and break the grid the
   single table exists to keep. */
.listing tr.listing-group th {
  text-align: left;
  padding-top: var(--space-2);
}

/* A literal on purpose, not an oversight: no token on the scale equals 2rem (--space-3 is
   1.5rem, --space-4 is 2.5rem), and this amount is not a spacing-scale value in the first
   place -- it is the whole mechanism that makes a file read as belonging to the group heading
   above it, which is what replaced the separate per-group tables. Tokenising it to the nearest
   neighbour would shrink it by a quarter as a side effect of a cleanup nobody asked for. */
.listing td.listing-indent {
  padding-left: 2rem;
}

/* The monitor's Edit panel gets NO indent of its own, and the two attempts at one are worth
   recording so a third is not made.

   On the cell (`tr.watch-editor > td`) it indented the opened form as well, so the panel's left
   edge sat 2rem inside the table while its right edge still reached the table's own -- a card
   aligned on one side and not the other, which is what the owner saw.

   On the summary it did nothing visible: `.field-disclosure > summary` is `width: max-content`
   with a border, so padding pushes the TEXT right and leaves the button's own left edge where
   it was. It read as a wider button, not a nested one. Measured: the summary box at x=192 with
   the doctor's name at x=192, indent 0.

   Neither is needed. What made the panel read as belonging to the row above was taking the
   dividing line out from between them (`.listing tr.watch > td`, below); the indent was a
   second signal for a thing already said once. */

/* The one destructive control in the interface, and it must not read as another Save button
   stacked beneath the real one. No accent fill: this borrows .message-error's own tokens
   (:132) instead of new ones, since it is the same "notice this before acting" signal, applied
   to a control rather than a message -- bordered and tinted, not solid, so Save keeps the
   page's only filled button. */
/* Either shape: the class on a form wrapping the button (the bin page, the schedules table) or
   on the button itself (the edit page, where the buttons sit outside their forms so that a grid
   can size them together). One rule for one look, rather than a second class meaning the same
   thing in a different place. */
.danger-action button, button.danger-action {
  color: var(--colour-error);
  background: var(--colour-error-tint);
  border: 1px solid var(--colour-error-edge);
}

/* Save and Move to bin, made equal by SHARING A COLUMN rather than by agreeing on a number.
   This was `min-width: calc(var(--space-4) * 4)` -- four of something, chosen because it looked
   right, and wrong the moment either label changed length or a translation made one longer. A
   one-column grid takes the width of its widest item and `justify-items: stretch` gives that
   width to both, which is the same intention expressed as a rule instead of as a measurement.

   `width: max-content` keeps the column at the wider BUTTON's size rather than the form's,
   which is `--form-width` and would give the page two enormous buttons. */
/* THE CONVENTION, in one class, for every form on every page: the actions end the row.
   Destructive first, primary last.

   Verified rather than recalled -- the GNOME HIG: "always ensure that the cancel button appears
   first, before the affirmative button. In left-to-right locales, this is on the left." Apple's
   and Material's own pages would not render for checking, so they are not cited. It is also
   what this application had already arrived at on the two rows somebody thought about
   (`Upload a file` before `Translate selected`; the library's links before its own `Upload`),
   while every other page put its button wherever the markup happened to leave it.

   One class rather than a rule per template, because the owner's question was exactly this: a
   button whose position is decided page by page has no position, and a reader learns nothing
   from the first page that helps on the second.

   The SAME `--form-width` the fields use, not a number that matches it, so the buttons end where
   the fields above them end rather than at the far side of a wide page -- they belong to the
   form, not to the window. */
.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-1);
  margin-top: var(--space-2);
}

/* The row is as wide as the COLUMN OF FIELDS it belongs to -- when there is one. `:has` is what
   lets that be a rule rather than a guess: an element holding a `.form-field` has a field column,
   and its own action row lines up with it.

   The cap used to be unconditional, and a screenshot found what that costs. On the run page the
   two panels (`Running`, `Sharing`) hold no fields at all, so `--form-width` had nothing to align
   WITH: the buttons ended at the right edge of an invisible 549px column inside a 910px card, and
   read as centred by accident. Measured, not noticed -- three convention passes done by reading
   templates had all missed it, because in the markup it looks identical to the case that works.

   Without a field column the row fills its container, which in a card is the card. That is the
   same intention -- "end the row you are in" -- said about whichever row that actually is. */
:has(> .form-field) > .form-actions,
:has(.form-field) > .form-actions {
  max-width: var(--form-width);
}

/* A form inside a panel fills it, because the PANEL is already the column. `--form-width` caps a
   field so a form sitting loose on a page does not stretch to the window; a panel has done that
   job already, and capping again left every new-schedule card 912px wide around a 549px form
   with 340px of white to the right of it.

   The owner chose this over shrinking the panel to the form, and the reason is worth keeping:
   with the panels narrowed, the page had THREE different right edges going down it -- the table
   at 1188, the notice at 1096, the cards at 780. Edges that line up are what an eye reads as
   order. The readable-width argument is about PROSE, and a field for a URL is not prose.

   Both halves, and the second is not optional: `.form-actions` carries the same cap, so lifting
   it from the fields alone leaves the button where the fields used to end. Measured -- the first
   attempt did exactly that. */
.panel .form-field,
.panel .form-actions {
  max-width: none;
}

/* An action row under the Advanced grid matches the GRID, not a 60ch column. The rule above it
   caps a row whose form holds fields, which is right when those fields are a column and wrong
   here: inside `.workbox-advanced-fields` they are a full-width grid, so the buttons ended a
   third of the way across with the fields running past them. Measured on the review page, which
   is the first form to put the grid and its own actions in one <form>. */
form:has(.workbox-advanced-fields) > .form-actions {
  max-width: none;
}

/* The margin above is the row's now. `form > button`'s own (meant for a lone button under a
   column of fields) would add to it. */
.form-actions > button { margin-top: 0; }

/* The buttons are the grid items directly -- they sit outside both forms and name theirs with
   the `form` attribute, which is what makes them siblings at all. `margin-top: 0` because the
   grid's own gap is what separates them now, and `form > button`'s margin (meant for a button
   under a column of fields) no longer applies to either of them anyway. */


.pager {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
}

/* The page is two forms: what goes in, and what comes out (docs/superpowers/specs/2026-08-12-
   the-translation-page-is-two-forms-design.md §2). A <fieldset> with a <legend> is what makes
   that a fact on screen rather than only in the markup -- before this, a filter, a button and
   three links floated between and after the tables with nothing showing which one any of them
   belonged to. Styled as a card, matching `.schedule-create`'s own treatment, so the frame reads
   as one thing rather than a bare HTML default (a fieldset's own border is a thin grey rule with
   no padding, which disappears beside every other card on this page). */
.workbox {
  background: var(--colour-surface);
  border: 1px solid var(--colour-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}

.workbox legend {
  padding: 0 var(--space-1);
  font-weight: 600;
  font-size: 1.1rem;
}

/* `Upload a file` and `Translate selected` (design's own mockup, §2): one that leads to this
   box's contents, one that acts on what is ticked in it. Split to the row's two ends so neither
   reads as modifying the other -- adjacent and unstyled, the browser's default block stacking
   put one directly above the other with nothing to say they were not one control in two parts. */
/* A box's own actions END its row, the same as `.form-actions` and for the same reason -- this
   is one convention, not two that happen to agree.

   It was `justify-content: space-between`, which is not "left and right" but "spread whatever
   is here to the two ends", and the two pages using it have different children. The library's
   are a span of links and one button, so the button landed right and read correctly. The
   subtitles box's are two BUTTONS -- `Upload a file` and `Translate selected` -- and
   space-between threw them to opposite ends of the page, so the same control sat right on one
   page and left on the other. The owner found it by opening both.

   The identical defect was already fixed on `.filter-row-actions` a few rules below, whose own
   comment describes this exact behaviour -- and this rule, its sibling, was not looked at.
   Fixed here by the mechanism that has no such failure mode: everything ends the row, and the
   ONE thing that belongs at the other end asks for it. */
.workbox-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

/* `Filter` and `Translate selected` used to render identically -- same size, same fill, same
   weight -- though one only rearranges what is on screen and the other spends money on an AI
   (measured on the deployed page). Outlined in the accent colour rather than filled with it: no
   new colour, only the one token every other button already uses, put to a quieter job. */
/* Reachable from both: `button.button-secondary` for a real button, `.button.button-secondary`
   for a link wearing the shape above. */
.button-secondary {
  color: var(--colour-accent);
  background: var(--colour-surface);
  border: 1px solid var(--colour-accent);
}

/* "no translation yet" wrapped across three lines on the deployed page, making an untranslated
   row three times the height of a translated one for no reason a reader could see -- measured
   directly against a screenshot of it. `nowrap` keeps every state on one line regardless of its
   length; `table-layout: auto` (the default `.listing` already had) then simply gives this
   column the room `nowrap` asks for rather than compressing it back into a wrap. */
.listing td.listing-state {
  white-space: nowrap;
}

/* Fix round, 2026-08-12: the owner's screenshot measured 430px of filter before the first
   filename -- Group, Search, Show, the checkbox and Filter each on their own full-width row,
   `.form-field`'s own layout (a label above a control, meant for a single column of fields
   stacked top to bottom) applied to three fields that belong on one line instead. The design
   spec's own sketch (§2) draws them on two lines: the three inputs, then the checkbox and the
   button. `.filter-row` is that line -- a flex row wrapping only `.form-field`'s block-level
   default, nothing about the field itself.

   `align-items` is `flex-start`, not `flex-end` -- measured directly (`getBoundingClientRect`
   over CDP) rather than assumed, because the first version got this backwards and it only
   showed up once Search's control had a different intrinsic height than Group's and Show's.
   `.form-field` is a top-anchored column (label, then control): flex-end aligns each field's
   BOTTOM edge to the row's, which for the tallest fields (Group/Show, a <select>) IS their
   control's own bottom -- but for a shorter field (Search, an <input>) it drags the entire
   block, label included, down by however much shorter that field's total height is. Measured
   before this fix: Group/Show's label sat at y=199.5 and Search's at y=223.7, 24px lower, for
   a plain reason -- Search's total height (label + gap + input) was 24px less than Group/Show's
   (label + gap + select), and flex-end pushed the whole shorter block down to make the bottoms
   meet. Every label here is the same font at the same size, so flex-start makes their tops (and
   so their whole line) coincide for a reason that has nothing to do with the controls below
   them -- and because each field's control sits a fixed label-height-plus-gap below its own
   label, top-aligning the labels also puts every CONTROL's own top on one shared line, whatever
   that control's height turns out to be. Confirmed after: all three labels' tops and all three
   controls' tops become the same y; only the controls' bottoms still differ, which is exactly
   what "whatever each control's intrinsic height is" allows. */
.filter-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-2);
}

/* Not stretched to the column `.form-field` was built for: `--form-width` and `width: 100%`
   (the rules above, meant for a lone field filling its own row) are what stretched a
   four-option `Show` select to 540px. Overridden here, on the two properties responsible. */
/* The three fields SHARE the row rather than each taking a width somebody chose. `auto` sized
   Group and Show to their longest option and left Search at the browser's default, narrower
   than both, so the row read as two matched controls and one odd one out -- the owner's own
   measurement. That was first answered with `width: calc(var(--space-4) * 6)`, which lined them
   up and which nothing could justify: six of anything is a number picked until it looked right,
   and it stayed the same width in a window half the size.

   `flex: 1` says what is actually meant -- equal shares of whatever room there is -- and the
   controls already fill their field (`.form-field input { width: 100% }` above). `min-width: 0`
   because a flex item refuses to shrink below its content by default, and a `<select>` whose
   longest option is a long group name would otherwise push the row wider than its container. */
.filter-row .form-field {
  max-width: none;
  margin-bottom: 0;
  flex: 1;
  min-width: 0;
}

/* The checkbox and `Filter` at the row's two ends -- the design sketch's second line, "☐ show
   other files too" against "[ Filter ]". Shares `.filter-row`'s flex line and only overrides
   where the content goes on it and how far from the row above it sits, the same relationship
   `.workbox-actions` already has to the box it closes. */
.filter-row-actions {
  align-items: center;
  margin-top: var(--space-1);
}

/* `Filter` ends the row wherever it is, whatever is beside it. This was
   `justify-content: space-between` on the row, which puts two children at its two ends and a
   LONE child at the start -- so the subtitles page (a checkbox, then Filter) read correctly
   while the library, whose row holds only the button, had it hanging off the left. An auto
   margin on the last item is the same intention without the dependency on how many there are:
   it takes all the free space, so the button goes to the end at one child or at three. */
.filter-row-actions > :last-child {
  margin-left: auto;
}

/* Ten declared parameters, each rendered full-width by the shared `param_field` macro
   (core/templates/jobs/_param_field.html -- not edited here; it also serves schedules and the
   submit page), pushed the whole table off screen the moment Advanced was opened. The grid is on
   `.workbox-advanced-fields`, an explicit wrapper the template puts around the macro's loop --
   not on `.workbox-advanced` (the `<details>` itself): Chromium wraps everything after
   `<summary>` in one internal anonymous box, so a `<details>` made `display: grid` only ever
   has two boxes to place -- the summary and that single wrapper -- and every field piled into
   one grid cell regardless of how many columns were declared (measured: fix round, 2026-08-12).
   Sized the same way `.card-list` already sizes its own cards above (`repeat(auto-fit,
   minmax(14rem, 1fr))`, reused rather than a new width invented for this one container) -- two
   or three fields land on a row depending on how wide the box actually is. */
.workbox-advanced-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 0 var(--space-3);
}

.workbox-advanced-fields .form-field {
  max-width: none;
}

/* Chunk size, Context window, Max line length and Fingerprint size are small numbers. D34 still
   renders every one of them -- disabled where this caller may not set it, never hidden -- but
   none needs the 540px box `.form-field input { width: 100% }` gives a field meant to fill a
   single-column page on its own, nor even the ~270px a grid cell gives it here. `width: auto`
   alone is not enough: `.form-field`'s own `display: flex; flex-direction: column` makes width
   the cross axis, and a flex item's cross-size default (`align-self: stretch`) fills the
   container whenever the item's own width computes to auto -- which `auto` is. `align-self:
   flex-start` is the same escape the checkbox rule above already uses, for the identical
   reason. */
.workbox-advanced-fields .form-field input[type="number"] {
  width: auto;
  align-self: flex-start;
}

/* The Advanced panel's one action, on its own line under the grid of fields.

   Two placements were tried and measured first. As a paragraph of prose beside the button it
   wrapped to two lines and pushed the box's own footer -- `Upload a file` and `Translate
   selected` -- around; as a cell inside the grid it made the Model column twice the height of
   its neighbours and left a hole in the row beside it. One short line, right-aligned, is the
   only shape that changes nothing else on the page when its text changes length. */
.workbox-advanced-refresh {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-1);
  margin: var(--space-1) 0 0;
}

/* The hint carries `.field-hint`'s colour and size, but not its block layout: as a flex item it
   must not claim the row's whole width and push the button off the end. */
.workbox-advanced-refresh .field-hint {
  margin: 0;
}

/* A provider that answered with an error, said under the field its models would have been in.
   `.field-hint`'s size and placement, the error colour instead of the muted one -- an empty menu
   and a broken one must not look alike, which is how a three-month-stale codex went unnoticed on
   the deployed page. */
.field-hint-problem {
  color: var(--colour-error);
}

/* The two actions on a watched doctor's row: each is its own <form>, so the cell -- not the
   forms -- is what spaces them.

   `form > button { margin-top: var(--space-2) }` above is right where it was written, under a
   column of fields, and wrong here: a button that IS the whole form has nothing to sit below,
   and it dropped 16px, so the actions read as belonging to the row underneath. Measured over
   CDP before this rule: the doctor's name at y=194, the first button at y=209.

   Zeroed rather than reduced, because the gap below now owns the spacing -- two rules setting
   distance between the same two things is how one of them ends up unexplainable. */
.watch-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
  vertical-align: middle;
}

.watch-actions form { margin: 0; }
.watch-actions form > button { margin-top: 0; }

/* A watched doctor and its Edit panel are ONE row to a reader, and were two to the eye: every
   `.listing td` carries a bottom border, so the doctor's own line fell between him and his
   panel and the panel read as belonging to the doctor below -- the owner's own report, "Edit
   looks like it is outside the doctor's row, as if it refers to something else".

   The pair is joined by taking the line off the first of the two. The panel row keeps its own,
   so one line still separates one doctor from the next -- the same number of lines as before,
   drawn between the right things. */
.listing tr.watch > td {
  border-bottom: 0;
}

/* A link wearing the button shape keeps none of a link's own decoration: underlined text inside
   a bordered box reads as a mistake, and `display: inline-block` is what lets its padding take
   effect at all -- an inline <a> lays its vertical padding over the line above rather than
   growing the line box. */
a.button {
  display: inline-block;
  text-decoration: none;
}

/* The two links that lead away from the library, kept together at the far end of its action row
   so the one button there is not crowded. `.workbox-actions` splits its children to the row's
   two ends; without this wrapper each link would become an end of its own and the row would
   read as three unrelated things. */
.workbox-elsewhere {
  display: flex;
  gap: var(--space-2);
  /* What sends a reader somewhere else does not belong among the box's own actions, so it takes
     the far end: `auto` eats the slack the row has, which is the same mechanism that ends the
     row on the other side. A page with no such links simply has no element carrying this, and
     its actions sit together at the end -- no rule to add and none to remember. */
  margin-right: auto;
}

/* One sentence under a card's own link, saying what is behind it. The dashboard used to carry
   these only for the three core destinations, as prose beside the link -- and the sub-sites,
   which had cards, carried nothing at all, so the cards were the entries a reader knew least
   about. Same treatment for both groups now, which is the whole point of putting them in one
   shape. */
.card-note {
  margin: var(--space-1) 0 0;
  font-size: 0.9rem;
  color: var(--colour-muted);
}
