/* ============================================================================
   SCEND Finance portal — additions on top of scend-client.css
   Load AFTER scend-client.css. NEVER redefine its tokens here.

   WHERE THE LOOK COMES FROM
   -------------------------
   The CS portal's look is not in its page <style> blocks — it is
   `scend-client.css` (the approved SCEND design system, forked from the client
   portal) plus `cs.css` (portal-specific additions). This file is the finance
   equivalent of cs.css, extracted the same way.

   WHAT IS DELIBERATELY *NOT* HERE
   -------------------------------
   cs.css carries a COMPAT section mapping the old ops-kit vocabulary
   (.panel / .pill-bad / .skel-row / --card / --border / --green) onto the client
   kit, because its pages predated the kit. This portal has NO such block, on
   purpose. Pages here are written against the kit directly:

       ops kit  ->  this portal
       --card       --canvas          (there is NO --card token; using one
                                       renders a panel fully transparent)
       --border     --line
       --green      --good      --amber  --warn      --red  --crit
       .panel       .card + .card-h + .card-b
       .pill-bad    .pill.crit
       .skel-row    .skel

   Moving a finance page means TRANSLATING it to that vocabulary, not adding a
   shim. A shim is how one portal's accretion becomes two portals' accretion.

   Sections:
     1. Shell chrome      — _supabase.js renders the sidebar/topbar with these
                            class names; the kit styles .topbar and nothing else.
     2. Money & tables    — the thing this portal is actually for.
     3. Status vocabulary — invoice / settlement / request lifecycles.
     4. Version ladder    — effective-dated rate card versions.
     5. Impact preview    — "what would this move" before a write commits.
     6. Decision surfaces — approve/reject, staleness, guard warnings.
     7. States            — loading / empty / error (mandatory, per DESIGN.md ②).
   ========================================================================== */

/* ── 1. SHELL CHROME ─────────────────────────────────────────────────────────
   Class names inherited from the ops shell in _supabase.js. Defined against the
   kit's tokens (--rail/--rail-ink/--rail-lbl), so dark mode comes free.
   ========================================================================== */

body{background:var(--bg);color:var(--ink);font-family:var(--ff);margin:0}
body.has-side{padding-left:232px;transition:padding-left var(--t) var(--ease)}
body.has-side.side-collapsed{padding-left:64px}
@media (max-width:900px){body.has-side{padding-left:0}}

.scend-side{position:fixed;top:0;left:0;bottom:0;width:232px;background:var(--rail);
  display:flex;flex-direction:column;z-index:40;overflow-y:auto;overflow-x:hidden;
  transition:width var(--t) var(--ease)}
.side-collapsed .scend-side{width:64px}
.side-top{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:16px 14px;flex-shrink:0}
.side-top img{height:20px;width:auto;display:block;filter:brightness(0) invert(1)}
.side-collapsed .side-top img{display:none}
.side-collapse{background:none;border:0;color:var(--rail-lbl);cursor:pointer;padding:4px;
  border-radius:6px;display:flex;transition:color var(--t-fast) var(--ease)}
.side-collapse:hover{color:var(--rail-ink-strong)}
.side-collapsed .side-collapse{transform:rotate(180deg)}

.side-nav{padding:4px 8px 20px;display:flex;flex-direction:column;gap:1px}
.side-grp{font-size:10.5px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--rail-lbl);padding:16px 10px 6px}
.side-collapsed .side-grp{opacity:0;height:0;padding:8px 0 0;overflow:hidden}

.side-link{display:flex;align-items:center;gap:11px;padding:8px 10px;border-radius:var(--r-sm);
  color:var(--rail-ink);text-decoration:none;font-size:13.5px;white-space:nowrap;
  transition:background var(--t-fast) var(--ease),color var(--t-fast) var(--ease)}
.side-link:hover{background:var(--rail-2);color:var(--rail-ink-strong)}
/* the accent marks the ACTIVE item only — DESIGN.md: if a screen looks
   accent-heavy, the accent has been overused */
.side-link.active{background:var(--rail-2);color:var(--rail-ink-strong);
  box-shadow:inset 2px 0 0 var(--accent)}
