/* ============================================================================
   Foundation.

   One tuned neutral palette, one type scale, one spacing rhythm. Colour is
   reserved for meaning — money that is late, money that arrived, the one
   action that matters on a screen — so it still registers when it appears.
   Chrome is built from hairlines and weight, not from tinted fills.

   Legacy token names (--blue, --ink, --white ...) are kept as aliases so the
   ~27 template directories keep rendering; new work should use the semantic
   names below.
   ========================================================================= */
:root {
  color-scheme: light dark;

  /* Neutral ramp — a faint cool cast, near-zero chroma. */
  --n-0:  #ffffff;
  --n-25: #fcfcfd;
  --n-50: #f7f8f9;
  --n-100:#f0f1f3;
  --n-150:#e8eaed;
  --n-200:#dfe1e6;
  --n-300:#c3c6cf;
  --n-500:#6e727b;
  --n-600:#5b5f68;
  --n-700:#4a4e57;
  --n-900:#1c1e23;

  /* Surfaces */
  --surface:        var(--n-0);      /* cards, tables, menus */
  --surface-sunken: var(--n-50);     /* the page behind them */
  --surface-hover:  var(--n-50);
  --surface-active: var(--n-100);

  /* Text */
  --text:        var(--n-900);
  --text-muted:  var(--n-600);
  --text-subtle: var(--n-500);

  /* Hairlines. Two weights: the default, and one for structural rules. */
  --border:        #e7e8ec;
  --border-strong: #d3d6dd;

  /* Accent. One hue, used for interaction — never as decoration. */
  --accent:        #4a5bd8;
  --accent-hover:  #3d4cc4;
  --accent-text:   #4351c4;
  --accent-text-hover: #35429f;
  --accent-wash:   #eef0fd;
  --accent-border: #c9cef6;

  /* Semantic. Only ever means something. */
  --pos:        #17794b;
  --pos-wash:   #e9f6ef;
  --neg:        #bf2f22;
  --neg-wash:   #fdeeec;
  --warn:       #9a6212;
  --warn-wash:  #fdf3e3;

  /* Spacing. One scale; card padding and grid gutters are drawn from it so
     interior and exterior rhythms agree instead of fighting. */
  --sp-2:  8px;
  --sp-3:  12px;
  --sp-4:  16px;
  --sp-6:  24px;

  /* Geometry — one radius scale, one control height. */
  --r-sm: 5px;
  --r:    7px;
  --r-lg: 10px;
  --h-control: 32px;
  --h-control-sm: 28px;

  /* Depth. Overlays only; flat surfaces stay flat. */
  --shadow-sm: 0 1px 2px rgba(20, 22, 28, .05);
  --shadow-md: 0 4px 12px -2px rgba(20, 22, 28, .10), 0 2px 4px -2px rgba(20, 22, 28, .06);
  --shadow-lg: 0 12px 28px -6px rgba(20, 22, 28, .16), 0 4px 10px -4px rgba(20, 22, 28, .08);

  --ease: 120ms cubic-bezier(.4, 0, .2, 1);

  /* One chevron, masked so it takes the colour of whatever it sits in. */
  --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.2 8 10l4-3.8'/%3E%3C/svg%3E");

  /* Charts. Deliberately NOT the accent: the accent means "you can act on
     this", and a bar is a measured quantity, not a control. Two series kept
     ~3:1 apart in relative luminance so they survive greyscale printing. */
  --chart-in:   #255f5b;
  --chart-out:  #d19b52;
  --chart-grid: #e7e9ec;

  /* Ageing is a severity ramp, not two categories: one hue darkening as the
     debt gets older. It deliberately does NOT reuse the cash-flow pair, which
     means something else entirely 400px further down the same page. */
  --age-0: #b9c2cc;
  --age-1: #d9b26a;
  --age-2: #c8823f;
  --age-3: #b04a34;

  /* --- Legacy aliases ------------------------------------------------- */
  --blue: var(--accent);
  --blue-dark: var(--accent-text);
  --blue-pale: var(--accent-wash);
  --ink: var(--text);
  --muted: var(--text-muted);
  --line: var(--border);
  --bg: var(--surface-sunken);
  --white: var(--surface);
  --green: var(--pos);
  --red: var(--neg);
}

/* Dark is a first-class theme, not an inversion: the ramp is rebuilt so the
   canvas sits below the cards. In dark the surface step alone carries the
   containment and cards drop their border entirely (see `.card` below) —
   carrying both a raised fill and a hairline made every panel announce itself
   twice. Light keeps the hairline, where a white-on-near-white step cannot
   separate on its own.

   Declared twice on purpose — once for the system preference, once for an
   explicit choice — so the toggle wins in both directions. */
:root[data-theme="dark"] {
  --n-0:  #17181c;
  --n-25: #141519;
  --n-50: #101114;
  --n-100:#1e2025;
  --n-150:#24262c;
  --n-200:#2b2e35;
  --n-300:#3a3e47;
  --n-500:#82868f;
  --n-600:#9aa0aa;
  --n-700:#c6cad2;
  --n-900:#eceef2;

  /* The surface step carries the containment; the hairline only confirms it.
     Previously the border sat ~8 L* above the card while the card sat only
     ~4 above the page, so the outline was the loudest thing on the screen. */
  --surface:        #17181c;
  --surface-sunken: #08090c;
  --surface-hover:  #1d1f25;
  --surface-active: #25272e;

  --border:        #212329;
  --border-strong: #2e3138;

  --accent:        #5464dd;
  --accent-hover:  #6070e8;
  --accent-text:   #9aa4f7;
  --accent-text-hover: #b4bbfa;
  --accent-wash:   #1e2143;
  --accent-border: #333a72;

  --pos:       #3fb87c;
  --pos-wash:  #10281d;
  --neg:       #f0705f;
  --neg-wash:  #2c1512;
  --warn:      #d9a441;
  --warn-wash: #2b1f0d;

  --chart-in:   #3d8b84;
  --chart-out:  #e0ab63;
  --chart-grid: #25272d;

  --age-0: #4a525c;
  --age-1: #c9a05c;
  --age-2: #d08a4c;
  --age-3: #cf5f45;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-md: 0 4px 12px -2px rgba(0, 0, 0, .5), 0 2px 4px -2px rgba(0, 0, 0, .4);
  --shadow-lg: 0 12px 28px -6px rgba(0, 0, 0, .6), 0 4px 10px -4px rgba(0, 0, 0, .5);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --n-0:  #17181c;
    --n-25: #141519;
    --n-50: #101114;
    --n-100:#1e2025;
    --n-150:#24262c;
    --n-200:#2b2e35;
    --n-300:#3a3e47;
    --n-500:#82868f;
    --n-600:#9aa0aa;
    --n-700:#c6cad2;
    --n-900:#eceef2;

    /* The surface step carries the containment; the hairline only confirms it.
       Previously the border sat ~8 L* above the card while the card sat only
       ~4 above the page, so the outline was the loudest thing on the screen. */
    --surface:        #17181c;
    --surface-sunken: #08090c;
    --surface-hover:  #1d1f25;
    --surface-active: #25272e;

    --border:        #212329;
    --border-strong: #2e3138;

    --accent:        #5464dd;
    --accent-hover:  #6070e8;
    --accent-text:   #9aa4f7;
    --accent-text-hover: #b4bbfa;
    --accent-wash:   #1e2143;
    --accent-border: #333a72;

    --pos:       #3fb87c;
    --pos-wash:  #10281d;
    --neg:       #f0705f;
    --neg-wash:  #2c1512;
    --warn:      #d9a441;
    --warn-wash: #2b1f0d;

    --chart-in:   #3d8b84;
    --chart-out:  #e0ab63;
    --chart-grid: #25272d;

    --age-0: #4a525c;
    --age-1: #c9a05c;
    --age-2: #d08a4c;
    --age-3: #cf5f45;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-md: 0 4px 12px -2px rgba(0, 0, 0, .5), 0 2px 4px -2px rgba(0, 0, 0, .4);
    --shadow-lg: 0 12px 28px -6px rgba(0, 0, 0, .6), 0 4px 10px -4px rgba(0, 0, 0, .5);
  }
}

* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI",
               Roboto, "Helvetica Neue", Arial, sans-serif;
  color: var(--text);
  background: var(--surface-sunken);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-variant-numeric: tabular-nums;
}
/* Money is compared down a column far more often than it is read as prose.
   font-variant-numeric alone is silently ignored by some system faces, so the
   OpenType feature is requested directly alongside it. */
