/* ===========================================================================
   Carbon Design System tokens
   ---------------------------------------------------------------------------
   The neutral palette, type and spacing below are IBM's Carbon Design System,
   g10 theme (carbondesignsystem.com/elements/color/tokens/): the recommended
   theme for product UIs, as opposed to White, which Carbon reserves for
   content/marketing pages. Values confirmed against Carbon's own published
   color package rather than guessed from screenshots.

     gray 100   #161616   primary text
     gray 70    #525252   secondary/muted text
     gray 60    #6f6f6f   placeholder-level text
     gray 50    #8d8d8d   strong borders (fields, dividers a control sits on)
     gray 20    #e0e0e0   subtle borders (card edges, table rules)
     gray 10    #f4f4f4   page background (the "g10" the theme is named for)
     white      #ffffff   card/panel background

   Carbon is famously square: 0 radius on buttons, cards, tiles and inputs.
   Genuinely circular things (an avatar disc, a step marker, a notification
   count) stay circles -- that is a different shape language, not an
   exception to this one.

   Type is IBM Plex Sans: Regular for body, SemiBold for headings and
   buttons, exactly as Carbon specifies. Bundled in ../fonts/ (SIL Open Font
   License), so nothing is ever fetched from a CDN and the interface works with no
   route to the internet.

   --accent is the one colour this file does not source from Carbon: it is
   the business's own brand colour (Settings -> Brand), read at request time
   from an inline <style> in base.html. Carbon's own guidance for a custom
   theme is exactly this -- override the interactive token and let the rest
   of the neutral system carry it. Concretely it drives: links, the focus
   ring, primary buttons, the current-page nav indicator, and progress bars.
   Because it is chosen by whoever runs this installation rather than
   verified once here, the contrast guarantees below hold for its Carbon
   default (#0f62fe, "blue 60") but are the operator's own responsibility if
   they pick another colour -- the same trade-off Carbon's own theming
   guidance leaves to a theme's author.
   =========================================================================== */

:root {
  --gray-100: #161616;
  --gray-70: #525252;
  --gray-60: #6f6f6f;
  --gray-50: #8d8d8d;
  --gray-20: #e0e0e0;
  --gray-10: #f4f4f4;
  --white: #ffffff;

  /* Kept as its own token (not folded into the grays) because a test pins the
     name: a subtle divider colour a control can sit on. */
  --sand-line: #e0e0e0;
  /* --sand-line is 4.10:1 on white, comfortably past the 3:1 that 1.4.11
     asks for the boundary of a control. */
  --field-line: #8d8d8d;
  --cream: #f4f4f4;

  --ink: #161616;
  --ink-soft: #525252;
  --ink-muted: #6f6f6f;
  --panel: #ffffff;

  /* Support colours: fixed, not brand-configurable, since a note or a status
     pill has to mean the same thing regardless of whose colour is chosen
     above. Carbon's own support palette (10 = tint background, 60 = the
     colour itself). */
  --success: #198038;
  --success-bg: #defbe6;
  --danger: #da1e28;
  --danger-bg: #fff1f1;
  --warning: #ff832b;
  --warning-bg: #fff2e8;
  --info-bg: #edf5ff;

  /* The one brand-specific colour: overridden per install via base.html's inline <style>, from
     the business_name/brand_accent settings (see the file header above). */
  --gap-bg: #ffe0c2;          /* an open question, [in brackets], in a draft or a card */
  --gap-ink: #6b2a00;
  --accent: #0f62fe;
  --accent-ink: #ffffff;      /* text/icon colour used on a solid --accent fill */

  --sans: "IBM Plex Sans", "IBM Plex Sans Regular", -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --sans-semi: "IBM Plex Sans SemiBold", "IBM Plex Sans", -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --radius: 0;         /* inputs, notes, popovers -- Carbon's rectangular surfaces */
  --radius-lg: 0;      /* cards, matching Carbon's Tile */
  --radius-pill: 0;    /* buttons, matching Carbon's Button */
  --shadow: 0 1px 2px rgba(22, 22, 22, .06), 0 4px 16px -8px rgba(22, 22, 22, .18);
  --maxw: 1056px;

  /* Settings > Costs: one colour per thing the money went to, from Carbon's categorical palette,
     in its order, so neighbouring segments of a bar stay apart. Never the brand accent. */
  --fn-drafting: #6929c4;     /* purple 70 */
  --fn-translation: #1192e8;  /* cyan 50 */
  --fn-knowledge: #005d5d;    /* teal 70 */
  --fn-indexing: #9f1853;     /* magenta 70 */
  --fn-other: #8d8d8d;        /* gray 50 */
}

* { box-sizing: border-box; }
/* The hidden attribute means hidden, whatever display a class gives the element. Without this,
   .actions { display: flex } showed the job's "Show the results" button on every finished import,
   where nothing was wired to it, so pressing it did nothing. */
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font: 17px/1.55 var(--sans);
}

/* Headings follow Carbon: IBM Plex Sans SemiBold, gray-100, sentence case. */
h1, h2, h3, h4 {
  font-family: var(--sans-semi);
  font-weight: 600;
  color: var(--ink);
  letter-spacing: 0;
}
h1 { font-size: 32px; line-height: 1.2; margin: 0 0 8px; }
h2 { font-size: 23px; line-height: 1.3; margin: 0 0 12px; }
h3 { font-size: 19px; line-height: 1.35; margin: 0 0 6px; }

/* --accent's Carbon default (blue 60, #0f62fe) is verified at 4.5:1+ against every surface a
   link sits on in this file (gray-10, white, and the info-note tint); an operator's own choice
   here is their responsibility, per the file header above. */
a { color: var(--accent); text-decoration-color: var(--gray-50); text-underline-offset: 2px; }
a:hover { color: color-mix(in srgb, var(--accent) 80%, black); }

/* Carbon's small uppercase labels. */
.eyebrow, .side-label {
  font-family: var(--sans-semi);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .085em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 10px;
}
.side-label { margin-bottom: 7px; font-size: 12px; }

/* --- chrome ---------------------------------------------------------------
   Carbon's own UI Shell header is a flat bar, not a floating rounded one;
   the same idea here, square instead of the old floating khaki pill. */

.topbar { background: transparent; padding: 14px 0 0; }
.topbar-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 0 22px;
  display: flex; align-items: center; gap: 16px;
}
.topbar-bar {
  background: var(--white); border-bottom: 1px solid var(--sand-line);
  display: flex; align-items: center; gap: 14px;
  padding: 9px 12px 9px 18px; width: 100%;
}
.logo {
  display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--ink);
  flex: none;                     /* the wordmark must not be squeezed by the navigation */
}
/* A circular badge, not a card or a button -- an avatar-style mark keeps its own shape language
   regardless of the square treatment everything else gets. */