.side-link svg{width:18px;height:18px;flex-shrink:0;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.side-collapsed .side-link span{display:none}
.side-collapsed .side-link{justify-content:center;padding:10px 0}

.topbar{display:flex;align-items:center;gap:12px;padding:0 20px;min-height:56px;
  background:var(--canvas);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:30}
.topbar-title{font-size:16px;font-weight:600;color:var(--ink);letter-spacing:-.2px}
.topbar-right{margin-left:auto;display:flex;align-items:center;gap:9px;flex-wrap:wrap}

.side-burger{display:none;background:none;border:0;color:var(--muted);cursor:pointer;padding:6px}
@media (max-width:900px){
  .side-burger{display:flex}
  .scend-side{transform:translateX(-100%);transition:transform var(--t) var(--ease)}
  .side-open .scend-side{transform:translateX(0)}
}

.scend-client,select.mini-sel{font:inherit;font-size:13px;color:var(--ink);background:var(--canvas);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:6px 10px;cursor:pointer}
.scend-client:focus,select.mini-sel:focus{outline:none;border-color:var(--accent)}
.scend-onlyclient{font-size:13px;font-weight:600;color:var(--ink)}

/* The SITE filter, left of the header beside the title (Kyle, 16/09). Carries a
   visible "Site" label where the client filter does not: the client filter's options
   are brand names and say what they are, whereas "UK - DE55" on its own could be read
   as a courier service, a rate band or a postcode search box.
   Accent when a site IS chosen — U1: the accent marks the ACTIVE thing, and a narrowed
   view is a state you must be able to see without opening the dropdown. Missing that
   is how someone spends ten minutes wondering where the other 86 invoices went. */
.scend-sitewrap{display:inline-flex;align-items:center;gap:7px;margin-left:4px}
.scend-sitelbl{font-size:11px;font-weight:650;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted)}
.scend-site{font-variant-numeric:tabular-nums}
.scend-site:not([data-all]){border-color:var(--accent);color:var(--accent-ink);
  background:var(--accent-soft);font-weight:650}

.scend-fresh,.scend-fresh-t{font-size:11.5px;color:var(--faint);white-space:nowrap}
.scend-refresh{font:inherit;font-size:12px;color:var(--muted);background:var(--canvas);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 10px;cursor:pointer}
.scend-refresh:hover{border-color:var(--accent);color:var(--accent)}
.scend-refresh.spinning .rc{display:inline-block;animation:fin-spin .9s linear infinite}
@keyframes fin-spin{to{transform:rotate(360deg)}}

.scend-bell{position:relative;background:none;border:0;cursor:pointer;padding:6px;
  color:var(--muted);display:inline-flex;align-items:center}
.scend-bell:hover{color:var(--accent)}

.scend-rolebadge{font-size:10.5px;font-weight:600;padding:2px 9px;border-radius:999px;
  border:1px solid currentColor;white-space:nowrap;flex-shrink:0}

.scend-logout{font:inherit;font-size:12.5px;color:var(--muted);background:var(--canvas);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 12px;cursor:pointer;
  white-space:nowrap;transition:border-color var(--t-fast) var(--ease),color var(--t-fast) var(--ease)}
.scend-logout:hover{border-color:var(--crit);color:var(--crit)}

/* ── Updates bell (28/08) ──────────────────────────────────────────────────
   Owen kept asking what was being done, so the portal tells him instead of him
   having to ask. A count, not a feed of noise: the dot appears only when there
   is something he has not seen. */
.scend-bell{position:relative;font:inherit;background:var(--canvas);border:1px solid var(--line);
  border-radius:8px;color:var(--muted);cursor:pointer;padding:3px 8px;line-height:1;display:flex;
  align-items:center}
.scend-bell:hover{color:var(--ink);border-color:var(--ink)}
.scend-bell .dot{position:absolute;top:-5px;right:-5px;min-width:16px;height:16px;padding:0 4px;
  border-radius:99px;background:var(--crit);color:#fff;font-size:10px;font-weight:700;
  display:flex;align-items:center;justify-content:center;box-sizing:border-box}
.upd-panel{position:absolute;top:46px;right:14px;width:min(420px,calc(100vw - 28px));
  max-height:70vh;overflow:auto;background:var(--canvas);border:1px solid var(--line);
  border-radius:14px;box-shadow:var(--card-shadow);z-index:80;padding:6px 0}