body, .num, td.num, th.num, .big-number, .stat .n, .dash-line, .recon-amount,
.recon-stats .n, table.data td, table.report td, .cc-total {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

a { color: var(--accent-text); text-decoration: none; transition: color var(--ease); }
a:hover { color: var(--accent-text-hover); text-decoration: none; }
/* R15 F93 (WCAG 1.4.1): a link inside running text is told apart by more than its colour. Table cells, lists and
   menus keep the hover-only underline below. R15 FIX-B (ADV-B-06): so does every link of a help page (its tables
   and lists are running text) and a link marked `in-text` (a sentence in a card or a table cell). */
/* R17-W345-A7 (USE-14, USE-04): and a link in a message, warning or error box (the weak-password banner). */
p a:not(.btn), .helptext a:not(.btn), .doc-body a:not(.btn), a.in-text,
.msg a:not(.btn), .warn-box a:not(.btn), .error-box a:not(.btn) { text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
/* Links standing alone in a list of links (dashboard, help, menus) are 24px targets (WCAG 2.5.8). */
.main li > a:not(.btn), .dash-card a:not(.btn), .more-items a { display: inline-flex; align-items: center; min-height: 24px; }
/* Underline on hover only: in a dense ledger, permanently underlined links
   turn every table column into a striped mess. Buttons are never underlined. */
a:hover:not(.btn):not(.badge):not(.icon-btn):not(.filter-tabs a) {
  text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px;
}
.btn:hover, .btn:focus { text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-radius: var(--r-sm);
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

.icon-defs { display: none; }
.shell { display: flex; min-height: 100vh; }

/* --- Sidebar -------------------------------------------------------------
   Sits on the sunken canvas rather than on its own white panel: one hairline
   separates it, so the eye goes to the content, not to the furniture. */
.sidebar {
  width: 216px; flex-shrink: 0;
  background: var(--surface-sunken);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; padding: 14px 0 12px;
  position: sticky; top: 0; height: 100vh;
}
.brand {
  font-size: 14px; font-weight: 600; color: var(--text);
  letter-spacing: -.01em; padding: 2px 18px 12px;
  display: flex; align-items: center; gap: 8px;
}
.brand::before {
  content: ""; width: 15px; height: 15px; flex: none; border-radius: 4.5px;
  background: linear-gradient(145deg, var(--accent), color-mix(in oklab, var(--accent) 62%, #000));
  box-shadow: var(--shadow-sm);
}

.company-switcher { padding: 0 10px 10px; margin-bottom: 6px; }
.company-switcher summary {
  cursor: pointer; padding: 0 9px; height: var(--h-control);
  display: flex; align-items: center; gap: 6px;
  border: 1px solid transparent; border-radius: var(--r);
  font-weight: 500; font-size: 13.5px; color: var(--text);
  background: transparent; list-style: none;
  transition: background var(--ease), border-color var(--ease);
}
.company-switcher summary::-webkit-details-marker { display: none; }
.company-switcher summary::after {
  content: ""; margin-left: auto; width: 7px; height: 7px; flex: none;
  border-right: 1.5px solid var(--text-subtle); border-bottom: 1.5px solid var(--text-subtle);
  transform: rotate(45deg) translate(-2px, -2px);
}
.company-switcher summary:hover { background: var(--surface-hover); border-color: var(--border); }
.company-switcher details[open] summary { background: var(--surface-active); border-color: var(--border); }
.company-switcher ul {
  list-style: none; margin: 4px 0 0; padding: 4px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-md);
}
.company-switcher li a {
  display: block; padding: 6px 9px; border-radius: var(--r-sm);
  font-size: 13.5px; color: var(--text);
}
.company-switcher li a:hover { background: var(--surface-hover); }
.company-switcher li a.active { background: var(--accent-wash); color: var(--accent-text); font-weight: 500; }

/* The organisation's pages under its name (FIX-E G4): members, key rotation, reconnect, recover, a lost passkey. */
.organisation-menu summary {
  cursor: pointer; list-style: none; font-size: 12.5px; color: var(--text-subtle);
}
.organisation-menu summary::-webkit-details-marker { display: none; }
.organisation-menu summary:hover { color: var(--text); }
.organisation-menu ul {
  list-style: none; margin: 4px 0 0; padding: 4px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-md);
}
.organisation-menu li a {
  display: block; padding: 6px 9px; border-radius: var(--r-sm);
  font-size: 13.5px; color: var(--text);
}
.organisation-menu li a:hover { background: var(--surface-hover); }

.sidebar nav { display: flex; flex-direction: column; padding: 0 10px; gap: 1px; overflow-y: auto; }
.sidebar nav a {
  display: flex; align-items: center; gap: 9px;
  padding: 0 9px; height: 30px; border-radius: var(--r-sm);
  color: var(--text-muted); font-weight: 450; font-size: 13.5px;
  transition: background var(--ease), color var(--ease);
}
.sidebar nav a:hover { background: var(--surface-hover); color: var(--text); text-decoration: none; }
.sidebar nav a.active { background: var(--surface-active); color: var(--text); font-weight: 550; }
.sidebar nav a.disabled { color: var(--n-300); cursor: default; }
/* Icons carry the row's identity at a glance; they inherit the label's colour
   so active and hover states move as one. */
.sidebar nav a svg { width: 15px; height: 15px; flex: none; opacity: .82; }
.sidebar nav a.active svg { opacity: 1; }

.sidebar-footer {
  margin-top: auto; padding: 10px 18px 0; margin-top: auto;
  border-top: 1px solid var(--border);
  color: var(--text-subtle); font-size: 12.5px;
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
}
.linklike {
  background: none; border: none; color: var(--text-subtle); cursor: pointer;
  font: inherit; font-size: 12.5px; padding: 0; transition: color var(--ease);
  /* R15 F93 (D101, WCAG 2.2 target size): a 24px target however small the text. */
  display: inline-flex; align-items: center; min-height: 24px; min-width: 24px;
}
.linklike:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
/* Destructive actions are never the quietest thing on the row — grey reads as
   disabled, and a bookkeeper should see what deletes before they click it. */
.linklike-danger { color: var(--neg); }
.linklike-danger:hover { color: var(--neg); }
.sf-user { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.sf-actions form { display: flex; }

/* A square, quiet icon button — the shape used for any chrome-level action. */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; flex: none;
  background: none; border: 1px solid transparent; border-radius: var(--r-sm);
  color: var(--text-subtle); cursor: pointer;
  transition: background var(--ease), color var(--ease), border-color var(--ease);
}
.icon-btn:hover { background: var(--surface-active); color: var(--text); }
.icon-btn svg { width: 15px; height: 15px; }
/* The button offers the theme you would switch TO, so the moon shows in light. */
.t-moon { display: none; }
.t-sun { display: none; }
.icon-btn .t-moon { display: block; }
html.is-dark .icon-btn .t-moon { display: none; }
html.is-dark .icon-btn .t-sun { display: block; }

/* min-width:0 lets this flex item shrink below its content's min-content
   width; without it a wide table pushes the whole page sideways instead of
   scrolling inside its own card. */
.main { flex: 1; min-width: 0; padding: var(--sp-6) 30px 56px; max-width: 1240px; }
/* The status badge centres on the title's x-height instead of hanging above
   its baseline. */
h1 { font-size: 21px; font-weight: 600; letter-spacing: -.02em; margin: 0 0 4px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; overflow-wrap: anywhere; }
h1 .badge { font-size: 12px; font-weight: 500; position: relative; top: 1px; }
h2 { letter-spacing: -.01em; }
.page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.subtitle { color: var(--text-muted); margin: 0; font-size: 13.5px; overflow-wrap: anywhere; }
/* R17-W345-A7 (USE-15): a long unbroken name, memo or filename in a title, subtitle or the supplier VAT warning wraps
   instead of widening the page past a phone screen (it took detail pages to 531-749 px at 375 px). */
.js-vat-number-text { overflow-wrap: anywhere; }

.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: var(--sp-4); margin-bottom: var(--sp-3);
  /* A table too wide for the page scrolls within its own card rather than
     dragging the whole layout sideways. */
  max-width: 100%; overflow-x: auto;
}
/* Summary tiles: label above figure. The label is read first to know what the
   number means, so it leads — and the figure gets the size, not a colour.

   Equal-width, because tiles sized to their own content leave the row ragged
   and it stops reading as one module. */
.stat-row { display: flex; gap: var(--sp-3); margin-bottom: var(--sp-3); flex-wrap: wrap; }
.stat {
  display: flex; flex-direction: column-reverse; gap: 2px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4); min-width: 164px;
  flex: 0 1 auto;
}
/* Within one row the tiles share a width so their edges line up. */
.stat-row .stat { flex: 1 1 0; max-width: 232px; }
.stat .n { font-size: 20px; font-weight: 600; letter-spacing: -.02em; color: var(--text); line-height: 1.25; }
.stat .l { color: var(--text-subtle); font-size: 12.5px; }

/* Dark: the fill separates on its own, so panels drop the outline. Structural
   rules inside them (table hairlines, input borders) are unaffected — those
   divide content rather than contain it. */
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .stat {
  border-color: transparent;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .card,
  :root:not([data-theme="light"]) .stat {
    border-color: transparent;
  }
}


table.data { width: 100%; border-collapse: collapse; background: transparent; }
table.data th {
  text-align: left; font-size: 12px; font-weight: 550; letter-spacing: .025em;
  text-transform: none; color: var(--text-muted);
  padding: 0 12px 8px; border-bottom: 1px solid var(--border-strong);
}
table.data td { padding: 8px 12px; border-bottom: 1px solid var(--border); }
table.data tr:last-child td { border-bottom: none; }
table.data tbody tr { transition: background var(--ease); }
table.data tbody tr:hover td { background: var(--surface-hover); }

/* A row that highlights on hover must be clickable across its width, or the
   highlight is a promise the row does not keep. The behaviour is delegated in
   base.html; this only shows the affordance. Rows carrying their own controls
   (checkboxes, selects, inline forms) are excluded there and keep the arrow. */
table.data tbody tr.is-row-link { cursor: pointer; }
/* `table.data th` (0,1,2) outranks a bare `th.num` (0,1,1), which left-aligned
   every money heading over a right-aligned column of figures. Matched here so
   the heading sits over the digits it labels. */
td.num, th.num, table.data th.num, table.report th.num {
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}
td.nowrap, th.nowrap { white-space: nowrap; }
td.ellipsis { max-width: 220px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sent-tick { color: var(--pos); white-space: nowrap; }
.not-sent { color: var(--text-subtle); white-space: nowrap; }
/* Empty states. A tbody holding one row whose only cell spans the table is
   always the "nothing here yet" case: give it room and centre it, so an empty
   list reads as empty rather than as a first row of data. */
table.data tbody tr:only-child td[colspan] {
  padding: 34px 16px; text-align: center;
  color: var(--text-muted); font-size: 13.5px; border-bottom: none;
}
table.data tbody tr:only-child:hover td[colspan] { background: transparent; }
table.data tbody tr:only-child td[colspan] code {
  font-size: 12.5px; padding: 1px 5px; border-radius: var(--r-sm);
  background: var(--surface-active); color: var(--text);
}

/* A document's summation: its own right-aligned block, only as wide as it
   needs to be. The grand total steps up in scale as well as weight, so it is
   not distinguished by boldness alone. */
.doc-totals {
  margin: 0; margin-left: auto; width: fit-content; min-width: 260px;
  padding: 10px 12px 2px 0;
}
.doc-totals > div {
  display: flex; justify-content: space-between; align-items: baseline; gap: 32px;
  padding: 4px 0;
}
.doc-totals dt { color: var(--text-muted); font-size: 13.5px; }
.doc-totals dd { margin: 0; font-variant-numeric: tabular-nums; }
.doc-totals .t-grand {
  border-top: 1px solid var(--border-strong);
  margin-top: 6px; padding-top: 10px;
}
.doc-totals .t-grand dt { color: var(--text); font-weight: 600; font-size: 14px; }
.doc-totals .t-grand dd { font-size: 17px; font-weight: 600; letter-spacing: -.015em; }

/* A total is closed off by a rule above it, not by a coloured band. */
tr.total td {
  font-weight: 600; border-top: 1px solid var(--border-strong);
  border-bottom: none; background: transparent; padding-top: 10px;
}
table.data tbody tr.total:hover td { background: transparent; }

/* --- Badges --------------------------------------------------------------
   A tinted wash with matching text, no border. Same size as body copy minus
   one step so a column of them reads as a column, not as buttons. */
.badge {
  white-space: nowrap; display: inline-flex; align-items: center;
  padding: 1px 8px; border-radius: var(--r-sm);
  font-size: 12px; font-weight: 500; line-height: 1.6;
}
.badge-posted { background: var(--pos-wash); color: var(--pos); }
.badge-draft { background: var(--surface-active); color: var(--text-muted); }
.badge-type { background: var(--accent-wash); color: var(--accent-text); }

/* --- Buttons -------------------------------------------------------------
   One filled button per screen carries the primary action; everything else
   is a quiet outline that only lifts on hover. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--h-control); padding: 0 13px;
  background: var(--accent); color: #fff; border: 1px solid transparent;
  border-radius: var(--r); font-weight: 500; font-size: 13.5px;
  cursor: pointer; white-space: nowrap;
  box-shadow: var(--shadow-sm);
  transition: background var(--ease), border-color var(--ease), color var(--ease), box-shadow var(--ease);
}
.btn:hover { background: var(--accent-hover); color: #fff; text-decoration: none; }
.btn:active { box-shadow: none; }
.btn-secondary {
  background: var(--surface); color: var(--text); border-color: var(--border-strong);
}
.btn-secondary:hover { background: var(--surface-hover); color: var(--text); border-color: var(--n-300); }
.btn-sm { height: var(--h-control-sm); min-height: 24px; padding: 0 10px; font-size: 13px; }
.btn:disabled, .btn[disabled] { opacity: .5; cursor: not-allowed; }

/* --- Fields --------------------------------------------------------------
   Every control is exactly one height, so any row of them shares a baseline. */
form.stacked p { margin: 0 0 14px; }
form.stacked label { display: block; font-weight: 500; font-size: 12.5px; color: var(--text-muted); margin-bottom: 5px; }
input[type="text"], input[type="date"], input[type="number"], input[type="email"],
input[type="password"], input[type="month"], input[type="search"], select, textarea {
  width: 100%; max-width: 420px; height: var(--h-control); padding: 0 10px;
  border: 1px solid var(--border-strong); border-radius: var(--r);
  font: inherit; font-size: 13.5px; color: var(--text);
  background: var(--surface);
  transition: border-color var(--ease), box-shadow var(--ease);
}
textarea { height: auto; padding: 8px 10px; }
/* A stroked chevron, matching the nav icon set's weight — not the browser's
   solid triangle, which reads much heavier than everything around it. */
select {
  -webkit-appearance: none; appearance: none; padding-right: 26px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 16 16' fill='none' stroke='%2382868f' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.2 8 10l4-3.8'/%3E%3C/svg%3E");
  background-position: right 9px center;
  background-size: 10px 10px;
  background-repeat: no-repeat;
}
select::-ms-expand { display: none; }
input::placeholder, textarea::placeholder { color: var(--text-subtle); }
input:hover, select:hover, textarea:hover { border-color: var(--n-300); }
/* Mouse focus gets the soft ring; keyboard focus keeps the same 2px outline
   every other control uses, so tabbing through a reconcile row is as legible
   as tabbing through its buttons. */
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 18%, transparent);
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); height: auto; }
.helptext { display: block; color: var(--text-subtle); font-size: 12px; margin-top: 4px; }
/* R8 F39 (D100): the visible explanation beside every VAT-treatment select. */
.vat-terms { margin-top: 10px; font-size: 13px; color: var(--text-subtle); max-width: 72ch; }
.vat-terms > summary { cursor: pointer; color: var(--text); }
.vat-terms dl { margin: 6px 0 0; }
.vat-terms dt { font-weight: 600; color: var(--text); margin-top: 6px; }
.vat-terms dd { margin: 2px 0 0; }
/* Money fields (F18, static/js/money.js): the reading or the refusal beside the field; the field itself is never rewritten.
   The hint line exists, empty, from the moment the field does and its height never changes, so the blur that fills it
   cannot move the button being clicked (R4 F18 round 2, ADV-F18-4). A long sentence is cut to one line; the whole of it
   is the title and what aria-describedby reads. */