.logo-disc {
  width: 56px; height: 56px; border-radius: 50%; background: var(--gray-10);
  display: grid; place-items: center; flex: none; overflow: hidden; padding: 4px;
}
.logo-disc img { width: 100%; height: 100%; object-fit: contain; }
/* Sized to fit inside the disc without clipping a long business name. */
.logo-mark {
  font-family: var(--sans-semi); font-weight: 600; font-size: 9px; line-height: 1.15;
  text-transform: uppercase; text-align: center; letter-spacing: 0; color: var(--ink);
  padding: 0 3px;
  /* The disc clips (overflow:hidden) rather than wrapping a single long word mid-letter, which
     read worse than a clean crop for anything past a couple of short words. */
  white-space: nowrap; overflow: hidden; text-overflow: clip; max-width: 100%;
}
.logo-text {
  font-family: var(--sans-semi); font-weight: 600; font-size: 17px; line-height: 1.05;
  text-transform: uppercase; letter-spacing: .05em; white-space: nowrap;
}
.logo-sub {
  font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-soft); margin-top: 3px; white-space: nowrap;
}
.topnav { margin-left: auto; display: flex; gap: 2px; align-items: center; flex-wrap: wrap; }
.topnav a {
  font-family: var(--sans-semi); font-weight: 600; font-size: 13.5px;
  letter-spacing: .04em; text-transform: uppercase; text-decoration: none;
  color: var(--ink); padding: 9px 13px; border-radius: var(--radius-pill); white-space: nowrap;
}
/* Six sections do not fit beside the wordmark on a laptop, so the strapline goes first. */
@media (max-width: 1120px) { .logo-sub { display: none; } }
@media (max-width: 900px) {
  .topbar-bar { flex-wrap: wrap; }
  .topnav { margin-left: 0; width: 100%; order: 3; }
  /* The account badge stays up beside the logo rather than on a row of its own. */
  .account-menu { order: 2; }
}
/* The same, wider than that, when ui.js measured that the links would otherwise wrap inside the
   bar beside the logo: they move below it together, in one row. */
.topbar-bar.nav-own-row { flex-wrap: wrap; }
.topbar-bar.nav-own-row .topnav { margin-left: 0; width: 100%; order: 3; }
.topbar-bar.nav-own-row .account-menu { order: 2; margin-left: auto; }
.topbar-bar.no-strapline .logo-sub { display: none; }
/* A long business name wraps on a phone rather than pushing the page sideways. */
@media (max-width: 600px) {
  .logo { flex: 0 1 auto; min-width: 0; }
  .logo-text { white-space: normal; overflow-wrap: anywhere; }
}
.topnav a:hover { background: var(--gray-10); }
/* The inset underline is a second, non-chromatic cue, because 1.4.1 does not allow colour to
   carry state on its own. */
.topnav a.on {
  background: var(--gray-10); color: var(--ink);
  box-shadow: inset 0 -3px 0 var(--accent);
}
/* A circular notification badge, matching Carbon's own count/notification-dot convention. */
.topnav .count {
  display: inline-block; min-width: 20px; text-align: center; margin-left: 7px;
  background: var(--gray-20); border-radius: 99px; font-size: 12px; padding: 1px 6px;
}
.topnav a.on .count { background: var(--white); color: var(--ink); }

main { max-width: var(--maxw); margin: 0 auto; padding: 26px 22px 64px; }
.page-head { margin-bottom: 22px; }
.page-head p { color: var(--ink-soft); margin: 0; max-width: 64ch; }

/* --- cards ---------------------------------------------------------------
   Carbon's Tile: a flat white rectangle with a hairline border, no shadow
   games. White for legibility on the gray-10 page; gray-10-on-gray-10
   panels (via a visible border) group related content or quoted mail. */