.upd-h{display:flex;align-items:baseline;gap:10px;padding:12px 16px 10px;
  border-bottom:1px solid var(--line)}
.upd-h b{font-size:13.5px}
.upd-h span{font-size:12px;color:var(--muted)}
.upd-h button{margin-left:auto;font:inherit;font-size:12px;color:var(--muted);background:none;
  border:0;cursor:pointer;padding:2px 4px}
.upd-h button:hover{color:var(--ink)}
.upd-row{padding:11px 16px;border-bottom:1px solid var(--line)}
.upd-row:last-child{border-bottom:0}
.upd-row .t{font-size:13px;font-weight:650;line-height:1.45}
.upd-row .d{font-size:12.5px;color:var(--ink-2);line-height:1.55;margin-top:3px}
.upd-row .m{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline;margin-top:6px}
.upd-row .when{font-size:11.5px;color:var(--muted)}
/* --good/--crit pair with their -soft backgrounds; --good-ink does NOT exist in this kit */
.upd-chip{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;
  border-radius:99px;padding:2px 8px}
.upd-chip.new{background:var(--crit-soft);color:var(--crit)}
.upd-chip.mine{background:var(--accent-soft);color:var(--accent-ink)}
.upd-chip.fixed{background:var(--good-soft);color:var(--good)}
.upd-none{padding:16px;font-size:13px;color:var(--muted)}

.scend-logo{height:20px;width:auto;display:block}

.scend-csv-wrap{display:flex;justify-content:flex-end;padding:8px 14px}
.scend-csv-btn,.scend-pager button{font:inherit;font-size:12px;color:var(--muted);
  background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:5px 11px;cursor:pointer}
.scend-csv-btn:hover,.scend-pager button:hover{border-color:var(--accent);color:var(--accent)}
.scend-pager{display:flex;align-items:center;gap:8px;justify-content:flex-end;
  padding:10px 14px;font-size:12px;color:var(--muted);border-top:1px solid var(--line-2)}
.scend-pager select{font:inherit;font-size:12px;padding:4px 8px;border:1px solid var(--line);
  border-radius:var(--r-sm);background:var(--canvas);color:var(--ink)}

/* page container. The kit's .content assumes its own .app grid; this portal uses
   the ops shell's fixed rail, so the width cap lives here. */
.wrap{max-width:1400px;margin:0 auto;padding:24px 24px 48px}
.wrap-narrow{max-width:920px}
.h1{font-size:25px;font-weight:700;letter-spacing:-.5px;margin:0 0 3px;text-wrap:balance}
.sub{color:var(--muted);font-size:13.5px;margin:0 0 22px}
.muted{color:var(--muted)}
.faint{color:var(--faint)}

/* ── 2. MONEY & TABLES ───────────────────────────────────────────────────────
   This is a finance portal: most screens are a column of figures that must be
   scannable and comparable. Money is ALWAYS tabular-numeric and right-aligned,
   so digits line up vertically and a wrong order of magnitude is visible as a
   ragged column rather than something you have to read.
   ========================================================================== */

.tbl-wrap{overflow-x:auto}
table.fin{width:100%;border-collapse:collapse;font-size:13px}
table.fin th{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);text-align:left;padding:9px 14px;background:var(--line-2);
  border-bottom:1px solid var(--line);white-space:nowrap}
table.fin td{padding:10px 14px;border-bottom:1px solid var(--line-2);vertical-align:middle}
/* A numeric COLUMN's header has to sit over its figures. `.num` sets text-align:right, but
   `table.fin th` above sets text-align:left and wins on specificity (0,0,1,2 vs 0,0,1,0) — so
   every money column in this portal has had a left-aligned header above right-aligned numbers.
   Kyle, 02/09: "the rates don't fit inline with the colum on all tables". One line, and it is the
   header that was wrong, not the figures. */
table.fin th.num{text-align:right}
table.fin tbody tr:last-child td{border-bottom:0}
table.fin tbody tr.clickable{cursor:pointer;transition:background var(--t-fast) var(--ease)}
table.fin tbody tr.clickable:hover{background:var(--line-2)}

/* long line-item lists: the header stays put while you scroll the figures */
table.fin.sticky th{position:sticky;top:0;z-index:1}