.money-hint { display: block; box-sizing: content-box; font-size: 12px; margin-top: 2px; line-height: 16px; height: 16px;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: left;
  width: 0; min-width: 100%; }   /* adds no width of its own: a table column does not widen when it fills */
.money-hint[data-float] { position: absolute; z-index: 5; width: auto; min-width: 0; max-width: 28em; margin-top: 1px;
  pointer-events: none; background: var(--surface, #fff); }
.money-hint[data-float]:empty { visibility: hidden; }
.money-hint-ok { color: var(--text-subtle); }
.money-hint-error { color: var(--neg); }
input[aria-invalid="true"].money-input, input[aria-invalid="true"].qty-input, input[aria-invalid="true"].rate-input { border-color: var(--neg); }
/* One field, rendered one way (templates/partials/field.html, gauntlet N6). */
.field { margin: 0 0 12px; }
.field-error input, .field-error select, .field-error textarea { border-color: var(--red); }
.req { color: var(--red); margin-left: 2px; }
/* R15 F93 (D101): the mark is a 24px target (WCAG 2.2 AA 2.5.8); the glyph stays small inside it. */
a.help-mark, p a.help-mark { color: var(--text-subtle); text-decoration: none; font-size: 13px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; min-height: 24px; margin-left: 2px; vertical-align: middle; border-radius: var(--r-sm); }
a.help-mark:hover { color: var(--accent); }

.messages { margin-bottom: 16px; }
.msg { padding: 10px 16px; border-radius: var(--r); margin-bottom: 8px; }
.msg-success { background: var(--pos-wash); color: var(--pos); }
.msg-error, .errorlist { background: var(--neg-wash); color: var(--red); }
.error-box { background: var(--neg-wash); color: var(--red); padding: 10px 16px; border-radius: var(--r); margin-bottom: 14px; }
.warn-box { background: var(--warn-wash); color: var(--warn); padding: 10px 16px; border-radius: var(--r); margin-bottom: 14px; }   /* R8 F40a, F40b */
/* R16b P1 (case refresh BAK-02): the alert boxes (VAT caveats, a document's missing or changed file) and the warn
   badge had no rules and rendered as plain text. The theme's own text/wash pairs, so each theme keeps >= 4.5:1. */
.alert { padding: 10px 16px; border-radius: var(--r); margin: 0 0 14px; }
.alert-error { background: var(--neg-wash); color: var(--neg); }
.alert-warning { background: var(--warn-wash); color: var(--warn); }
.alert-info { background: var(--accent-wash); color: var(--accent-text); }
.badge-warn { background: var(--warn-wash); color: var(--warn); }

/* Running totals on the journal form. The state text carries the meaning and
   the colour only reinforces it, so the row still reads without colour. */
.je-totals td {
  border-top: 1px solid var(--border-strong); border-bottom: none;
  padding-top: 12px; font-weight: 600; font-size: 14px;
}
.je-totals .je-totals-label { font-weight: 500; color: var(--text-muted); }
.je-balance-state::before {
  content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: 7px; vertical-align: 1px; background: var(--n-300);
  transition: background var(--ease);
}
.je-totals.is-balanced .je-totals-label { color: var(--pos); font-weight: 600; }
.je-totals.is-balanced .je-balance-state::before { background: var(--pos); }
.je-totals.is-unbalanced .je-totals-label { color: var(--neg); font-weight: 600; }
.je-totals.is-unbalanced .je-balance-state::before { background: var(--neg); }

/* Remove-line control: present but recessive, so it never competes with the
   fields it sits beside. It only gains colour on hover, when it is the thing
   being aimed at. */
/* Always visible, just quiet. Hiding it until its own row is hovered meant a
   user who added a line by mistake had no way of knowing it could be undone —
   an affordance that only exists once you have already found it is not one. */
.je-remove { width: 34px; padding-left: 0 !important; padding-right: 4px !important; }
.je-remove-btn { color: var(--n-300); transition: color var(--ease), background var(--ease); }
.je-line:hover .je-remove-btn { color: var(--text-subtle); }
.je-remove-btn:hover { color: var(--neg); background: var(--neg-wash); }

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

/* journal form line grid */
.je-lines input, .je-lines select { max-width: none; }
.je-lines td { padding: 4px 6px; }
.je-actions { display: flex; gap: 10px; margin-top: var(--sp-4); }
.reminder-actions { align-items: flex-end; flex-wrap: wrap; gap: 12px 16px; margin-top: var(--sp-3); }

/* invoicing */
/* A segmented control, not a row of pills: one enclosing track, the selected
   segment raised out of it. Reads as one control with one current state. */
.filter-tabs {
  display: inline-flex; gap: 2px; margin-bottom: 16px; padding: 2px;
  background: var(--surface-active); border-radius: var(--r);
}
.filter-tabs a {
  display: inline-flex; align-items: center; height: 26px; padding: 0 11px;
  border-radius: var(--r-sm); font-size: 13px; font-weight: 500;
  color: var(--text-muted); border: none; background: transparent;
  transition: background var(--ease), color var(--ease), box-shadow var(--ease);
}
.filter-tabs a:hover { text-decoration: none; color: var(--text); }
.filter-tabs a.active {
  background: var(--surface); color: var(--text);
  box-shadow: var(--shadow-sm); font-weight: 550;
}
/* Card headings share one rule instead of three inline font-sizes. */
.card-title { font-size: 14px; font-weight: 600; letter-spacing: -.01em; margin: 0 0 var(--sp-3); }

/* A labelled control in a horizontal form: label above, both as one unit, so
   a row of them shares a baseline and nothing is left to be guessed at. */
.field { display: flex; flex-direction: column; gap: 4px; }
.field-label { font-size: 12px; font-weight: 500; color: var(--text-muted); white-space: nowrap; }
.field-optional { color: var(--text-subtle); font-weight: 400; }
.inline-form { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.inline-form .btn { margin-bottom: 0; }

/* Already sent: the form folds away so the control that just fired is not
   sitting armed under the cursor. */
.sent-note { display: flex; align-items: center; gap: 7px; margin: 0 0 10px; font-size: 13.5px; color: var(--text-muted); }
.sent-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--pos); flex: none; }
.resend > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
  font-size: 13.5px; color: var(--accent-text);
}
.resend > summary::-webkit-details-marker { display: none; }
.resend > summary::before {
  content: ""; width: 9px; height: 9px; flex: none; background: currentColor;
  -webkit-mask: var(--chev) center / 9px 9px no-repeat; mask: var(--chev) center / 9px 9px no-repeat;
  transform: rotate(-90deg); transition: transform var(--ease);
}
.resend[open] > summary::before { transform: rotate(0deg); }
.resend > summary:hover { text-decoration: underline; text-underline-offset: 2px; }
.resend .send-form { margin-top: 12px; }
.resend .helptext { margin-top: 8px; }
.inline-form input, .inline-form select { max-width: 220px; }
/* An address is read whole before anything is sent to it: the field fits a
   long address and still shrinks to the row on a phone (R4 USE-01). */