.card {
  background: var(--panel); border: 1px solid var(--sand-line); border-radius: var(--radius-lg);
  padding: 20px 22px; margin-bottom: 14px; box-shadow: var(--shadow);
}
.card.flat { box-shadow: none; }
.card.sand { background: var(--gray-10); border-color: var(--sand-line); box-shadow: none; }
.card-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; }
.card-head h2, .card-head h3 { margin: 0; }
.card-head .spacer { margin-left: auto; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* Two cards stacked in one grid cell, such as the reading language over the time-saved estimate. */
.card-stack { display: flex; flex-direction: column; min-width: 0; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
/* minmax(0, 1fr), not 1fr: a bare 1fr track is never narrower than its widest card's content, so
   the logo picker (a fixed 253px in Chrome) and the signature editor pushed every card on /settings
   23px into the page margin and 1px past a 320px window (1.4.10 Reflow). */
@media (max-width: 760px) { .grid-2, .grid-3 { grid-template-columns: minmax(0, 1fr); } }

.stat .num {
  font-family: var(--sans-semi); font-weight: 600; font-size: 34px; line-height: 1.05;
  color: var(--accent);
}
.stat .lbl { font-size: 14px; color: var(--ink-muted); margin-top: 5px; }

/* The dashboard's "Tasks to do" list: a pill naming the kind of task, then a link straight to
   where it gets resolved. Deliberately plain -- the form that actually settles each one lives on
   Knowledge/Queue/Held, this is only ever a pointer there. */
.task-list { list-style: none; margin: 0; padding: 0; }
.task-list li {
  display: flex; align-items: baseline; gap: 9px; padding: 7px 0;
  border-bottom: 1px solid var(--sand-line);
}
.task-list li:last-child { border-bottom: none; padding-bottom: 0; }
.task-list a { overflow-wrap: anywhere; }

/* --- forms --------------------------------------------------------------- */

label {
  display: block; font-family: var(--sans-semi); font-weight: 600; font-size: 14.5px;
  margin-bottom: 6px; color: var(--ink);
}
label .hint {
  display: block; font-family: var(--sans); font-weight: 400; font-size: 14px;
  color: var(--ink-muted); margin-top: 3px;
}
/* The model picker's "this may not fit in memory" note -- same warning colour as .pill.warn,
   just as plain text rather than a pill, since it is a sentence, not a label. */
.hint.warn { color: #ba3a13; }

input[type=text], input[type=password], input[type=number], input[type=email], input[type=date],
input[type=color], select, textarea {
  width: 100%; padding: 11px 13px; font: 16px/1.4 var(--sans); color: var(--ink);
  background: #fff; border: 1.5px solid var(--field-line); border-radius: var(--radius);
  /* .row/.grid-2/.grid-3 stretch .field to the tallest sibling (a two-line label, or one with a
     hint the next field over lacks); this pins the control itself to the bottom of that stretched
     box, so inputs across a row always line up regardless of what grew above them. A no-op outside
     a stretched row: with no slack to fill, auto collapses to the content's own height. */
  margin-top: auto;
}
input[type=color] { padding: 4px 6px; height: 44px; cursor: pointer; }
input[type=file] { max-width: 100%; }
/* A field the server rejected. Paired with aria-invalid so it is not colour alone (1.4.1). */
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--danger); border-width: 2px;
}
.field-error {
  display: block; margin-top: 6px; font-family: var(--sans); font-weight: 400;
  font-size: 14px; color: var(--danger);
}
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* One focus indicator for the whole app, Carbon's own: a solid outline in the interactive
   colour, offset from the control rather than replacing its border (which vanishes under
   Windows High Contrast). :where() keeps specificity at zero so anything below can still
   override. Square, like everything else -- Carbon's focus ring has no radius of its own. */
:where(a, button, .btn, summary, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
@media (forced-colors: active) {
  :where(a, button, .btn, summary, input, select, textarea, [tabindex]):focus-visible {
    outline: 3px solid Highlight;
  }
}
.field { margin-bottom: 15px; display: flex; flex-direction: column; }
.field[hidden] { display: none; }
.row { display: flex; gap: 12px; }
.row > * { flex: 1; }
.row .narrow { flex: 0 0 130px; }
/* 1.4.10 Reflow: .narrow is a fixed 130px that cannot shrink, so a row of fields was as wide as its
   label plus every narrow column. /knowledge's card rows came to 454px at both 320 and 390, and on
   /settings the host-and-port rows stretched each grid card 1px past the window. On a phone every
   field takes a line of its own, as it does everywhere else; a row with no .field in it, such as a
   logo beside its Remove checkbox, keeps its one line. */
@media (max-width: 620px) {
  .row:has(> .field) { flex-wrap: wrap; row-gap: 0; }
  .row > .field { flex: 1 1 100%; min-width: 0; }
}
/* 16px, not 15px: iOS Safari zooms the page on any field below that and does not zoom back out.
   This is the field the owner types in most, on the page the manual says to use on a phone.
   field-sizing lets it grow with the reply instead of scrolling; browsers without it keep the
   min-height and scroll, which is what happened before. */
textarea.compose {
  min-height: 300px; font: 16px/1.6 var(--mono); resize: vertical;
  field-sizing: content; max-height: 70vh;
}

/* Buttons follow Carbon's Button exactly: square, IBM Plex Sans SemiBold, no uppercase
   transform, a solid fill for the primary action. */
button, .btn {
  font-family: var(--sans-semi); font-weight: 600; font-size: 15px; letter-spacing: 0;
  text-transform: none; padding: 11px 24px; border-radius: var(--radius-pill);
  border: 1.5px solid var(--field-line); background: #fff; color: var(--ink);
  cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 8px;
  line-height: 1.25;
}
button:hover, .btn:hover { border-color: var(--accent); color: var(--accent); }
/* The button the owner is meant to press, often on a phone in bright sunlight: a solid fill in
   the brand colour with white text, exactly Carbon's own primary button treatment. */
button.primary, .btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
button.primary:hover, .btn.primary:hover {
  background: color-mix(in srgb, var(--accent) 85%, black);
  border-color: color-mix(in srgb, var(--accent) 85%, black);
  color: var(--accent-ink);
}
button.go, .btn.go { background: var(--success); border-color: var(--success); color: #fff; }
button.go:hover, .btn.go:hover {
  background: color-mix(in srgb, var(--success) 82%, black);
  border-color: color-mix(in srgb, var(--success) 82%, black); color: #fff;
}
/* Destructive actions looked exactly like secondary ones: same border, same weight, only the word
   differed. The border is now a real 3:1 boundary (1.4.11) and reads as a different class of thing. */
button.danger {
  color: var(--danger); border-color: var(--danger); background: #fff;
}
button.danger:hover { border-color: #a2181f; color: #a2181f; background: var(--danger-bg); }
button:disabled, .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
/* A form mid-submit: the label has already changed, this stops a second press landing. */
form[aria-busy="true"] button { pointer-events: none; opacity: .55; }

.actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 18px; }
.actions .spacer { margin-left: auto; }
/* A row holding a labelled field: its buttons line up with the field, not with the label above it. */
.actions.bottom { align-items: flex-end; }
/* margin-left:auto only separates within one flex line. Once the buttons wrap on a phone the
   destructive one ends up 10px under "Save for later", so the spacer becomes a real line break. */
@media (max-width: 760px) {
  .actions .spacer, .decide-actions .spacer { flex-basis: 100%; height: 0; margin-left: 0; }
  .actions > button, .actions > .btn { width: 100%; justify-content: center; }
}

/* --- the i-icon and its popover -----------------------------------------
   Shown on hover for a mouse, and on focus for a keyboard or a tap, which is why the trigger is a
   real button rather than a styled span. */

.help { position: relative; display: inline-block; margin-left: 7px; vertical-align: middle; }
/* The disc still reads as 19px, but the button itself is 24x24 so it meets 2.5.8 Target Size.
   A circular icon badge, kept round like the logo disc and the step markers below.
   cursor was `help`, which tells a mouse user this is not clickable; it is a real button. */
.help-icon {
  width: 24px; height: 24px; padding: 0; border: 0; border-radius: 50%;
  background: none; color: var(--ink-muted);
  font: 700 11.5px/1 var(--sans); letter-spacing: 0; text-transform: none;
  display: grid; place-items: center; cursor: pointer;
}
.help-icon::before {
  content: "i"; width: 19px; height: 19px; border-radius: 50%;
  border: 1.5px solid var(--sand-line); background: #fff;
  display: grid; place-items: center;
}
.help-icon:hover::before, .help:focus-within .help-icon::before {
  border-color: var(--accent); color: var(--accent); background: #fff;
}
.help-text {
  position: absolute; z-index: 30; left: -6px; top: 26px;
  /* Was display:none, which drops the text out of the accessibility tree, so the
     aria-describedby on the button resolved to nothing whenever it was not focused and a screen
     reader in browse mode could never reach any of the help texts. Clipped instead: present for
     assistive technology at all times, painted only on hover or focus. */
  visibility: hidden; opacity: 0;
  width: max-content; max-width: min(340px, 74vw);
  background: var(--panel); color: var(--ink-soft);
  border: 1.5px solid var(--sand-line); border-radius: var(--radius);
  box-shadow: 0 2px 4px rgba(22, 22, 22, .08), 0 12px 28px -12px rgba(22, 22, 22, .32);
  padding: 11px 13px; font: 400 14.5px/1.5 var(--sans); text-transform: none; letter-spacing: 0;
  white-space: normal;
}
/* 1.4.13 requires the popover to be hoverable. The gap between the 24px icon and the panel used to
   be dead space, so the pointer lost :hover on the way down and the panel could never be reached
   to read a long explanation. This bridges the full width of the panel. */
.help-text::after {
  content: ""; position: absolute; top: -8px; left: 0; right: 0; height: 8px;
}
.help-text::before {           /* small pointer up towards the icon */
  content: ""; position: absolute; top: -6px; left: 9px; width: 10px; height: 10px;
  background: var(--panel); border-left: 1.5px solid var(--sand-line);
  border-top: 1.5px solid var(--sand-line); transform: rotate(45deg);
}
.help:hover .help-text, .help:focus-within .help-text, .help-text:hover {
  visibility: visible; opacity: 1;
  /* ui.js measures the open panel and sets this when it would cross the edge of the window. */
  translate: var(--help-shift, 0px) 0;
}
/* ...and the pointer moves back the other way, so it still points at the icon. */
.help-text::before { translate: calc(-1 * var(--help-shift, 0px)) 0; }
/* Esc closes it (1.4.13 dismissable); the script sets this on the wrapper. */
.help.dismissed .help-text { visibility: hidden; opacity: 0; }
/* 1.4.10 Reflow: hidden is not the same as gone. A closed panel kept its full 340px, laid out
   beside its icon, and in a right-hand column that ran past the window and /settings scrolled
   sideways between about 820 and 1000px. Closed, it now has no size at all; the text is still
   there for aria-describedby. */
.help:not(:hover):not(:focus-within) .help-text, .help.dismissed .help-text {
  width: 0; height: 0; padding: 0; border-width: 0; overflow: hidden; translate: none;
}
/* Which way it opens by default. Only a first guess: whether it fits depends on where the icon
   lands at the current width, which CSS cannot see, so ui.js nudges the open panel back inside. */
.grid-2 > *:last-child .help-text, .row > *:last-child .help-text { left: auto; right: -6px; }
.grid-2 > *:last-child .help-text::before,
.row > *:last-child .help-text::before { left: auto; right: 9px; }
@media (max-width: 620px) {
  .help-text { left: auto; right: -6px; max-width: 78vw; }
  .help-text::before { left: auto; right: 9px; }
}

/* --- wizard -------------------------------------------------------------- */

/* row-gap only matters once this wraps (the step count has already outgrown one row width
   twice -- 8 steps, now 9 -- and will likely again) -- harmless whitespace above that,
   breathing room between rows if it ever does. */
.steps { display: flex; gap: 0; margin: 4px 0 26px; list-style: none; padding: 0;
  flex-wrap: wrap; row-gap: 10px; }
.steps li {
  display: flex; align-items: center; gap: 5px; padding: 8px 5px 8px 0;
  font-family: var(--sans-semi); font-weight: 600; font-size: 11px;
  letter-spacing: .02em; text-transform: uppercase; color: var(--ink-muted);
}
/* A circular step marker, matching Carbon's own ProgressIndicator dots. */
.steps li .dot {
  width: 19px; height: 19px; flex: none; border-radius: 99px; display: grid; place-items: center;
  border: 1.5px solid var(--sand-line); background: #fff; font-size: 10px; letter-spacing: 0;
}
.steps li.done .dot { background: var(--success); border-color: var(--success); color: #fff; }
.steps li.on .dot { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.steps li.on { color: var(--ink); }
.steps li + li::before {
  content: ""; width: 6px; height: 2px; background: var(--sand-line); margin-right: 5px;
}

/* --- progress ------------------------------------------------------------ */

.track {
  height: 8px; background: var(--gray-20); overflow: hidden;
  border: 1px solid var(--field-line);
}
.track > i {
  display: block; height: 100%; width: 0;
  background: var(--accent);
  transition: width .4s ease;
}
/* A job that failed keeps the same warning colour used everywhere else that means "needs
   attention" (.pill.warn, .card.decide.urgent), rather than the colour that means success. */
.track.failed > i { background: var(--warning); }
.job-step { font-size: 15px; color: var(--ink-soft); margin-top: 11px; }
.job-log {
  margin-top: 12px; max-height: 190px; overflow: auto; font: 13px/1.7 var(--mono);
  color: var(--ink-soft); background: var(--gray-10); border: 1px solid var(--sand-line);
  border-radius: var(--radius); padding: 11px 13px; white-space: pre-wrap;
}

/* --- messages ------------------------------------------------------------ */

.note {
  border-radius: var(--radius); padding: 12px 15px; font-size: 15.5px; margin-bottom: 16px;
  border: 1.5px solid var(--sand-line); background: var(--gray-10);
}
.note.ok { background: var(--success-bg); border-color: var(--success); color: #0e4420; }
.note.bad { background: var(--danger-bg); border-color: var(--danger); color: #921118; }
.note.info { background: var(--info-bg); border-color: var(--accent); color: #002d9c; }
/* Something to look at before sending that is not an error: the guest asked more than the
   draft could take on. Same orange as .pill.warn. */
/* What an import found: at the top of the Knowledge page after it ran, and grouped by card in
   the setup wizard. */
.import-results { margin-bottom: 30px; }
.import-summary a { margin-left: 4px; }
.job-log-more { margin-top: 8px; font-size: 14px; color: var(--ink-soft); }
.job-log-more summary { cursor: pointer; }
.decide-group .group-count { margin-left: 8px; vertical-align: middle; }
.decide-group .decide-item + .decide-item { border-top: 1px solid var(--sand-line); margin-top: 18px; padding-top: 16px; }
/* The earlier messages of a conversation on a draft's page, folded by default. */
.thread summary { cursor: pointer; }
.thread summary h2 { display: inline; }
.thread-message { margin-top: 14px; }
.thread-who { font-size: 13.5px; color: var(--ink-soft); margin-bottom: 4px; }
.thread-message.us .quoted { border-left: 3px solid var(--accent); }
.note.warn { background: var(--warning-bg); border-color: var(--warning); color: #8a3800; }
.note ul.extra-questions { margin: 8px 0 0; padding-left: 20px; }
.note ul.extra-questions li { margin: 2px 0; overflow-wrap: anywhere; }
.note code { background: rgba(22, 22, 22, .08); padding: 1px 5px; }

.pill {
  font-family: var(--sans-semi); font-weight: 600; font-size: 12.5px; letter-spacing: .04em;
  padding: 5px 11px; border-radius: var(--radius); border: 1.5px solid var(--sand-line);
  color: var(--ink-soft); background: #fff; white-space: nowrap;
  /* Pills hold text we do not control: a page title off the owner's website, a guest's name. One
     long enough to exceed the card used to run off the edge of it. Truncate instead, with the full
     text on the element's title attribute. */
  display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  vertical-align: middle;
}

/* A row of pills. Needed because a Jinja loop emits </span><span> with no whitespace between them,
   and inline elements only wrap at whitespace, so without this they never wrap at all. */
.pill-row {
  display: flex; flex-wrap: wrap; gap: 7px; align-items: center; margin: 0;
  min-width: 0;
}
.pill-row .pill { max-width: min(100%, 34ch); }

/* "and 19 more", which opens the rest as a plain list rather than 19 more pills. */
.pill-more { position: relative; }
.pill-more > summary { cursor: pointer; list-style: none; }
.pill-more > summary::-webkit-details-marker { display: none; }
.pill-more[open] > summary { border-color: var(--accent); }
.pill-more > ul {
  margin: 8px 0 0; padding: 10px 14px 10px 28px; background: #fff;
  border: 1px solid var(--sand-line); border-radius: var(--radius); font-size: 14px;
  max-height: 240px; overflow: auto;
}
.pill-more > ul li { margin-bottom: 3px; }
.pill.good { color: #0e4420; border-color: var(--success); background: var(--success-bg); }
.pill.warn { color: #921118; border-color: var(--warning); background: var(--warning-bg); }
.pill.lang { text-transform: uppercase; }
.pill.rec { color: #002d9c; border-color: var(--accent); background: var(--info-bg); }

/* One row per candidate model in the setup wizard's model picker: the whole row is the label, so
   clicking anywhere (not just the radio dot) selects it. Three columns -- the radio, what the
   model is, its status -- each centred on the row, so the radio, the text and the status line up
   whatever the description's length. On a phone the status moves under the text rather than
   being squeezed into "do…". */
.model-option {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 6px 14px;
  align-items: center; padding: 14px 12px; margin: 0 -12px;
  border-bottom: 1px solid var(--sand-line); border-radius: var(--radius);
  cursor: pointer; font: 400 15px/1.45 var(--sans); letter-spacing: normal;
}
.model-option:last-child { border-bottom: none; }
.model-option:has(input:checked) { background: var(--info-bg); }
.model-option input[type=radio] { margin: 0; }
.model-option-title { font-family: var(--sans-semi); font-weight: 600; }
.model-option-name { font: 13px/1.4 var(--mono); color: var(--ink-muted); margin-left: 6px; }
.model-option-desc { display: block; font-size: 14px; color: var(--ink-soft); margin-top: 3px; }
.model-option-meta { display: block; font-size: 13px; color: var(--ink-muted); margin-top: 3px; }
.model-option .pill-row { justify-content: flex-end; }
.model-memory { margin: 0 0 12px; color: var(--ink-soft); font-size: 14.5px; }
@media (max-width: 620px) {
  .model-option { grid-template-columns: auto minmax(0, 1fr); }
  .model-option .pill-row { grid-column: 2; justify-content: flex-start; }
}

/* Models on this machine: name, size, then what it is used for or a way to delete it, in three
   columns centred on each row, so a status pill and a Delete button sit on the same line as the
   name rather than one of them lower. */
.disk-card { margin-top: 32px; }

/* Settings > Advanced, Cloud drafting: one fieldset per provider, only the chosen one shown. */
.cloud-provider { border: 0; border-top: 1px solid var(--sand-line); margin: 6px 0 0; padding: 12px 0 0;
  min-width: 0; }
.cloud-provider legend { font-family: var(--sans-semi); font-weight: 600; font-size: 14.5px; padding: 0 8px 0 0; }
.cloud-option { margin-top: 18px; }
.ik-models-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; margin-top: 2px; }
.ik-models-row .field-hint { margin-top: 0; }
.ik-models-row button { padding: 4px 8px; font-size: 13.5px; }

/* Signing in, and the accounts an admin manages (butler_li/accounts.py). */
/* The signed-in account: a round badge with the initials, and a small menu under it. */
.account-menu { position: relative; flex: none; margin-left: 4px; }
@media (max-width: 900px) { .account-menu { margin-left: auto; } }
.account-badge {
  list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%; background: var(--gray-10);
  border: 1.5px solid var(--sand-line); font-family: var(--sans-semi); font-weight: 600;
  font-size: 14px; letter-spacing: .03em; color: var(--ink); user-select: none;
}
.account-badge::-webkit-details-marker { display: none; }
.account-badge:hover, .account-menu[open] .account-badge { border-color: var(--accent); }
.account-pop {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 20; min-width: 15rem;
  background: var(--white); border: 1px solid var(--sand-line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow); padding: 14px 16px; display: flex; flex-direction: column; gap: 10px;
}
.account-pop-who { margin: 0; display: flex; flex-direction: column; gap: 2px; font-size: 14px;
  color: var(--ink-soft); }
.account-pop-who strong { color: var(--ink); font-size: 15px; overflow-wrap: anywhere; }
.account-pop a { font-size: 14.5px; }
.account-pop form { margin: 0; }
.account-pop button { width: 100%; }
.login-wrap { display: flex; justify-content: center; padding-top: 6vh; }
.login-card { width: 100%; max-width: 26rem; padding: 26px 28px; }
.login-card h1 { margin: 0 0 6px; font-size: 26px; }
.login-card .actions { margin-top: 6px; }
.login-card .actions button { width: 100%; }
.login-help { margin: 14px 0 0; font-size: 14px; }
.accounts-card { margin-top: 32px; }
.accounts-intro { margin: -4px 0 12px; font-size: 14px; }
/* The rows share their columns (subgrid), each as wide as its widest entry in any row, so roles
   and dates line up down the list and a long pill such as "Erste Anmeldung ausstehend" stays on
   one line rather than wrapping under the role in a fixed column. */
.account-list {
  display: grid; column-gap: 16px; border-bottom: 1px solid var(--sand-line);
  grid-template-columns: minmax(8rem, 1fr) minmax(0, max-content) minmax(0, max-content) minmax(7.5rem, max-content);
}
.account-row {
  grid-column: 1 / -1; display: grid; grid-template-columns: subgrid;
  /* At least as tall as the Manage button, so the row does not shift when it opens (below). */
  grid-template-rows: minmax(35px, auto);
  row-gap: 10px; align-items: center; padding: 11px 0; border-top: 1px solid var(--sand-line);
  position: relative;
}
.account-row.off .account-who strong { color: var(--ink-soft); }
.account-who { display: flex; flex-wrap: wrap; gap: 2px 8px; align-items: baseline; min-width: 0; }
.account-who strong { font-family: var(--sans-semi); font-weight: 600; font-size: 16px; overflow-wrap: anywhere; }
.account-who .muted { font-size: 14.5px; }
.account-state { display: flex; gap: 6px; flex-wrap: wrap; min-width: 0; }
.account-last { font-size: 14px; }
.account-actions { justify-self: end; }
.account-manage > summary { list-style: none; cursor: pointer; padding: 6px 14px; font-size: 14px;
  line-height: 20px; display: block; width: fit-content; margin-left: auto; white-space: nowrap; }
.account-manage > summary::-webkit-details-marker { display: none; }
.account-manage[open] > summary { border-color: var(--accent); color: var(--accent); }
/* Opened, the forms take the whole width under the row, and the button stays where it was rather
   than dropping onto a line of its own above them, which left a gap and made the forms look as if
   they belonged to the next account. */
.account-row:has(.account-manage[open]) .account-actions { grid-column: 1 / -1; justify-self: stretch; }
.account-row:has(.account-manage[open]) .account-manage > summary {
  position: absolute; top: 11px; right: 0;
}
.account-row:has(.account-manage[open]) { padding-bottom: 16px; }
.account-forms {
  display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 12px 20px;
  align-items: center; padding: 16px 18px;
  background: var(--gray-10); border: 1px solid var(--sand-line);
}
.account-forms .inline-form {
  grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; align-items: center; margin: 0;
}
/* A form with no label of its own (deactivate, reactivate, delete) starts at the left edge. */
.account-forms .inline-controls:only-child { grid-column: 1 / -1; }
.account-forms .account-danger { padding-top: 12px; border-top: 1px solid var(--sand-line); }
.account-forms .inline-form > label, .account-forms .inline-legend {
  margin: 0; font-family: var(--sans-semi); font-weight: 600; font-size: 14.5px;
}
.inline-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; min-width: 0; }
/* One height for every control in the panel: the field, its button and the radio row. */
.account-forms button { padding: 0 18px; height: 38px; font-size: 14.5px; }
.account-forms input[type=password] {
  width: 15rem; max-width: 100%; height: 38px; padding: 0 12px; margin: 0;
}
.account-forms input[type=radio] { width: auto; margin: 0; }
.account-forms .muted { font-size: 14px; }
.accounts-subhead { margin: 26px 0 4px; font-size: 16px; }
/* Adding an account: the three fields keep their own heights, so labels stay level at the top. */
.account-add .grid-2, .account-add .grid-3 { align-items: start; }
/* A hint under its field rather than inside the label: see .account-add and _infomaniak_model.html. */
.field-hint { display: block; margin-top: 6px; font-size: 13.5px; color: var(--ink-muted); }
.role-choice { border: 0; margin: 2px 0 14px; padding: 0; min-width: 0; }
.role-choice legend { font-family: var(--sans-semi); font-weight: 600; font-size: 15px;
  padding: 0; margin-bottom: 8px; }
.role-options { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.role-option {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 10px; align-items: start;
  padding: 13px 14px; border: 1.5px solid var(--sand-line); border-radius: var(--radius);
  cursor: pointer; font: 400 15px/1.45 var(--sans); letter-spacing: normal;
}
.role-option:hover { border-color: var(--field-line); }
.role-option:has(input:checked) { border-color: var(--accent); background: var(--info-bg); }
.role-option input[type=radio] { margin: 3px 0 0; }
.role-option-title { display: block; font-family: var(--sans-semi); font-weight: 600; }
.role-option-desc { display: block; font-size: 14px; color: var(--ink-soft); margin-top: 2px; }
.role-inline { display: inline-flex; gap: 14px; align-items: center; }
.role-inline label { display: inline-flex; gap: 6px; align-items: center; margin: 0; font-size: 15px;
  font-family: var(--sans); font-weight: 400; cursor: pointer; }
.inline-legend { font-family: var(--sans-semi); font-weight: 600; font-size: 14px; }
@media (max-width: 620px) { .role-options { grid-template-columns: 1fr; } }
@media (max-width: 760px) {
  /* On a phone each row stacks: the name with Manage beside it, then role and state, then the
     date; opened, the forms come after all three. */
  .account-list { display: block; }
  .account-row { grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; row-gap: 6px; }
  .account-who { grid-column: 1; grid-row: 1; }
  .account-actions { grid-column: 2; grid-row: 1; }
  .account-state, .account-last { grid-column: 1 / -1; }
  .account-row:has(.account-manage[open]) .account-actions { grid-row: auto; margin-top: 6px; }
}
@media (max-width: 620px) {
  /* Labels over their controls rather than beside them. */
  .account-forms { grid-template-columns: minmax(0, 1fr); row-gap: 8px; padding: 14px; }
  .account-forms .inline-form { row-gap: 6px; }
  .account-forms .inline-form + .inline-form { margin-top: 6px; }
  /* min-width: a field's automatic minimum comes from its size attribute, about 14rem, which kept
     Set off the field's line even where both fit. */
  .account-forms input[type=password] { flex: 1 1 10rem; width: auto; min-width: 0; }
}

/* Settings > Costs: what the models were asked to do, in tokens and time. */
.usage-card { margin-top: 32px; }
.usage-head { flex-wrap: wrap; row-gap: 10px; }
.usage-periods { display: flex; flex-wrap: wrap; gap: 6px; }
.usage-periods .btn { padding: 6px 12px; font-size: 14px; }
.usage-intro { margin: -4px 0 16px; font-size: 14px; }
.usage-stats { display: flex; flex-wrap: wrap; gap: 14px 34px; margin: 0 0 14px; }
.usage-stats .num { font-size: 26px; font-variant-numeric: tabular-nums; }
.usage-reply { margin: 0 0 6px; }
.usage-subhead { margin: 22px 0 6px; font-size: 16px; }
/* Seven columns do not fit a phone; the table scrolls inside its card, never the page. */
.usage-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Fixed widths, so the task table and the job table under it line up column for column. */
.usage-table { width: 100%; min-width: 620px; border-collapse: collapse; font-size: 14.5px;
  table-layout: fixed; }
.usage-table .c-name { width: 31%; }
.usage-table .c-calls { width: 9%; }
.usage-table .c-read, .usage-table .c-written { width: 12%; }
.usage-table .c-time { width: 13%; }
.usage-table .c-notes { width: 23%; }
.usage-table th, .usage-table td {
  padding: 7px 10px 7px 0; border-bottom: 1px solid var(--sand-line); text-align: left;
  vertical-align: top; white-space: nowrap;
}
.usage-table thead th { font-family: var(--sans-semi); font-weight: 600; color: var(--ink-muted); }
.usage-table tbody th { font-weight: 400; white-space: normal; }
.usage-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.usage-table tr:last-child td, .usage-table tr:last-child th { border-bottom: none; }
.usage-table .num + td, .usage-table thead th:last-child { padding-left: 14px; }
.usage-notes { color: var(--ink-soft); white-space: normal; }
.usage-downloads { margin: 18px 0 4px; display: flex; flex-wrap: wrap; gap: 6px 10px; }
.usage-kept { margin: 0; font-size: 13.5px; }

/* Settings > Costs. The period buttons sit above the cards; the cards stack. */
.cost-periods { margin: 0 0 18px; }
.cost-stack { display: grid; gap: 18px; }
.cost-stack > .card { margin: 0; min-width: 0; }
.cost-figures { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px 28px; }
@media (max-width: 900px) { .cost-figures { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 460px) { .cost-figures { grid-template-columns: minmax(0, 1fr); } }
.cost-figures .num { font-size: 28px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.cost-basis { font-size: 13.5px; color: var(--ink-soft); margin-top: 4px; }
.cost-note { margin: 14px 0 0; font-size: 14px; color: var(--ink-soft); }
.cost-summary .note { margin: 16px 0 0; }

/* Settings > Data & security: a short two-column table of what is kept, then one block per
   outside service, its pill beside the heading and wrapping under it on a phone. */
.data-table { border-collapse: collapse; font-size: 14.5px; margin: 4px 0 0; }
.data-table th, .data-table td { padding: 6px 0; border-bottom: 1px solid var(--sand-line);
  text-align: left; font-weight: 400; }
.data-table td.num { padding-left: 28px; text-align: right; font-variant-numeric: tabular-nums; }
.data-table tr:last-child th, .data-table tr:last-child td { border-bottom: none; }
.data-services { list-style: none; margin: 0; padding: 0; }
.data-services li { padding: 12px 0; border-top: 1px solid var(--sand-line); }
.data-service-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px; }
.data-service-head h3 { margin: 0; font-size: 16px; }
.data-services p { margin: 6px 0 0; overflow-wrap: anywhere; }
.data-points { margin: 0; padding-left: 20px; }
.data-points li { margin: 0 0 8px; }
/* One guest's data: the address field and its button on one line where there is room. */
.guest-find { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0 12px; }
.guest-find .field { flex: 1 1 260px; margin-bottom: 0; }
.guest-list { margin: 0; padding-left: 20px; }
.guest-list li { margin: 0 0 4px; overflow-wrap: anywhere; }
/* The box that confirms a deletion sits at the start of its sentence, which wraps beside it. */
.guest-confirm { display: flex; align-items: flex-start; gap: 8px; font: inherit; overflow-wrap: anywhere; }
.guest-confirm input { flex: none; margin-top: 4px; }

/* Bars of one height scale, each split by what its money went to. A day with nothing is an
   empty slot, so the days stay evenly spaced. */
.cost-chart { display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: 160px auto;
  column-gap: 10px; }
.cost-chart-scale { display: flex; flex-direction: column; justify-content: space-between;
  font-size: 12.5px; color: var(--ink-muted); text-align: right; font-variant-numeric: tabular-nums; }
.cost-chart-bars { display: flex; align-items: flex-end; gap: 2px; height: 160px;
  border-bottom: 1px solid var(--field-line); }
.cost-chart-bars.many { gap: 1px; }   /* three months of days still fit a phone */
.cost-bar { flex: 1 1 0; min-width: 0; height: 100%; display: flex; align-items: flex-end; }
.cost-bar-fill { width: 100%; display: flex; flex-direction: column-reverse; min-height: 1px; }
.cost-bar-fill > span { display: block; width: 100%; }
.cost-chart-axis { grid-column: 2; display: flex; justify-content: space-between; margin-top: 4px;
  font-size: 12.5px; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.cost-legend { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-wrap: wrap;
  gap: 6px 18px; font-size: 14px; }
.cost-legend li { display: flex; align-items: center; gap: 6px; }
.cost-swatch { display: inline-block; width: 10px; height: 10px; margin-right: 6px; flex: none; }
.cost-legend .cost-swatch { margin-right: 0; }
.fn-drafting { background: var(--fn-drafting); }
.fn-translation { background: var(--fn-translation); }
.fn-knowledge { background: var(--fn-knowledge); }
.fn-indexing { background: var(--fn-indexing); }
.fn-other { background: var(--fn-other); }
@media (forced-colors: active) {
  /* The colours are the information here: keep them, and an outline for the empty slots. */
  .cost-bar-fill > span, .cost-swatch { forced-color-adjust: none; }
}

/* The cost tables share the usage table's look, with their own columns. */
.cost-table .c-name { width: 34%; }
.cost-table .c-cost { width: 11%; }
.cost-table .c-share { width: 22%; }
.cost-table .c-kind { width: 15%; }
.cost-table .c-price { width: 16%; }
.cost-table .c-use { width: 34%; }
.cost-hint { display: block; font-size: 13px; color: var(--ink-muted); }
.cost-model { font-family: var(--mono); font-size: 13.5px; overflow-wrap: anywhere; }
.cost-share { display: flex; align-items: center; gap: 8px; font-variant-numeric: tabular-nums; }
.cost-share-bar { flex: 1 1 auto; max-width: 110px; height: 8px; background: var(--cream); }
.cost-share-bar > span { display: block; height: 100%; background: var(--ink-soft); }
.cost-prices .pill { margin-right: 4px; }
.cost-prices td, .cost-prices tbody th { vertical-align: middle; }
.cost-prices .price-input { width: 6.5em; max-width: 100%; text-align: right;
  font-variant-numeric: tabular-nums; padding: 5px 8px; }
.cost-own { color: var(--ink-soft); font-style: italic; }
.cost-compare .c-name { width: 46%; }
.cost-compare .c-cost { width: 18%; }
.cost-compare .c-diff { width: 36%; }
.disk-summary { margin: -4px 0 6px; color: var(--ink-soft); font-size: 14px; }
.disk-model {
  /* A fixed last column, so the sizes stay in one column when a row turns into its question. */
  display: grid; grid-template-columns: minmax(0, 1fr) 5.5em 17rem; gap: 8px 16px;
  align-items: center; min-height: 54px; padding: 8px 0; border-top: 1px solid var(--sand-line);
}
.disk-model:first-child { border-top: none; }
.disk-model code { font-size: 14px; }
.disk-model-size { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.disk-model-action { justify-self: end; }
.disk-model-action form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  justify-content: flex-end; margin: 0; }
.disk-model-action .disk-question { font-size: 14px; color: var(--ink-soft); }
@media (max-width: 620px) {
  .disk-model { grid-template-columns: minmax(0, 1fr) auto; }
  .disk-model-action { grid-column: 1 / -1; justify-self: start; }
}

/* --- queue --------------------------------------------------------------- */

a.qrow { display: block; text-decoration: none; color: inherit; }
a.qrow .card { margin-bottom: 10px; padding: 16px 19px; transition: border-color .15s; }
a.qrow:hover .card { border-color: var(--accent); }
.qmeta {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  font-size: 14px; color: var(--ink-muted); margin-bottom: 6px;
}
/* An <h2> so the queue has a real heading outline and can be navigated by heading, styled back
   down to the size it always looked. */
.qsubj {
  font-family: var(--sans-semi); font-weight: 600; font-size: 19px; line-height: 1.3;
  margin: 0 0 5px; color: var(--ink);
}
.qsnip {
  color: var(--ink-soft); font-size: 15px; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}

pre.quoted {
  white-space: pre-wrap; word-break: break-word; margin: 0; padding: 15px 17px;
  background: var(--gray-10); border: 1px solid var(--sand-line);
  border-left: 4px solid var(--gray-50); border-radius: var(--radius);
  font: 15.5px/1.6 var(--sans); max-height: 340px; overflow: auto;
}
pre.quoted.mine { border-left-color: var(--accent); background: var(--info-bg); }
/* Spam's Set aside list: enough to recognise a newsletter, the rest scrolls in its box. */
pre.quoted.spam-preview { max-height: 220px; }

/* A proposed answer read off the website. Deliberately not styled like an accepted fact: it stays
   a suggestion until the owner presses the button. */
.answer-suggest {
  margin-top: 10px; padding: 12px 15px; border-radius: var(--radius);
  background: var(--info-bg); border: 1px dashed var(--accent); color: #002d9c;
  font: 16px/1.55 var(--sans);
}
.answer-suggest a { margin-left: 8px; }

.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
.pair pre.quoted { max-height: 520px; }
@media (max-width: 800px) { .pair { grid-template-columns: 1fr; } }

.empty { text-align: center; padding: 48px 20px; color: var(--ink-soft); }
.empty svg { opacity: .5; margin-bottom: 14px; color: var(--accent); }
.empty h2 { font-size: 21px; margin: 0 0 8px; }
.empty p { margin: 0 auto 6px; max-width: 46ch; }
.empty .actions { justify-content: center; margin-top: 20px; }

/* --- the decisions inbox -------------------------------------------------
   One list, one row per decision, one primary action per row. The visual job is to make the row you
   should act on first obvious without reading all of them, and to make the list look finishable. */

.muted { color: var(--ink-soft); }
/* Replaces the style="color:var(--ink)" repeated on four templates. */
.ink { color: var(--ink); }
/* Replaces style="display:inline" on nine forms. */
form.inline { display: inline; }
/* The folder list on the connection step. Two fixed columns of server folder names were
   unreadable at 360px; this drops to one column when there is no room for two. */
.folder-list {
  columns: 2 16em; margin: 0; padding-left: 20px; font-size: 14px;
}

.inbox-head { display: flex; align-items: baseline; gap: 12px; margin: 26px 0 12px; flex-wrap: wrap; }
.inbox-head h2 { margin: 0; }

.card.decide { border-left: 4px solid var(--sand-line); }
/* A gap on a card that is switched on is putting brackets into live replies, so it earns the one
   loud colour in the palette. Nothing else in the list does. */
.card.decide.urgent { border-left-color: var(--warning); background: #fffcfa; }

.decide-title {
  margin: 8px 0 2px; font-size: 19px; line-height: 1.3;
  overflow-wrap: anywhere;           /* a placeholder can be one long unbroken string */
}
.decide-context { margin: 0 0 12px; color: var(--ink-soft); font-size: 14.5px; }
.decide-form { margin-top: 12px; }
.decide-form .field:last-of-type { margin-bottom: 12px; }
.decide-actions { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.decide-actions .spacer { margin-left: auto; }
.decide-actions .field { margin-bottom: 0; }
.decide-actions .field.narrow { flex: 0 1 230px; }
.decide-note { margin: 12px 0 0; color: var(--ink-muted); font-size: 13.5px; max-width: 68ch; }
button.quiet {
  border-color: transparent; background: none; color: var(--ink-muted); padding: 10px 14px;
}
button.quiet:hover { border-color: var(--sand-line); background: var(--gray-10); }

.decide-more { margin-top: 10px; font-size: 14px; }
/* padding takes the disclosure control from ~22px to 30px tall, clearing the 24px in 2.5.8. */
.decide-more > summary { cursor: pointer; color: var(--ink-muted); padding: 4px 0; }
.decide-more ul { margin: 6px 0 0; color: var(--ink-soft); }
.decide-more p { margin: 6px 0 4px; overflow-wrap: anywhere; }
.decide-more blockquote {
  margin: 0; padding: 8px 12px; background: #fff;
  border-left: 3px solid var(--accent); color: var(--ink-soft); font-size: 14px;
}

.empty-inbox { display: flex; gap: 16px; align-items: flex-start; }
.empty-inbox svg { color: var(--accent); flex: 0 0 auto; margin-top: 2px; }

/* --- collapsible blocks -------------------------------------------------
   The store of cards used to be one page you scrolled for ten screens. Each card is now a row that
   opens, so the list is readable at a glance and the editor is one click away. */

.block, .card.fact-card { margin-bottom: 12px; }
.block {
  background: var(--panel); border: 1px solid var(--sand-line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.block > summary, .card.fact-card > summary {
  cursor: pointer; padding: 16px 20px; display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap; list-style: none; font-family: var(--sans-semi); font-weight: 600;
  border-radius: var(--radius-lg);
}
.card.fact-card { padding: 0; }
.card.fact-card > summary { padding: 15px 20px; }
.block > summary::-webkit-details-marker,
.card.fact-card > summary::-webkit-details-marker { display: none; }
.block > summary::before, .card.fact-card > summary::before {
  content: ""; flex: 0 0 auto; width: 8px; height: 8px; margin-right: 2px;
  border-right: 2px solid var(--ink-muted); border-bottom: 2px solid var(--ink-muted);
  transform: rotate(-45deg); transition: transform .15s ease;
}
.block[open] > summary::before, .card.fact-card[open] > summary::before { transform: rotate(45deg); }
.block > summary:focus-visible, .card.fact-card > summary:focus-visible {
  outline: 3px solid var(--accent); outline-offset: 2px;
}
.block-body { padding: 4px 20px 20px; }
.block-body > p:first-child { margin-top: 0; color: var(--ink-soft); }

/* An <h3> inside the <summary> so the card list appears in the heading outline. Heading content
   inside summary is valid and is the documented pattern. */
.fact-name { font-size: 17px; margin: 0; font-family: var(--sans-semi); font-weight: 600; }

/* The fact-card editors. Monospace is the point of them, 15px was not: below 16px iOS Safari
   zooms the page on focus and does not zoom back out. Replaces an inline style on two textareas. */
textarea.mono-edit {
  min-height: 130px; font: 16px/1.6 var(--mono); field-sizing: content; max-height: 60vh;
}
.fact-preview {
  color: var(--ink-muted); font-size: 13.5px; font-family: var(--sans); font-weight: 400;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1 1 120px; min-width: 0;
}
@media (max-width: 700px) { .fact-preview { display: none; } }

table.kv { width: 100%; border-collapse: collapse; font-size: 15.5px; }
table.kv td, table.kv th {
  padding: 8px 0; border-bottom: 1px solid var(--sand-line); vertical-align: top;
  overflow-wrap: anywhere; text-align: left;
}
table.kv th { font-family: var(--sans-semi); font-weight: 600; }
table.kv td:first-child, table.kv th:first-child { color: var(--ink-muted); width: 34%; }
table.kv th:first-child { color: var(--ink); }
table.kv tr:last-child td { border-bottom: none; }
/* A cell cannot shrink below its longest unbreakable token, and these hold things like
   http://192.168.1.20:11434 and INBOX.Sent Messages. At 320px the fixed 34% column forced the
   whole document to scroll sideways, which is a 1.4.10 Reflow failure on the proposal page the
   manual tells the owner to use on her phone. Stack the pairs instead. */
@media (max-width: 620px) {
  table.kv, table.kv tbody, table.kv tr, table.kv td, table.kv th {
    display: block; width: auto;
  }
  table.kv td, table.kv th { border-bottom: none; padding: 1px 0; }
  table.kv td:first-child, table.kv th:first-child { padding-top: 10px; font-size: 14px; }
  table.kv tr { border-bottom: 1px solid var(--sand-line); padding-bottom: 9px; margin-bottom: 3px; }
  table.kv tr:last-child { border-bottom: none; }
  /* Stacked pairs label themselves, so a two-cell header row is just noise repeated once. */
  table.kv thead { display: none; }
}
code { font: 14.5px/1.4 var(--mono); overflow-wrap: anywhere; }

/* One row per availability period. Was a two-column .kv table, wrong tool here: the first
   column held a date range rather than a label, and the second crammed a pill, a note and a
   Remove button together with no breathing room. */
.period-list { display: flex; flex-direction: column; }
.period-row {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 0; border-bottom: 1px solid var(--sand-line);
}
.period-list .period-row:last-child { border-bottom: none; padding-bottom: 6px; }
.period-main { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.period-dates { font-family: var(--sans-semi); font-weight: 600; font-size: 15px; }
.period-note { flex: 1 1 200px; min-width: 0; font-size: 14.5px; }
.period-remove { margin-left: auto; }
.period-remove button { padding: 4px 12px; font-size: 12.5px; }
@media (max-width: 560px) {
  .period-remove { margin-left: 0; flex-basis: 100%; }
}

footer.foot {
  max-width: var(--maxw); margin: 0 auto; padding: 0 22px 44px;
  color: var(--ink-muted); font-size: 13.5px;
}
footer.foot a { color: var(--ink-muted); }
footer.foot .build {
  display: inline-block; margin-left: 10px; padding: 2px 9px;
  background: var(--gray-10); border: 1px solid var(--sand-line);
  font: 12.5px/1.5 var(--mono); color: var(--ink-soft); cursor: help;
}

/* --- visually hidden ------------------------------------------------------
   Text for assistive technology only. Used for "opens in a new tab" and for the headings that
   give the queue and the card list a real outline without changing how they look. */
.vh {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* --- motion ---------------------------------------------------------------
   Nothing here is parallax and nothing flashes, so this is a courtesy rather than a conformance
   fix, but it costs two lines. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* --- signature editor -------------------------------------------------
   The Jodit island in settings.html (frontend/src/components/RichTextEditor.tsx). Jodit brings
   its own toolbar and dialogs (jodit's css is served as signature-editor.css); what is set here is
   only what makes it look like the rest of the app, and how the saved signature is previewed. */
.card.span-all, .grid-2 > .span-all { grid-column: 1 / -1; }

.signature-editor-box .jodit-container:not(.jodit_inline) {
  border: 1px solid var(--field-line); border-radius: 0; background: #fff;
}
.signature-editor-box .jodit-toolbar__box:not(:empty) {
  background: var(--gray-10); border-bottom: 1px solid var(--field-line);
}
.signature-editor-box .jodit-workplace { font: 16px/1.5 var(--sans); }
.signature-editor-box .jodit-wysiwyg { padding: 14px; font: 16px/1.5 var(--sans); }
.signature-editor-box .jodit-wysiwyg p { margin: 0 0 8px; }
.signature-editor-box .jodit-container.jodit-container_focused,
.signature-editor-box .jodit-container:focus-within {
  outline: 2px solid var(--accent); outline-offset: -2px;
}
.signature-editor-box .jodit-placeholder { padding: 14px; }
/* Open questions in a draft or a fact card: the [bracketed text], coloured over the text by the
   CSS Custom Highlight API (frontend/src/components/gapHighlight.ts), so nothing is added to the reply
   itself. The brackets stay, so the colour is never the only way to tell. */
::highlight(mm-gap) { background-color: var(--gap-bg); color: var(--gap-ink); }

/* The link dialog offers a CSS class name. The sanitizer strips class attributes from a
   signature anyway, so the field would only promise something that does not happen. */
.jodit-ui-block:has(> .jodit-ui-block__className) { display: none; }
/* Jodit's dialogs and popups are appended to <body>, above the app's own layers. */
.jodit-popup, .jodit-dialog { z-index: 2000; }

/* The translations sit side by side now that the card is the full width; the one being edited
   takes a whole row. */
.signature-translations { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.signature-translations .signature-translation { margin-top: 0; }
.signature-translations .signature-translation.editing { grid-column: 1 / -1; }
@media (max-width: 760px) { .signature-translations { grid-template-columns: 1fr; } }

.signature-preview {
  border: 1px solid var(--field-line); padding: 14px; min-height: 120px;
  background: #fff; font: 16px/1.5 var(--sans);
}
.signature-preview p { margin: 0 0 8px; }
.signature-preview img { max-width: 100%; height: auto; }
.signature-preview table {
  border-collapse: collapse; margin: 8px 0;
}
.signature-preview td, .signature-preview th {
  border: 1px solid var(--field-line); padding: 6px 10px;
}
.signature-preview th { background: var(--gray-10); }
.signature-translation { margin-top: 12px; }

/* --- print ----------------------------------------------------------------
   pre.quoted caps at 340px and scrolls, so printing a held message to forward to the police, or a
   past exchange for the records, silently cut the body off at the fold and printed nothing after
   it. That is content loss, not a styling preference. */
@media print {
  body { background: #fff; font-size: 11.5pt; }
  .topbar, .actions, .decide-actions, .decide-form, footer.foot .build, .help { display: none; }
  main { max-width: none; padding: 0; }
  .card, .block, .card.fact-card {
    box-shadow: none; border: 1px solid #999; break-inside: avoid;
  }
  pre.quoted, .job-log, .pill-more > ul, .block-body {
    max-height: none !important; overflow: visible !important;
  }
  /* Print what is inside a collapsed disclosure rather than only its summary. */
  details > *:not(summary) { display: block !important; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; word-break: break-all; }
  h1, h2, h3 { break-after: avoid; }
}