/* money + any figure. .num is the alignment, .money adds the £ treatment. */
.num,.money{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.money{font-weight:600;color:var(--ink)}
.money.zero{color:var(--faint);font-weight:500}
/* a credit / negative movement is a data state, so it earns a semantic colour */
.money.neg{color:var(--crit)}
.money.big{font-size:15px}
.unit{color:var(--faint);font-weight:500;font-size:11.5px}

/* totals row — the number the page exists to produce */
table.fin tfoot td,table.fin tr.total td{border-top:2px solid var(--line);
  border-bottom:0;padding-top:12px;font-weight:700;background:var(--canvas)}
table.fin tr.subtotal td{background:var(--line-2);font-weight:600}

/* the 9 billing buckets, as a breakdown strip rather than 9 more table rows */
.buckets{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden}
.bucket{background:var(--canvas);padding:12px 14px}
.bucket .b-lbl{font-size:11px;font-weight:600;color:var(--muted);margin-bottom:5px}
.bucket .b-val{font-size:17px;font-weight:700;font-variant-numeric:tabular-nums;
  letter-spacing:-.3px;color:var(--ink)}
.bucket.is-zero .b-val{color:var(--faint);font-weight:500}
.bucket .b-sub{font-size:11px;color:var(--faint);margin-top:3px}

/* KPI figures. The kit's .kpi is right; these are the finance label/value
   children so a page never hand-rolls the type scale. */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:16px}
.kpi-lbl{font-size:11px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:var(--muted)}
.kpi-val{font-size:26px;font-weight:700;color:var(--ink);letter-spacing:-.6px;margin-top:4px;
  font-variant-numeric:tabular-nums;line-height:1.1}
.kpi-sub{font-size:11.5px;color:var(--faint);margin-top:4px}

/* ── 3. STATUS VOCABULARY ────────────────────────────────────────────────────
   One vocabulary for every lifecycle in this portal, so "held" looks the same on
   an invoice, a postage settlement and a rate request. Semantic colours ONLY —
   these encode state, never decoration, and never the accent.

   invoice     draft -> confirmed -> sent -> paid          (void = withdrawn)
   settlement  held -> released
   request     pending -> approved | rejected
   ========================================================================== */

.st{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:650;
  padding:3px 9px;border-radius:20px;white-space:nowrap}
.st::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}

/* neutral / in-progress states say the least */
.st-draft,.st-pending{color:var(--muted);background:var(--line-2)}
/* held and anything awaiting a human is a warning: money is not moving */
.st-held,.st-review{color:var(--warn);background:var(--warn-soft)}
/* settled / paid / approved is the good end state */
.st-paid,.st-released,.st-approved{color:var(--good);background:var(--good-soft)}
/* confirmed and sent are real progress but not yet money in — accent-ink reads
   as "on track" without spending a semantic colour that means something else */
.st-confirmed,.st-sent{color:var(--accent-ink);background:var(--accent-soft)}
/* failures and withdrawals */
.st-rejected,.st-failed{color:var(--crit);background:var(--crit-soft)}
.st-void{color:var(--faint);background:var(--line-2);text-decoration:line-through}

/* Filter chips. Defined here, once — in the CS portal this was duplicated in one
   page's <style> while two other pages used the markup with no CSS at all and
   rendered as raw browser buttons. */
.chips{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:12px}
.chip{padding:5px 12px;border:1px solid var(--line);border-radius:999px;background:var(--canvas);
  color:var(--ink-2);font:inherit;font-size:12.5px;font-weight:500;cursor:pointer;white-space:nowrap;
  transition:border-color var(--t-fast) var(--ease),color var(--t-fast) var(--ease),
  background var(--t-fast) var(--ease)}