.inline-form .field:has(> input[type="email"]) { flex: 0 1 26rem; min-width: 0; }
.inline-form input[type="email"] { width: 100%; max-width: none; }
.form-grid { display: flex; gap: 20px; flex-wrap: wrap; }
.form-grid p { min-width: 220px; }
/* Status colour ranks urgency; it does not label every state.

   Four saturated hues at equal chroma made "Awaiting" shout as loudly as
   "Overdue", so nothing stood out and hue became decoration. Ordinary,
   expected states are neutral; only the states that need a person to do
   something — or that record a settled outcome — carry colour, and only
   Overdue and Voided carry the alarm one. */
.badge-approved, .badge-awaiting-payment, .badge-part-paid,
.badge-q-sent, .badge-q-draft {
  background: var(--surface-active); color: var(--text-muted);
}
.badge-paid, .badge-q-accepted, .badge-q-converted {
  background: var(--pos-wash); color: var(--pos);
}
.badge-overdue, .badge-voided, .badge-q-declined {
  background: var(--neg-wash); color: var(--neg);
}

.login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; }
.login-card { width: 360px; }
.login-card h1 { text-align: center; color: var(--accent-text); }   /* R15 F93: >= 4.5:1 in light and dark */

/* ---- reports ---- */
/* One control height, one baseline: filters left, view/export actions right.
   Every control is at least as wide as its own placeholder. */
.report-toolbar {
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px 14px; flex-wrap: wrap; padding: 8px 14px; margin-bottom: 12px;
}
.report-filter { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.report-filter label { font-size: 13px; font-weight: 600; color: var(--muted); display: flex; gap: 6px; align-items: center; }
.report-exports { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.report-exports .inline-form { gap: 8px; flex-wrap: nowrap; }
.report-toolbar input, .report-toolbar select, .report-toolbar .btn {
  height: 32px; margin: 0; line-height: 1.2;
}
.report-toolbar input, .report-toolbar select { padding: 2px 8px; }
.report-toolbar .btn { display: inline-flex; align-items: center; white-space: nowrap; }
.report-filter input[type="date"] { width: auto; max-width: 148px; }
/* R13 F57 fix 1 (ADV-F57 N1): filters carried from a drill-down that the form has no field for, shown and removable. */
.carried-filters { display: inline-flex; gap: 6px; align-items: center; flex-wrap: wrap; font-size: 13px; }
.filter-chip { display: inline-flex; gap: 4px; align-items: center; padding: 1px 4px 1px 9px; border-radius: 999px;
  background: var(--accent-wash); border: 1px solid var(--accent-border); color: var(--accent-text); }
.filter-chip a { text-decoration: none; padding: 0 4px; border-radius: 999px; color: inherit; font-weight: 700; }
.filter-chip a:hover { background: var(--surface-active); }
.report-exports input[type="text"] { width: 158px; max-width: 158px; }
table.report td.indent { padding-left: 28px; }
/* R12 F59a (reports/export.py): a cell only the CSV carries — the ledger's and account
   transactions' "Correction" column. Hidden on the screen and in print; the CSV keeps it. */
.export-only { display: none !important; }
table.report tr.section-head td {
  font-size: 12px; text-transform: none; letter-spacing: 0; font-weight: 600;
  color: var(--text); padding-top: 18px; border-bottom: 1px solid var(--line); background: none;
}
table.report tr.subtotal td { font-weight: 650; border-top: 1px solid var(--border-strong); background: var(--surface-hover); }
table.report tr.keyline td { font-weight: 700; border-top: 1px solid var(--border-strong); border-bottom: 2px solid var(--border-strong); }
.muted { color: var(--muted); }
/* A grid of destinations. Each card is one tap target, sized to its content
   rather than stretched to the tallest sibling, and it lifts on hover so it
   reads as somewhere to go rather than as a panel of text. */
.report-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: var(--sp-3); align-items: start; }
.report-card {
  display: flex; flex-direction: column; gap: 4px;
  padding: var(--sp-3) var(--sp-4); color: var(--text); margin-bottom: 0;
  transition: border-color var(--ease), box-shadow var(--ease), background var(--ease);
}
.report-card h2 { margin: 0; font-size: 14px; font-weight: 550; letter-spacing: -.01em; }
.report-card p, .report-card .subtitle { margin: 0; font-size: 13px; color: var(--text-muted); line-height: 1.45; }
.report-card:hover {
  text-decoration: none; border-color: var(--border-strong);
  background: var(--surface-hover); box-shadow: var(--shadow-md);
}
.report-card:hover h2 { color: var(--accent-text); }

/* ---- dashboard ---- */
.dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-3); }
@media (min-width: 900px) {
  .dash-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .dash-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.card-caption { margin: -4px 0 8px; font-size: 13px; }
.aging-chart { width: 100%; max-width: 320px; height: auto; margin-top: 6px; }
.bill-hint { font-size: 12px; color: var(--muted); white-space: nowrap; }
.bill-hint.neg { color: var(--red); font-weight: 600; }
.dash-grid-wide { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.dash-card h2, .dash-grid h2 { margin: 0 0 10px; font-size: 12.5px; font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--text); }
.big-number { font-size: 24px; font-weight: 600; letter-spacing: -.02em; color: var(--text); font-variant-numeric: tabular-nums; margin-bottom: 8px; }
.big-number.neg { color: var(--neg); }
/* The unit sits with the figure but never competes with it: base currency is
   per-company, so an unlabelled hero figure is a real ambiguity. */
.big-number .cur {
  font-size: .6em; font-weight: 500; color: var(--text-subtle);
  margin-right: .34em; letter-spacing: .02em; vertical-align: .1em;
}
.overdue-note { color: var(--red); font-weight: 600; }
.dash-line { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; padding: 3px 0; border-top: 1px solid var(--line); }
.dash-line > a, .dash-line > span:first-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dash-line .num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.dash-line .num.neg { color: var(--red); }
.feed-date { width: 56px; color: var(--muted); }
.cash-chart { width: 100%; height: auto; }
.legend { display: flex; gap: 18px; font-size: 13px; color: var(--muted); margin-top: 6px; }
.swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; vertical-align: -1px; }