.chip:hover{border-color:var(--accent);color:var(--accent)}
.chip.on{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
.chip.on:hover{color:#fff}
.chip b{font-weight:700}

/* ── 4. VERSION LADDER ───────────────────────────────────────────────────────
   Effective-dated rate card versions, oldest at the bottom. The ladder exists
   because effective dating resolves a date to the LATEST version on or before
   it — so a backdated save changes nothing after a later version's date. That
   fact is invisible in a form and obvious in a ladder.

   Three states, and the distinction is the whole point:
     .is-now      the version in force today
     .is-future   scheduled, not yet applying
     (default)    superseded history
   ========================================================================== */

.ladder{position:relative;margin:0;padding:0;list-style:none}
.rung{position:relative;display:flex;align-items:flex-start;gap:14px;padding:13px 16px 13px 0;
  border-bottom:1px solid var(--line-2)}
.rung:last-child{border-bottom:0}
/* the spine, drawn behind the date markers */
.ladder::before{content:"";position:absolute;left:64px;top:14px;bottom:14px;width:1px;
  background:var(--line)}
.rung-date{position:relative;z-index:1;flex:none;width:64px;text-align:right;padding-right:0;
  font-size:12px;font-weight:600;color:var(--ink-2);font-variant-numeric:tabular-nums}
.rung-dot{position:relative;z-index:1;flex:none;width:11px;height:11px;margin:4px 0 0 -5.5px;
  border-radius:50%;background:var(--line);border:2px solid var(--canvas);box-shadow:0 0 0 1px var(--line)}
.rung-body{flex:1;min-width:0}
.rung-t{font-size:13.5px;font-weight:600;color:var(--ink);display:flex;align-items:center;
  gap:8px;flex-wrap:wrap}
.rung-s{font-size:12px;color:var(--muted);margin-top:3px;line-height:1.5}
.rung-figs{font-size:12px;color:var(--ink-2);margin-top:6px;display:flex;gap:14px;flex-wrap:wrap;
  font-variant-numeric:tabular-nums}
.rung-figs b{font-weight:650;color:var(--ink)}

/* in force today — the one that is actually billing */
.rung.is-now .rung-dot{background:var(--good);box-shadow:0 0 0 1px var(--good)}
.rung.is-now .rung-date{color:var(--ink)}
.rung.is-now{background:var(--good-soft)}
/* scheduled — real, but not applying yet */
.rung.is-future .rung-dot{background:var(--canvas);box-shadow:0 0 0 1px var(--warn)}
.rung.is-future .rung-date{color:var(--warn)}
/* superseded history reads quietly */
.rung:not(.is-now):not(.is-future) .rung-t{color:var(--ink-2);font-weight:600}
.rung:not(.is-now):not(.is-future) .rung-figs{color:var(--muted)}

/* the version currently loaded in the editor */
.rung.is-open{box-shadow:inset 2px 0 0 var(--accent)}

/* per-site marker. Two versions on the SAME date for different warehouses is
   normal and must never read as a duplicate — the site is on the rung. */
.rung-site{font-size:10.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);background:var(--line-2);padding:1px 7px;border-radius:5px;flex:none}

/* ── 5. IMPACT PREVIEW ───────────────────────────────────────────────────────
   What a write WOULD move, shown before it commits (PROGRAM.md §self-serve
   rule 1: preview then commit). Used by the rate editor and the request queue.
   ========================================================================== */

.impact{border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden;
  background:var(--canvas);margin:14px 0}
.impact-h{display:flex;align-items:center;gap:9px;padding:10px 14px;background:var(--line-2);
  border-bottom:1px solid var(--line);font-size:12px;font-weight:600;color:var(--ink-2)}
.impact-b{padding:6px 0}
.imp-row{display:grid;grid-template-columns:1fr auto auto auto;gap:12px;align-items:center;
  padding:8px 14px;font-size:13px}
.imp-row+.imp-row{border-top:1px solid var(--line-2)}
.imp-f{color:var(--ink-2);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.imp-was,.imp-now{font-variant-numeric:tabular-nums;text-align:right;min-width:74px}
.imp-was{color:var(--muted);text-decoration:line-through}
.imp-now{color:var(--ink);font-weight:650}
.imp-arrow{color:var(--faint);font-size:12px}
.imp-d{font-size:12px;font-weight:650;font-variant-numeric:tabular-nums;text-align:right;min-width:80px}
.imp-d.up{color:var(--crit)}      /* costs the client more */
.imp-d.down{color:var(--good)}    /* costs the client less */
.imp-d.flat{color:var(--faint)}
/* the headline: total £ movement over the period being previewed */
.impact-foot{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:12px 14px;border-top:1px solid var(--line);background:var(--line-2);font-size:13px}
.impact-foot .if-lbl{color:var(--muted);font-weight:600}
.impact-foot .if-val{font-size:19px;font-weight:700;font-variant-numeric:tabular-nums;
  letter-spacing:-.3px}
.impact-foot .if-sub{font-size:11.5px;color:var(--faint);width:100%;margin-top:2px}

/* ── 6. DECISION SURFACES ────────────────────────────────────────────────────
   Approving a rate change moves real money, so the affordances are deliberate:
   a bar that reads as a decision, and warnings that block with a door
   (PROGRAM.md §5 — "we need to check this", never "computer says no").
   ========================================================================== */

.decide{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:13px 16px;
  border-top:1px solid var(--line);background:var(--line-2)}
.decide .d-spacer{margin-left:auto}
.decide .note{flex:1;min-width:220px;font:inherit;font-size:13px;color:var(--ink);
  background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-sm);padding:7px 10px}
.decide .note:focus{outline:none;border-color:var(--accent)}

/* Guard strips. warn = proceed with your eyes open; crit = the guard is refusing
   and the text must say what to do instead. */
.guard{display:flex;gap:9px;align-items:flex-start;padding:11px 13px;border-radius:var(--r-sm);
  font-size:12.5px;line-height:1.55;margin:12px 0}
.guard-ic{flex:none;font-size:13px;line-height:1.5}
.guard.warn{background:var(--warn-soft);border:1px solid var(--warn);color:var(--ink-2)}
.guard.crit{background:var(--crit-soft);border:1px solid var(--crit);color:var(--ink-2)}
.guard.info{background:var(--accent-soft);border:1px solid var(--accent);color:var(--ink-2)}
.guard b{color:var(--ink)}
.guard .g-act{margin-top:7px;display:flex;gap:8px;flex-wrap:wrap}

/* stale-value callout: "based on £0.42, now £0.45" */
.stale{font-size:12px;color:var(--warn);font-weight:600;font-variant-numeric:tabular-nums}
.stale s{color:var(--muted);font-weight:500}

/* inline save feedback. Persists until the next edit — a message that clears
   itself after 3s cannot be distinguished from one that never appeared. */
.msg{font-size:12.5px;line-height:1.55;margin-top:10px;min-height:18px;color:var(--muted)}
.msg.ok{color:var(--good);font-weight:600}
.msg.err{color:var(--crit);font-weight:600}
.msg.work{color:var(--muted)}

/* a field the user has typed into but not yet saved */
.field input.changed,.field select.changed{border-color:var(--warn);
  box-shadow:inset 3px 0 0 var(--warn)}

/* ── 7. FORMS ────────────────────────────────────────────────────────────────
   Controls sized to the data they hold — a rate of £0.42 does not need a 62px
   box, and an ALL-CAPS label above every input turns a form into shouting.
   ========================================================================== */

.field{display:flex;flex-direction:column;gap:4px}
.field label{font-size:11.5px;font-weight:600;color:var(--muted)}
.field input,.field select,.field textarea{font:inherit;font-size:13px;color:var(--ink);
  background:var(--canvas);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:7px 9px;min-height:33px}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--accent)}