/* ---- bank reconciliation ---- */
.recon-summary { padding: 14px 20px; }
.recon-stats { display: flex; gap: 36px; flex-wrap: wrap; }
.recon-stats .l { display: block; font-size: 12px; text-transform: none; letter-spacing: 0; color: var(--text-subtle); }
.recon-stats .n { font-size: 18px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.recon-stats .diff-ok { color: var(--green); }
.recon-stats .diff-bad { color: var(--red); }
.recon-row { padding: 14px 20px; }
.recon-row.recon-done { background: var(--pos-wash); }
.recon-cols { display: flex; gap: 24px; align-items: flex-start; }
.recon-left { width: 260px; flex-shrink: 0; }
.recon-date { color: var(--muted); font-size: 13px; }
.recon-desc { font-weight: 600; margin: 2px 0; overflow-wrap: anywhere; }
.recon-amount { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.amt-in { color: var(--green); }
.amt-out { color: var(--red); }
.recon-right { flex: 1; display: flex; flex-direction: column; gap: 8px; }
/* The suggestion IS the control: the whole panel is one button, so the entire
   area is the target and it reads as something you press rather than as a
   paragraph that happens to be clickable — a stray click on prose should never
   post a ledger match. */
.suggestion { margin: 0; }
.suggestion-btn {
  display: flex; width: 100%; justify-content: space-between; align-items: center; gap: 12px;
  text-align: left; font: inherit; color: var(--text); cursor: pointer;
  background: var(--accent-wash); border: 1px solid var(--accent-border);
  border-radius: var(--r); padding: 9px 10px 9px 12px;
  transition: background var(--ease), border-color var(--ease);
}
.suggestion-btn:hover { background: color-mix(in oklab, var(--accent-wash) 82%, var(--accent)); }
.suggestion-btn:active { background: color-mix(in oklab, var(--accent-wash) 70%, var(--accent)); }
.suggestion-what { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The amount sits in the same tabular figures as the statement line above it,
   so the two can be read against each other without moving the eye far. */
.suggestion-amt { flex: none; margin-left: auto; font-weight: 550; white-space: nowrap; }
/* The verb travels with the panel so the action is named, not just implied. */
.suggestion-do {
  flex: none; font-size: 13px; font-weight: 550; color: var(--accent-text);
  border: 1px solid var(--accent-border); border-radius: var(--r-sm);
  padding: 3px 9px; background: var(--surface);
}
.quick-create { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; margin: 0; }
.quick-create select { max-width: 260px; }   /* R17-W345-A7: every VAT treatment's name fits */
.qc-field { display: flex; flex-direction: column; gap: 2px; }
.qc-label { font-size: 11.5px; text-transform: none; letter-spacing: 0; color: var(--text-subtle); }
.qc-label .suggestion-hint { font-style: normal; color: var(--pos); text-transform: none; letter-spacing: 0; }
/* "suggested" is a helpful guess; "check" is a default nobody has confirmed. */
.qc-label .suggestion-hint.check { color: var(--warn); }
/* Disclosures share one marker: a chevron on the icon set's weight that turns
   when it opens. The browser's default ▶ is a different family from every
   other glyph on the page. */
.recon-row details > summary,
.transfer-toggle > summary,
.quick-create-details > summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 13px; color: var(--text-muted);
  transition: color var(--ease);
}
.recon-row details > summary::-webkit-details-marker,
.transfer-toggle > summary::-webkit-details-marker,
.quick-create-details > summary::-webkit-details-marker { display: none; }
.recon-row details > summary::before,
.transfer-toggle > summary::before,
.quick-create-details > summary::before {
  content: ""; width: 9px; height: 9px; flex: none;
  background: currentColor;
  -webkit-mask: var(--chev) center / 9px 9px no-repeat;
          mask: var(--chev) center / 9px 9px no-repeat;
  transform: rotate(-90deg);
  transition: transform var(--ease);
}
.recon-row details[open] > summary::before,
.transfer-toggle[open] > summary::before,
.quick-create-details[open] > summary::before { transform: rotate(0deg); }
.recon-row details > summary:hover,
.transfer-toggle > summary:hover,
.quick-create-details > summary:hover { color: var(--text); }
.quick-create-details summary { cursor: pointer; font-size: 13px; }
.quick-create-details[open] summary { margin-bottom: 8px; }
.recon-note { margin: 8px 0 0; font-size: 13px; }
/* R9 F43: why a suggestion ranks where it does, and a contradiction it carries (D4: ranked lower, never hidden). */
.suggestion-why { display: block; font-size: 12px; color: var(--pos); white-space: normal; }
.suggestion-contra { display: block; font-size: 12px; color: var(--warn); white-space: normal; }
.suggestion-btn.suggestion-contradicted { background: var(--surface); }
.diff-note { color: var(--warn); }
/* R9 F43: the Unmatch confirm says what the match posted and offers its reversal (D4). */
.unmatch-confirm { margin-top: 8px; padding: 10px 12px; border: 1px solid var(--accent-border); border-radius: var(--r);
  background: var(--surface); display: flex; flex-direction: column; gap: 8px; }
.unmatch-confirm label { font-weight: normal; }
.unmatch-confirm .choice { display: flex; gap: 8px; align-items: flex-start; }
.unmatch-confirm .choice input[type="radio"] { width: auto; margin-top: 3px; }
.unmatch-confirm .actions { display: flex; gap: 8px; flex-wrap: wrap; }
/* USE-15: at narrow widths a suggestion wraps instead of cutting its verb to "Ma". */
@media (max-width: 860px) {
  .suggestion-btn { flex-wrap: wrap; }
  .suggestion-what { white-space: normal; flex: 1 1 100%; }
  .suggestion-do { margin-left: auto; }
}
.recon-filter { margin-bottom: 12px; }
/* type=search renders unstyled in WebKit without an explicit appearance, and
   the box must be wide enough for its own placeholder. */
.recon-filter input {
  -webkit-appearance: none; appearance: none;
  width: 360px; max-width: 100%; height: 34px; padding: 6px 12px;
  border: 1px solid var(--line); border-radius: var(--r); font: inherit; background: var(--white);
}
.vat-check { font-size: 13px; color: var(--muted); white-space: nowrap; }
.vat-check input { width: auto; }
.suggestion-hint { font-size: 12px; }
.matched-note { color: var(--green); font-weight: 600; }
.matched-note form { display: inline; margin-left: 8px; }

/* Dense list tables: 10 columns must fit a 1100px viewport without clipping. */
table.data.compact th, table.data.compact td { padding: 8px 5px; font-size: 13px; }
table.data.compact td.ellipsis { max-width: 132px; }
table.data.compact .badge { font-size: 11px; padding: 2px 7px; }

/* Cards must never force horizontal page scroll: clip long hint text instead. */
.dash-card { min-width: 0; }
.dash-card .obligation-line, .dash-card .stat-line { display: flex; justify-content: space-between; gap: 8px; min-width: 0; }
.bill-hint { display: block; font-size: 12px; color: var(--muted); }

/* --- Purchases track --- */
.badge-awaiting-approval { background: var(--surface-active); color: var(--text-muted); }
/* Give adjacent header labels breathing room so DUE DATE and TOTAL never run
   together at narrow column widths. */
table.data.compact th + th, table.data.compact td + td { padding-left: 8px; }

/* --- Contacts list ---
   Seven columns on a 1126px viewport: the name column carries the row's
   identity and its link, so it takes the width freed by dropping email and
   the groups column (groups render as chips beneath the name instead). */
.contacts-table { table-layout: fixed; }
.contacts-table th.cb, .contacts-table td.cb { width: 28px; padding-right: 0; }
.contacts-table td.cb input, .contacts-table th.cb input { width: auto; margin: 0; }
.contacts-table th.c-name, .contacts-table td.c-name { width: 300px; max-width: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contacts-table th.c-type, .contacts-table td.c-type { width: 88px; }
.contacts-table th.c-vat, .contacts-table td.c-vat { width: 100px; }
.contacts-table td.c-name a { font-weight: 600; }
.chip {
  display: inline-block; padding: 1px 7px; border-radius: var(--r);
  background: var(--bg); border: 1px solid var(--line);
  font-size: 11px; color: var(--muted); white-space: nowrap;
}
/* Group chips always sit on their own line under the name, so row heights
   stay even instead of depending on how long the name is. */
/* Chips sit inline after the name: every row is one line tall, so the
   list keeps an even rhythm and its total row stays above the fold. */
.chip-row { margin-left: 6px; }
.chip-row .chip + .chip { margin-left: 4px; }
.chip-type { background: var(--white); }

/* One baseline-aligned row of filter controls, each at least as wide as its
   own placeholder so nothing renders truncated. */
.filter-bar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.filter-bar input, .filter-bar select { margin: 0; height: 34px; padding-top: 0; padding-bottom: 0; line-height: 1.2; }
.filter-bar .fb-search { width: 260px; max-width: 260px; }
.filter-bar .fb-group { width: 160px; max-width: 160px; }
.filter-bar .fb-min { width: 130px; max-width: 130px; }
.filter-bar .fb-check {
  display: flex; align-items: center; gap: 6px; height: 34px;
  font-size: 13px; color: var(--muted); white-space: nowrap;
}
.filter-bar .fb-check input { width: auto; height: auto; }
.filter-bar .btn { height: 34px; display: inline-flex; align-items: center; }

/* Bulk-action bar: only appears once rows are ticked, so the checkboxes
   visibly lead somewhere. */
.bulk-bar {
  display: flex; align-items: center; gap: 10px; margin-top: 12px;
  padding: 10px 14px; border: 1px solid var(--blue); border-radius: var(--r-sm);
  background: var(--blue-pale);
}
.bulk-bar .bulk-count { font-weight: 600; font-size: 13px; color: var(--blue-dark); }
.bulk-bar select { margin: 0; max-width: 200px; height: 32px; }

/* --- Cash coding grid ---
   The description is what the bookkeeper reads to decide the coding, so it
   gets the width; contact and VAT are pickers and need only fit their own
   options. Overrides the narrow .compact ellipsis cap. */
/* A seven-column working grid needs more than the padded page column, so this
   one card spans the page gutters. */
/* Bottom padding equal to the sticky bar so the last rows can scroll clear
   of it instead of sitting underneath. */
.cc-card { margin-left: -30px; margin-right: -30px; padding: 14px 16px 68px; margin-bottom: 0; }

#cc-table { table-layout: fixed; width: 100%; }
#cc-table th, #cc-table td { padding: 8px 5px; }
#cc-table th:first-child, #cc-table td:first-child { width: 26px; padding-right: 0; }
#cc-table td:first-child input, #cc-table th:first-child input { width: auto; margin: 0; }
#cc-table .cc-date { width: 74px; padding-right: 12px; }
#cc-table .cc-desc { width: auto; max-width: none; min-width: 280px; }
#cc-table .cc-amt { width: 84px; }
#cc-table .cc-contact { width: 184px; }
#cc-table .cc-account { width: 232px; }
#cc-table .cc-vat { width: 104px; }
#cc-table select.cc-cell { width: 100%; max-width: none; margin: 0; height: 30px; padding: 2px 6px; font-size: 13px; }
.amt-out { color: var(--red); }
.amt-in { color: var(--green); }

/* A bulk grid needs its commit action in view at all times, not below the
   fold under a long table. */
/* Full-width sticky footer; the form below reserves matching space so the bar
   never sits on top of a row. */
.cc-bar {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: 14px;
  /* Spans the same breakout width as the grid so it reads as a footer band
     rather than a card floating over the rows. */
  margin: 0 -30px; padding: 12px 22px;
  background: var(--white); border-top: 1px solid var(--line);
  box-shadow: 0 -3px 10px rgba(20,22,28,.14);
}
.cc-bar .cc-summary { font-weight: 600; font-size: 14px; }
.cc-bar .cc-total { font-variant-numeric: tabular-nums; }
.cc-bar .cc-keys { margin-left: auto; font-size: 12px; color: var(--muted); }

/* Page-head action clusters stay on one line: buttons keep their labels
   intact rather than wrapping to two lines in a narrow header. */
.head-actions { display: flex; gap: 8px; align-items: center; flex-wrap: nowrap; }
.head-actions .btn { white-space: nowrap; }
.head-actions form { display: flex; gap: 6px; align-items: center; }
.head-actions input[type="month"], .head-actions input[type="date"] { width: auto; max-width: 150px; margin: 0; height: 30px; padding: 2px 8px; }
.head-actions input[type="text"] { width: 150px; max-width: 150px; margin: 0; height: 30px; padding: 2px 8px; }
.head-actions .inline-form { gap: 6px; flex-wrap: nowrap; }

/* "More" disclosure keeps secondary actions to one header row instead of
   spilling into a second row of buttons. */
.more-menu { position: relative; }
/* inline-flex, not inline-block: as a block the .btn's centring and gap were
   dropped, so a full-width menu button read as a left-aligned text input. */
.more-menu > summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.more-menu > summary::-webkit-details-marker { display: none; }
/* The caret is drawn from the shared chevron so it matches every other
   disclosure on the page, rather than a text glyph from another family. */
.more-menu > summary::after {
  content: ""; width: 9px; height: 9px; flex: none; margin-left: 2px;
  background: currentColor;
  -webkit-mask: var(--chev) center / 9px 9px no-repeat;
          mask: var(--chev) center / 9px 9px no-repeat;
  transition: transform var(--ease);
}
.more-menu[open] > summary::after { transform: rotate(180deg); }
.more-group {
  padding: 7px 10px 3px; font-size: 11.5px; font-weight: 500;
  color: var(--text-subtle); white-space: nowrap;
}
.more-group:first-child { padding-top: 3px; }
.more-items {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 190px;
  display: flex; flex-direction: column; padding: 6px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: 0 6px 18px rgba(20,22,28,.16);
}
.more-items a { padding: 7px 10px; border-radius: var(--r-sm); font-size: 13px; color: var(--ink); white-space: nowrap; }
.more-items a:hover { background: var(--blue-pale); color: var(--blue-dark); text-decoration: none; }

/* Invoice list: the customer is the row's subject, so it gets the width the
   short fixed-content columns don't need. */
.invoice-table { table-layout: fixed; }
.invoice-table .i-number { width: 96px; }
.invoice-table .i-customer { width: auto; max-width: none; }
.invoice-table .i-date, .invoice-table .i-due { width: 76px; }
.invoice-table th.i-due { white-space: nowrap; font-size: 11px; }
/* Wide enough for "Not sent" — the customer column is auto and absorbs it. */
.invoice-table .i-sent { width: 74px; }
.invoice-table .i-amt { width: 80px; }
.invoice-table .i-status { width: 86px; }
.invoice-table .i-ref { display: block; font-size: 11px; line-height: 1.15; color: var(--muted); }
.invoice-table td { line-height: 1.25; }
.bank-id { min-width: 0; display: flex; flex-direction: column; }
.bank-id > a { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bank-todo { font-size: 12px; color: var(--muted); white-space: nowrap; }

/* Budget variance: colour by whether the variance helps profit, not by sign —
   spending under budget is a negative number but good news. */
.fav { color: var(--green); }
.unfav { color: var(--red); }
.legend { font-size: 12px; margin: -6px 0 10px; }
/* The aged reports' explanatory paragraph is a `p.legend`, not a chart legend: text, not a flex row (R12 F59a). */
p.legend { display: block; }
.legend .fav, .legend .unfav { font-weight: 600; }
.variance-table { table-layout: fixed; }
.variance-table .v-account { width: auto; max-width: none; }
.variance-table th.num, .variance-table td.num { width: 104px; }

/* Aged payables: supplier balances with their bills nested beneath. */
.aged-table tr.aged-supplier td { font-weight: 600; }
.aged-table tr.aged-bill td { font-weight: 400; font-size: 13px; border-bottom-color: var(--border); }
.aged-table tr.aged-bill td.indent { padding-left: 28px; }
.aged-table tr.aged-bill .muted { font-size: 12px; white-space: nowrap; }
.aged-table { table-layout: fixed; }
.aged-table th:first-child, .aged-table td:first-child { width: auto; }
.aged-table th.num, .aged-table td.num { width: 96px; }
.aged-table th.num { font-size: 11px; }
.aged-table tr.aged-bill td.indent { font-size: 12px; }
/* Budgeted but nothing posted: state it rather than colouring it green. */
.unposted-note { font-size: 11px; color: var(--muted); }

/* Bank transfer is a rare match type: keep it one line until opened. */
.transfer-toggle { margin-top: 6px; }
.transfer-toggle > summary { cursor: pointer; font-size: 12px; color: var(--muted); }
.transfer-toggle > summary:hover { color: var(--blue); }
.transfer-toggle .quick-create { margin-top: 6px; }
.transfer-toggle select { min-width: 190px; }
.legend-bar {
  display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
  font-size: 12px; margin: -6px 0 10px;
}
.legend-bar a { white-space: nowrap; }
/* Comparative figures are data, not links: a neutral grey keeps them from
   reading as clickable next to the blue drill-down links. */
.prior-col { color: var(--text-subtle); }

/* ============================================================================
   Phone and tablet.

   The sidebar becomes a top bar with a drawer; the page keeps its own width
   and lets wide ledgers scroll inside their card, so the page itself never
   drags sideways.
   ========================================================================= */
.nav-toggle { display: none; }

@media (max-width: 860px) {
  .shell { flex-direction: column; }

  .sidebar {
    width: auto; height: auto; position: sticky; top: 0; z-index: 30;
    padding: 0; border-right: none; border-bottom: 1px solid var(--border);
    background: var(--surface);
  }
  .brand {
    padding: 0 14px; height: 52px; margin: 0;
    display: flex; align-items: center; gap: 9px;
  }
  .nav-toggle { display: inline-flex; margin-left: auto; }

  /* Collapsed by default; the toggle reveals it. */
  .sidebar .company-switcher,
  .sidebar nav,
  .sidebar-footer { display: none; }
  .sidebar.nav-open .company-switcher,
  .sidebar.nav-open nav,
  .sidebar.nav-open .sidebar-footer { display: flex; }
  .sidebar.nav-open .company-switcher { display: block; padding: 10px 12px 8px; }
  .sidebar.nav-open nav {
    padding: 0 10px 10px; max-height: 60vh; overflow-y: auto;
    border-top: 1px solid var(--border);
  }
  .sidebar.nav-open nav a { height: 44px; }   /* full touch target */
  .sidebar.nav-open .sidebar-footer { padding: 10px 14px; margin: 0; }

  .main { padding: 16px 14px 40px; max-width: none; }
  h1 { font-size: 19px; }
  .page-head { flex-direction: column; align-items: stretch; gap: 12px; }
  .head-actions { flex-wrap: wrap; }
  /* R15 F93 (USE-15): a form in the head (Save view, depreciation month) wraps instead of running under the
     next control. */
  .head-actions .inline-form, .head-actions form { flex-wrap: wrap; }

  /* A ledger keeps its column widths and scrolls inside its card. Squeezing a
     fixed-layout table into 375px overlaps the headings instead.

     The card is marked as scrollable so the edge fade below has something to
     attach to, and the identifying first column is pinned: scrolling to reach
     Balance is useless if you can no longer see which invoice it belongs to. */
  .card { overflow-x: auto; -webkit-overflow-scrolling: touch; position: relative; }
  /* A table card loses its side padding so the scrollport edge IS the card
     edge: otherwise `left: 0` pins the frozen column inside the padding and
     the scrolling columns stay visible in that gutter. The cells' own padding
     keeps the text off the edge. */
  .card:has(> table.data), .card:has(> form > table.data) { padding-left: 0; padding-right: 0; }
  .card:has(> table.data) > :not(table), .card:has(> form > table.data) > form > :not(table) { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  table.data { min-width: 720px; }
  /* table.data (0,1,1) out-ranks a lone class (0,1,0): these name the table too, so their width applies (R15 F93). */
  table.data.contacts-table, table.data.invoice-table, table.data.variance-table, table.data.aged-table,
  .contacts-table, .invoice-table, .variance-table, .aged-table { min-width: 760px; }

  .invoice-table th:first-child, .invoice-table td:first-child,
  .contacts-table th.c-name, .contacts-table td.c-name {
    position: sticky; left: 0; z-index: 2;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--border);
  }
  table.data tbody tr:hover th:first-child,
  .invoice-table tbody tr:hover td:first-child,
  .contacts-table tbody tr:hover td.c-name { background: var(--surface-hover); }

  /* Scroll affordance. The two cover layers are fixed to the card (scroll) and
     the two shadows scroll with the content (local), so a shadow is only
     visible while there is actually more table in that direction — it appears
     when it means something and disappears at the end of the scroll. */
  .card:has(> table.data),
  .card:has(> form > table.data) {
    background-image:
      linear-gradient(to right, var(--surface), transparent),
      linear-gradient(to left,  var(--surface), transparent),
      linear-gradient(to right, rgba(20, 22, 28, .13), transparent),
      linear-gradient(to left,  rgba(20, 22, 28, .13), transparent);
    background-position: 0 0, 100% 0, 0 0, 100% 0;
    background-size: 24px 100%, 24px 100%, 9px 100%, 9px 100%;
    background-repeat: no-repeat;
    background-attachment: local, local, scroll, scroll;
  }

  /* Filters and toolbars stack rather than squeezing each control thin. */
  .filter-tabs { width: 100%; overflow-x: auto; flex-wrap: nowrap; }
  .filter-tabs a { white-space: nowrap; }
  .filter-bar, .report-toolbar, .report-filter, .report-exports { width: 100%; }
  .filter-bar .fb-search, .filter-bar .fb-group, .filter-bar .fb-min { width: 100%; max-width: none; }
  input[type="text"], input[type="date"], input[type="number"], input[type="email"],
  input[type="password"], input[type="month"], input[type="search"], select, textarea { max-width: none; }

  /* Reconcile rows go from two columns to one. */
  .recon-cols { flex-direction: column; gap: 12px; }
  .recon-left { width: auto; }
  .recon-stats { gap: 18px 22px; }
  .quick-create { flex-direction: column; align-items: stretch; }
  .quick-create select, .qc-field { max-width: none; width: 100%; }

  /* The cash-coding grid keeps its full-bleed footer but not its negative
     gutters, which would push it off a narrow screen. */
  .cc-card { margin-left: 0; margin-right: 0; }
  .cc-bar { margin: 0; padding: 12px 14px; flex-wrap: wrap; gap: 8px 12px; }
  .cc-keys { display: none; }

  .dash-grid, .report-grid { grid-template-columns: minmax(0, 1fr); }
  /* Tiles go to a real grid rather than wrapping 2 + 1, which left the last
     figure alone and full-width as an accident of wrapping rather than a
     statement about its importance. */
  .stat-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat-row .stat { min-width: 0; max-width: none; }
  .stat-row .stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

@media (max-width: 620px) {
  /* Column priority on a phone. The question asked of an invoice list here is
     "what is outstanding and what state is it in", so Number, Customer, Total,
     Balance and Status stay and the two secondary date columns step out — they
     are one tap away on the invoice itself. Dropping them brings Status within
     a short scroll instead of stranding it past 760px of table. */
  .invoice-table .i-due, .invoice-table .i-sent { display: none; }
  table.data.invoice-table, .invoice-table { min-width: 560px; }
  .contacts-table .c-vat { display: none; }
  table.data.contacts-table, .contacts-table { min-width: 560px; }
}

@media (max-width: 520px) {
  /* Every hit target reaches 44px, and buttons stop sharing a cramped row. */
  .btn { height: 40px; padding: 0 15px; }
  .btn-sm { height: 34px; }
  .head-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .head-actions .btn { width: 100%; }
  .head-actions form { grid-column: 1 / -1; }
  .head-actions form input[type="text"], .head-actions form input[type="month"] { width: 100%; max-width: none; flex: 1 1 auto; }
  .stat-row { gap: 8px; }
}

/* An archived company's row on the Companies page (gauntlet N1b). */
tr.archived { opacity: .7; }

/* Help pages and glossary (gauntlet N6). */
.glossary dt { font-weight: 600; margin-top: 16px; }
.glossary dd { margin: 4px 0 0; padding-left: 0; }
.glossary dd p { margin: 0 0 4px; }
.doc-body h1, .doc-body h2 { margin-top: 22px; }
.doc-body h2 { font-size: 18px; }
.doc-body h3 { font-size: 15px; margin-top: 18px; }
.doc-body ul { margin-left: 18px; }
/* R17-W345-A7: --surface-2 was never defined, so dark mode kept the light fallback under light text (1.05:1). */
.doc-body pre { background: var(--surface-sunken); padding: 10px 12px;
  border-radius: var(--r); overflow-x: auto; font-size: 12.5px; }
.doc-body table { margin: 10px 0; }

/* ===== R15 P4 (F91, F92): one marked block (WO-R15-P4 §0) ===== */
/* partials/field.html is a <div> now (an error list cannot sit inside a <p>): the spacing the <p> had. */
form.stacked div.field { margin: 0 0 14px; }
.form-grid div.field { min-width: 220px; }
ul.errorlist { list-style: none; margin: 4px 0 0; padding: 4px 8px; border-radius: var(--r); }
ul.errorlist li + li { margin-top: 2px; }
.error-box ul.errorlist { background: none; margin: 0; padding: 0; }
/* A refused line's sentences, in a row under it (Q-F92-1). */
tr.doc-line.has-error input, tr.doc-line.has-error select { border-color: var(--red); }
tr.row-error td { padding-top: 0; border-top: 0; }
tr.row-error ul.errorlist { margin: 0; }
/* What capex and opex mean, in visible text under the grid (D100). */
.capex-help { margin-top: 10px; font-size: 12.5px; color: var(--text-muted); }
.capex-help p { margin: 0 0 4px; }

/* ===== R15 FIX-B (ADV-B-02, B-05, B-07): read-only fields, the cash coding grid, reflow at 375 and 320 px ===== */
/* Archived books: an admin page shows its settings in a disabled fieldset, not a form (B-02). */
fieldset.read-only-fields { border: 0; padding: 0; margin: 0; min-width: 0; }
/* The capex/opex column took what the fixed layout left over (14 px at 375 px); the grid keeps its columns and
   scrolls in its card instead (B-05). */
#cc-table .cc-capex { width: 96px; }
#cc-table { min-width: 1120px; }
@media (max-width: 860px) {
  /* The head's action group wraps instead of running past the screen (WCAG 1.4.10 at 320 px, B-07). */
  .page-head > *, .je-actions { flex-wrap: wrap; }
  /* The 860 px rule above lifts max-width from every search box; the reconcile filter fills the column. */
  #recon-search { width: 100%; max-width: 100%; }
}

/* ===== R17-W345-A7 (R17-24, E-F20): accessibility and 375 px ===== */
/* USE-15 sf5: a disclosure on the way to an action (Categorize, Bank transfer, Exclude, Send it again) is a 24px
   target (WCAG 2.5.8 without the spacing exception). Button-styled summaries have their own height. */
.main details > summary:not(.btn) { min-height: 24px; }
/* USE-01 sf2, USE-03 sf3, USE-06: the line grids' selects show their whole choice at desktop widths ('Standa',
   '6500 · Printing & Statione', 'No depart' at 1440 px). The description column gives up the width. */
tr.doc-line select.js-rate { min-width: 175px; }
tr.doc-line select[name=line_account] { min-width: 210px; }
tr.doc-line select[name=line_capex] { min-width: 80px; }
tr.doc-line input.qty-input { min-width: 56px; }
tr.doc-line input.money-input { min-width: 88px; }
.je-lines select.tracking-select { min-width: 140px; }
@media (max-width: 620px) {
  /* USE-15 sf2: the right-anchored menu panel opened past the left edge when its button sat on the left; on a phone
     it opens in the flow under the button instead. */
  .more-items { position: static; margin-top: 4px; box-shadow: none; }
}
/* R17-W345-A7 (USE-14 adversary): a help page's wide table scrolls in its own focusable region (core/help_views.py). */
.doc-body .table-scroll { overflow-x: auto; }
/* R17-W345-B2 (RUN5 REP-07 sf1, R17-29 C-F11): a figure with no single set of lines behind it (a subtotal, a ratio,
   a projection week) says so on screen, not only in the markup: a dotted underline, and the reason on hover. */
@media screen {
  tr[data-derived] > td.num, td.num[data-derived] {
    text-decoration: underline dotted; text-underline-offset: 3px; cursor: help;
  }
  tr[data-derived]:hover > td:first-child::after, tr[data-derived]:focus-within > td:first-child::after {
    content: " (" attr(data-derived) ")"; color: var(--muted); font-weight: 400; font-size: 12px;
  }
}
/* R17-S3B (RUN5 USE-14 adversary + confirm, S3): the opened company switcher lists each company as a submit button
   (templates/base.html); only `li a` was styled, so the buttons were native 142 x 21 px targets (axe target-size). */
.company-switcher li button {
  display: block; width: 100%; min-height: 28px; padding: 6px 9px; text-align: left; cursor: pointer;
  border: 0; border-radius: var(--r-sm); background: transparent; font: inherit; font-size: 13.5px; color: var(--text);
}
.company-switcher li button:hover { background: var(--surface-hover); }
.company-switcher li button.active { background: var(--accent-wash); color: var(--accent-text); font-weight: 500; }
@media (max-width: 620px) {
  /* R17-S3B (RUN5 USE-15 adversary + confirm, S3): at 560 px the fixed columns left the bill list's Supplier 31 px
     and the contacts list's three amount columns 36 px each (the figures printed over each other). The tables are
     wider, in their own scroller, so the party column and the amounts keep room; the sticky name column is narrower.
     The party column has a width of its own and the table exactly the sum of the shown columns: a total row's colspan
     keeps the hidden Due date column in the layout, and an auto column shared the spare width with it (81 px). */
  .invoice-table .i-customer { width: 160px; }
  table.data.invoice-table, .invoice-table { min-width: 658px; }
  .contacts-table th.c-name, .contacts-table td.c-name { width: 170px; }
  .contacts-table th.num, .contacts-table td.num { width: 104px; }
  table.data.contacts-table, .contacts-table { min-width: 598px; }
  /* USE-15 sf (S4): in the flow the menu panel kept min-width 190px inside a narrower details (page 382 px wide). */
  .more-items { min-width: 0; max-width: 100%; }
  .more-items a { white-space: normal; }
}

/* Settings in the UI (package f): the settings pages and the first-run wizard. */
.setting-row .helptext { display: block; margin-top: 4px; }
.setting-actions-form { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.setting-actions-title { font-size: 13px; margin: 0 0 6px; }
.setting-actions-list { list-style: none; margin: 0; padding: 0; }
.setting-actions-list li { margin: 0 0 4px; }
.setting-actions-list .linklike { margin-left: 8px; }
.settings-test-result { margin: 8px 0 0; min-height: 1.4em; }
.setup-steps { display: flex; flex-wrap: wrap; gap: 4px 16px; padding-left: 18px; margin: 0 0 14px; }
.setup-steps [aria-current="step"] { font-weight: 600; color: var(--text); }