.field input[type=number]{width:96px;text-align:right;font-variant-numeric:tabular-nums}
.field input[type=date]{width:142px}
.field .hint{font-size:11px;color:var(--faint)}
.field-row{display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end}
.field-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:12px 16px}

/* ── the money control: a £ prefix that sits INSIDE the control ──────────────
   The £ chip was already styled to butt against the input (border-right:0, left
   corners only) but the INPUT itself was never styled outside `.field`, so anywhere
   else — `.rate-row` on rate-cards.html, and invoices.html — it rendered with the
   browser's own 2px grey border at 22px tall against a 35px chip. Measured: the two
   halves didn't line up, the figure was left-aligned with proportional digits, and
   with the chip 25px wide on top of a 118px input the control overflowed its column.

   So the input is styled HERE rather than only under `.field`, the control is sized
   as a whole and the input flexes to fill it, and money is right-aligned with tabular
   figures so a column of rates lines up on the decimal point.
   ========================================================================== */
.money-in{display:inline-flex;align-items:stretch;gap:0;width:100%;max-width:132px}
.money-in .pre{flex:none;font-size:12.5px;color:var(--muted);background:var(--line-2);
  border:1px solid var(--line);border-right:0;border-radius:var(--r-sm) 0 0 var(--r-sm);
  padding:0 8px;display:flex;align-items:center}
/* the input, wherever the control is used — NOT scoped to .field */
.money-in input{flex:1 1 auto;width:auto;min-width:0;   /* min-width:0 or a number input refuses to shrink */
  font:inherit;font-size:13px;font-variant-numeric:tabular-nums;text-align:right;
  color:var(--ink);background:var(--canvas);
  border:1px solid var(--line);border-radius:0 var(--r-sm) var(--r-sm) 0;
  padding:7px 9px;min-height:33px}
.money-in input:focus{outline:none;border-color:var(--accent);position:relative;z-index:1}
.money-in input:disabled{background:var(--line-2);color:var(--muted)}
/* a control with no prefix (an integer field like a threshold) keeps both corners */
.money-in > input:only-child{border-radius:var(--r-sm)}

.dl{display:grid;grid-template-columns:auto 1fr;gap:7px 16px;font-size:13px;margin:0}
.dl dt{color:var(--muted);white-space:nowrap}
.dl dd{margin:0;color:var(--ink);font-weight:500}

.sub-head{font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);margin:0 0 8px}

/* ── 8. STATES ───────────────────────────────────────────────────────────────
   The kit ships .skel / .skel-tile / .empty / .error. These are the finance
   footprints — a skeleton must occupy the SAME space as the content it stands in
   for, or the page jumps when data lands (DESIGN.md ②, no layout shift).
   ========================================================================== */

.skel-row{height:41px;margin:8px 14px;border-radius:var(--r-sm);
  background:linear-gradient(90deg,var(--skel-2),var(--skel-1),var(--skel-2));
  background-size:200% 100%;animation:fin-shimmer 1.1s linear infinite}
.skel-fig{height:26px;width:96px;border-radius:6px;margin-top:4px;
  background:linear-gradient(90deg,var(--skel-2),var(--skel-1),var(--skel-2));
  background-size:200% 100%;animation:fin-shimmer 1.1s linear infinite}
@keyframes fin-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.skel-row,.skel-fig{animation:none}}

.state{padding:34px 20px;text-align:center;color:var(--muted);font-size:13.5px}
.state-title{font-weight:600;color:var(--ink);margin-bottom:4px;font-size:14px}
.state-err{color:var(--crit)}
.state .btn{margin-top:12px}

/* ── 9. THE FOUR CLASSES EVERY PAGE ALREADY USED AND NOTHING DEFINED ─────────
   Found 2026-08-18 while adding panels to rate-cards.html. `.card.flush`, `.ph`
   and `.pnote` are used by billing.html and rate-cards.html, and `.btn.sm` by
   four of the five pages — none of them existed in this file or in
   scend-client.css. They were inert: every panel header rendered as default
   stacked flow instead of a title row, and every "small" button was full size.

   It never looked broken enough to notice, which is exactly why it survived a
   live verification pass. Defining them here rather than in each page's <style>
   because four pages already depend on them. Deliberately conservative — this
   matches what the markup plainly intends, so no existing layout moves much.
   ========================================================================== */

/* a card whose header sits flush against the top edge, body padding supplied by .card-b */
.card.flush{padding:0;overflow:hidden}

/* the panel header row: title left, action/count right */
.ph{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:13px 16px;border-bottom:1px solid var(--line)}
.ph h3{margin:0;font-size:14px;font-weight:650;letter-spacing:-.01em}
.ph h4{margin:0;font-size:12.5px;font-weight:650;color:var(--muted)}

/* the explanatory footnote under a panel — smaller and quieter than body copy */
.pnote{padding:11px 16px;border-top:1px solid var(--line);background:var(--line-2);
  color:var(--muted);font-size:11.5px;line-height:1.5}
.pnote b{color:var(--ink);font-weight:600}

/* the compact button. `.sm` is a MODIFIER — always `.btn.sm`, never `.sm` alone */
.btn.sm{padding:5px 11px;font-size:12px;line-height:1.35}

/* ══ Readable text on the accent ═══════════════════════════════════════════════
   Kyle, 28/08: "yes sort it out".

   MEASURED, with the database's own WCAG functions: white text on SCEND teal
   #00B5B6 is 2.53:1, against a 4.5:1 threshold. Every primary button in the kit
   sets `color:#fff` on `background:var(--accent)`, so the most-clicked control in
   the portal has been failing contrast since the kit shipped.

   Darkening the teal does not fix it, which is why this is a text change and not a
   background change: in DARK mode the accent is the lighter #22C9C9 and white is
   2.05:1 — worse. Dark text is the only answer that works in both themes:
       light  #1f2430 on #00B5B6 -> 6.12:1
       dark   #1f2430 on #22C9C9 -> 7.59:1
   And it is not invented here — brand_palette() has always returned `on_accent`,
   choosing text colour by real sRGB luminance, and for teal it has always said
   dark. The CSS simply never used it. settings.html now sets --on-accent from the
   same function whenever someone picks a custom accent, so a chosen colour gets
   white or dark text according to which one is actually readable on it.

   scend-client.css is the shared kit and must not be edited in this repo, so this
   overrides it here. HANDOVER_kit_accent_contrast_2026-08-28.md asks the client
   portal to make the same change upstream — until it does, the other portals still
   have the 2.53:1 button. */
:root{ --on-accent:#1f2430; }
@media (prefers-color-scheme:dark){:root:not([data-theme]){ --on-accent:#1f2430; }}
:root[data-theme="dark"]{ --on-accent:#1f2430; }
:root[data-theme="light"]{ --on-accent:#1f2430; }
.btn.primary{ color:var(--on-accent); }
/* A disabled button must LOOK disabled — the kit never styled it, so an inert primary
 * read as a live one (the raise panel's "Pick a client to raise"). */
.btn:disabled,.btn:disabled:hover{opacity:.5;cursor:not-allowed;filter:none;transform:none}
/* KPI tile that is also a link (home page): same tile, plus the affordance */
a.kpi{display:block;text-decoration:none;color:inherit;
  transition:border-color var(--t-fast) var(--ease),box-shadow var(--t-fast) var(--ease)}
a.kpi:hover{border-color:var(--accent)}
a.kpi:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* .chip.on is the other white-on-accent fill this portal renders */
.chip.on{ color:var(--on-accent); }

/* PANEL HEADERS: black, white writing — every panel, every page (Kyle 08/10). */
.ph,.card-h,.card.flush .ph{background:#111 !important;color:#fff;border-bottom-color:#111 !important}
.card-h{padding-bottom:15px !important}
.ph :is(h3,h4,b,strong,label,small,.ph-tot),.card-h :is(h3,h4,b,strong,label,small){color:#fff !important}
.ph :is(span,.muted,.pnote,a.link):not(.st):not(.pill):not(.st *):not(.pill *),
.card-h :is(span,.muted,.pnote,a.link,.link):not(.st):not(.pill):not(.st *):not(.pill *){color:rgba(255,255,255,.72) !important}
.ph .pnote{background:none !important;border:0 !important;padding:0 !important}
.ph h3::before,.card-h h3::before,.ph .fold-caret,.card-h .fold-caret{color:rgba(255,255,255,.72) !important}
.ph .btn.ghost,.card-h .btn.ghost{color:#fff;background:transparent;border-color:rgba(255,255,255,.35)}
.ph .btn.ghost:hover,.card-h .btn.ghost:hover{background:rgba(255,255,255,.1)}
.ph .scend-csv-btn,.card-h .scend-csv-btn{color:#fff !important;background:transparent;border-color:rgba(255,255,255,.35);margin-left:0}
.ph .scend-csv-btn:hover,.card-h .scend-csv-btn:hover{background:rgba(255,255,255,.1);color:#fff;border-color:rgba(255,255,255,.6)}
.card-h > .scend-csv-btn.inhead,.ph > .scend-csv-btn.inhead{margin-left:10px}
.card-h:has(> .scend-csv-btn) > h3,.ph:has(> .scend-csv-btn) > h3{margin-right:auto}
.card.fold > .card-h{padding-bottom:15px !important}
.card-h .ps-fig.warn b{color:#ffb547 !important}
.ph :is(h3,h4),.card-h :is(h3,h4),.card.flush .ph h3{font-weight:800 !important}
