/* App shell — sidebar, topbar, forms and tables.
   Colours come entirely from theme.css; nothing here declares a hex value
   except the two neutral shadows. */

*{box-sizing:border-box}
body.app{
  margin:0;background:var(--ground);color:var(--ink);
  font-family:"IBM Plex Sans",system-ui,sans-serif;font-size:14px;line-height:1.5;
}
a{color:var(--brand)}
.skip{position:absolute;left:-9999px}
.skip:focus{left:8px;top:8px;z-index:99;background:var(--gold);color:var(--brand-deep);padding:8px 14px}

.layout{display:grid;grid-template-columns:236px 1fr;min-height:100vh}

/* ---------- sidebar ---------- */
.side{
  background:var(--brand-plate);color:#fff;display:flex;flex-direction:column;
  position:sticky;top:0;height:100vh;overflow-y:auto;
}
.side-brand{display:flex;align-items:center;gap:11px;padding:18px 18px 16px;border-bottom:1px solid rgba(255,255,255,.15)}
.side-brand span{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.side-brand b{font-family:"Source Serif 4",Georgia,serif;font-size:17px;letter-spacing:.04em}
.side-brand small{font-size:10.5px;opacity:.72;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.side-nav{padding:14px 0;flex:1}
.side-group{
  font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;opacity:.55;
  margin:16px 18px 6px;font-weight:600;
}
.side-group:first-child{margin-top:0}
.side-item{
  display:flex;align-items:center;gap:11px;padding:9px 18px;color:#fff;
  text-decoration:none;font-size:13.5px;border-left:3px solid transparent;
}
.side-item:hover{background:rgba(255,255,255,.09)}
.side-item.on{background:rgba(255,255,255,.14);border-left-color:var(--gold);font-weight:600}
.side-item:focus-visible{outline:2px solid var(--gold);outline-offset:-2px}
.si-icon{width:17px;text-align:center;opacity:.85;font-size:14px}
.si-label{flex:1}
.si-badge{
  background:var(--flame);color:#fff;font-family:"IBM Plex Mono",monospace;
  font-size:10.5px;font-weight:600;padding:1px 6px;border-radius:9px;
}
.side-foot{padding:13px 18px;border-top:1px solid rgba(255,255,255,.15);font-size:11px;opacity:.7}
.side-foot .dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--mpesa);margin-right:6px}
body.is-offline .side-foot .dot{background:var(--gold)}

/* ---------- topbar ---------- */
.main-col{display:flex;flex-direction:column;min-width:0}
.topbar{
  background:var(--paper);display:flex;align-items:center;gap:16px;
  padding:15px 24px;border-bottom:1px solid var(--rule);
}
.burger{display:none;background:none;border:1px solid var(--rule);color:var(--ink);font-size:16px;padding:5px 10px;cursor:pointer;
  min-width:44px;min-height:44px;align-items:center;justify-content:center}
.tb-title{flex:1;min-width:0}
.greeting{margin:0 0 1px;font-size:11.5px;color:var(--ink-soft);letter-spacing:.02em}
.tb-title h1{
  font-family:"Source Serif 4",Georgia,serif;font-size:21px;margin:0;letter-spacing:-.01em;
  /* 380 rather than the previous 600 — a genuinely lighter, more spacious
     register for the page headline, now reachable because the font request
     asks for a real continuous weight range (300..800) instead of three
     fixed stops. Deliberately the only typography this touches: the data
     tables keep their existing, carefully contrast-checked weights exactly
     as they are — this is a headline register, not a legibility change. */
  font-weight:380;
}
.tb-title p{margin:2px 0 0;font-size:12.5px;color:var(--ink-soft)}
.tb-user{display:flex;align-items:center;gap:12px}
.tb-meta{display:flex;flex-direction:column;text-align:right;line-height:1.3}
.tb-meta b{font-size:13px;font-weight:600}
.tb-meta small{font-size:11px;color:var(--ink-soft)}
.avatar{
  width:34px;height:34px;border-radius:50%;background:var(--brand);color:#fff;
  display:grid;place-content:center;font-size:12.5px;font-weight:600;flex:0 0 auto;
}
.tb-out button{
  background:none;border:1px solid var(--rule);color:var(--ink-soft);
  font-size:12px;padding:6px 11px;cursor:pointer;font-family:inherit;
}
.tb-out button:hover{border-color:var(--flame);color:var(--flame-text)}
.content{padding:22px 24px 70px;min-width:0}

/* ---------- panels, forms, tables ---------- */
.panel{background:var(--paper);border:1px solid var(--rule);padding:18px 20px 20px;min-width:0}
.panel > h2{font-family:"Source Serif 4",Georgia,serif;font-size:16.5px;font-weight:600;margin:0 0 3px}
.panel > .hint{font-size:12px;color:var(--ink-soft);margin:0 0 15px}
.grid{display:grid;gap:14px}
.grid > *{min-width:0}
.g2{grid-template-columns:1fr 1fr}
.g13{grid-template-columns:1fr 2fr}

.field{display:flex;flex-direction:column;gap:5px;margin-bottom:13px}
.field label{font-size:11.5px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-soft)}
.field .help{font-size:11.5px;color:var(--ink-soft);font-weight:400;text-transform:none;letter-spacing:0}
input[type=text],input[type=email],input[type=password],input[type=date],input[type=number],select,textarea{
  font-family:inherit;font-size:14px;padding:8px 10px;border:1px solid var(--rule);
  background:var(--paper);color:var(--ink);width:100%;
}
input:focus,select:focus,textarea:focus{outline:2px solid var(--brand);outline-offset:-1px;border-color:var(--brand)}
input[type=number],.mono{font-family:"IBM Plex Mono",monospace;font-variant-numeric:tabular-nums}
.row{display:grid;gap:13px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}
.btn{
  font-family:inherit;font-size:14px;font-weight:600;padding:9px 18px;cursor:pointer;
  border:1px solid var(--brand);background:var(--brand);color:#fff;
}
.btn:hover{background:var(--brand-deep);border-color:var(--brand-deep)}
.btn.ghost{background:none;color:var(--brand)}
.btn.ghost:hover{background:var(--brand-tint);color:var(--brand-deep)}
.btn:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
/* .tiny keeps its small visual size (row actions like Rename/Deactivate/
   Approve need to stay compact, not each grow into a full-size button) but
   the actual tappable area still reaches the same 44px minimum as every
   other control — small to look at, not small to hit. Previously
   redefined inline, slightly differently, in eight separate page files;
   consolidated here as the one shared rule so every page gets the fix
   from one place, the same leverage as the shared nav controls. */
.btn.tiny{font-size:11px;padding:5px 10px;display:inline-flex;align-items:center;
  justify-content:center;min-height:44px;min-width:44px;flex-shrink:0}
.actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:6px}

/* "As of when?" line, shared by every report/data screen -- was
   duplicated (slightly differently) in dashboard.php and report.php;
   consolidated here, and extended with a real refresh control. */
.computed{margin:16px 0 0;font-size:11.5px;color:var(--ink-soft);font-family:"IBM Plex Mono",monospace;
  display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.refresh-link{font-family:inherit;font-size:11.5px;white-space:nowrap;text-decoration:none}
.refresh-link span[aria-hidden]{display:inline-block}

/* ---------- bottom-anchored action bar, phone width only ----------
   The single highest-value mobile change from the UI modernisation review:
   on a real phone, held one-handed, a save button that scrolls away after
   the last field is a save button a thumb has to hunt for. Fixed to the
   bottom on narrow viewports only — desktop and tablet layouts are
   completely unaffected, this is additive, not a redesign.
   Form pages opt in with class="actions actions-bar". */
@media (max-width:480px){
  .actions-bar{
    position:fixed; left:0; right:0; bottom:0; z-index:35;
    margin:0; gap:10px; padding:12px 16px calc(12px + env(safe-area-inset-bottom));
    background:var(--paper); border-top:1px solid var(--rule);
    box-shadow:var(--e-2);
  }
  .actions-bar .btn{ flex:1; text-align:center; padding:13px 18px }
  /* Space for the fixed bar so it never overlaps the last real field.
     Applied via a class on the form itself (form-with-actions-bar), not a
     :has() selector — broadly supported now, but this codebase has stayed
     deliberately conservative about relying on newer CSS selectors when a
     plain class costs nothing extra. */
  .form-with-actions-bar{ padding-bottom:76px }
}

.notice{padding:11px 14px;font-size:13px;border-left:3px solid;margin-bottom:16px}
.notice.ok{border-color:var(--brand);background:var(--brand-tint)}
.notice.bad{border-color:var(--flame);background:var(--flame-tint);color:var(--flame-text)}
.notice.warn{border-color:var(--gold-text);background:#FFFCE0}

.tscroll{overflow-x:auto;max-width:100%}
table.t{border-collapse:collapse;width:100%;min-width:560px}
table.t th{
  font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-soft);
  font-weight:600;text-align:right;padding:7px 8px;border-bottom:1.5px solid var(--brand);
}
table.t th:first-child{text-align:left;padding-left:0}
table.t td{
  font-family:"IBM Plex Mono",monospace;font-size:12.5px;text-align:right;padding:8px;
  border-bottom:1px solid var(--rule-soft);font-variant-numeric:tabular-nums;
}
table.t td:first-child{font-family:"IBM Plex Sans",sans-serif;text-align:left;padding-left:0}
table.t tr:hover td{background:var(--brand-wash)}
.chip{
  font-family:"IBM Plex Mono",monospace;font-size:10.5px;padding:2px 6px;
  border:1px solid var(--rule);color:var(--ink-soft);
}
.chip.on{border-color:var(--brand);color:var(--brand)}
.tag{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;font-weight:600;padding:3px 7px;border:1px solid currentColor;display:inline-block}
.tag.ok{color:var(--brand)}.tag.warn{color:var(--gold-text)}.tag.bad{color:var(--flame-text)}
.neg{color:var(--flame-text)}.pos{color:var(--brand)}.dim{color:var(--ink-soft)}
.empty{padding:26px 4px;color:var(--ink-soft);font-size:13px}
.pager{display:flex;gap:8px;align-items:center;margin-top:14px;font-size:12.5px;color:var(--ink-soft)}
.pager a{border:1px solid var(--rule);padding:5px 11px;text-decoration:none;color:var(--brand)}
.pager a:hover{background:var(--brand-tint)}

/* ---------- responsive ---------- */
@media (max-width:900px){
  .layout{grid-template-columns:1fr}
  .side{
    position:fixed;inset:0 auto 0 0;width:236px;z-index:40;height:100vh;
    transform:translateX(-100%);transition:transform .18s ease;
  }
  body.side-open .side{transform:none;box-shadow:0 0 0 100vmax rgba(0,0,0,.35)}
  .burger{display:flex}
  .g2,.g13{grid-template-columns:1fr}
  .tb-meta{display:none}
  .content{padding:16px 15px 60px}
  .topbar{padding:12px 15px}
}
@media (prefers-reduced-motion:reduce){.side{transition:none}}

/* ==========================================================================
   Added with the operations release: text size, palette, preferences,
   counting pad, cash tally.
   ========================================================================== */

/* Text size is a user preference, not browser zoom. The people most likely to
   need larger type are the least likely to know browser zoom exists, and it
   has to follow them between the parish desktop and their phone. Everything is
   sized in rem so one variable moves the whole interface. */
body.size-normal{ font-size:14px }
body.size-large { font-size:16px }
body.size-larger{ font-size:18px }
body.size-large  .tb-title h1{ font-size:23px }
body.size-larger .tb-title h1{ font-size:25px }
body.size-large  table.t td, body.size-larger table.t td{ font-size:13.5px; padding:10px 8px }

/* ---------- topbar: search trigger and preferences ---------- */
.tb-find{
  display:flex;align-items:center;gap:9px;background:var(--ground);border:1px solid var(--rule);
  color:var(--ink-soft);font-family:inherit;font-size:12.5px;padding:7px 12px;cursor:pointer;
  min-width:0;
}
.tb-find:hover{ border-color:var(--brand);color:var(--ink) }
.tb-find kbd{
  font-family:"IBM Plex Mono",monospace;font-size:10px;border:1px solid var(--rule);
  padding:1px 5px;color:var(--ink-soft);
}
.tb-prefs{ position:relative }
.tb-prefs > summary{
  list-style:none;cursor:pointer;border:1px solid var(--rule);padding:6px 11px;
  font-family:"Source Serif 4",Georgia,serif;font-size:14px;color:var(--ink-soft);
}
.tb-prefs > summary::-webkit-details-marker{ display:none }
.tb-prefs[open] > summary{ border-color:var(--brand);color:var(--brand) }
/* The topbar must establish a stacking context ABOVE the page content.
   The motion layer animates it, and a transform creates a stacking context at
   z-index auto — as do the animated panels below. Two auto contexts paint in
   DOM order, so without an explicit z-index here the page content paints over
   the topbar's entire subtree, and the popover's own z-index is meaningless
   because it only counts inside its ancestor's context. The symptom was the
   preferences popover appearing to be cut off and overlapped by the page. */
.topbar{ position:relative; z-index:40 }

.prefs-pop{
  position:absolute;right:0;top:calc(100% + 6px);z-index:30;background:var(--paper);
  border:1px solid var(--rule);padding:14px;width:210px;box-shadow:var(--lift-2);
}
/* On a narrow phone the anchored card can hang off the left edge of the
   screen. Pin it to the viewport instead of the button. */
@media (max-width:480px){
  .prefs-pop{ position:fixed;left:12px;right:12px;top:60px;width:auto }
}
.prefs-pop label{ display:block;font-size:11.5px;font-weight:600;letter-spacing:.03em;
  text-transform:uppercase;color:var(--ink-soft);margin-bottom:12px }
.prefs-pop select{ margin-top:5px }
.prefs-pop .btn{ width:100%;padding:7px }

/* ---------- command palette ---------- */
.palette{
  position:fixed;inset:0;z-index:60;background:rgba(18,16,46,.45);
  display:none;justify-content:center;align-items:flex-start;padding:12vh 18px 18px;
}
.palette.on{ display:flex }
.pal-card{
  background:var(--paper);border:1px solid var(--rule);width:100%;max-width:560px;
  box-shadow:var(--lift-2);display:flex;flex-direction:column;max-height:70vh;
}
.pal-input{
  border:0;border-bottom:1px solid var(--rule);padding:15px 17px;font-size:16px;
  background:transparent;color:var(--ink);
}
.pal-input:focus{ outline:none;box-shadow:none }
.pal-list{ list-style:none;margin:0;padding:6px 0;overflow-y:auto;flex:1 }
.pal-list li{
  display:flex;align-items:baseline;gap:11px;padding:9px 17px;cursor:pointer;font-size:13.5px;
}
.pal-list li.on{ background:var(--brand-tint) }
.pal-list li.on::before{ content:"";position:absolute;left:0;width:3px;height:1.9em;background:var(--brand) }
.pal-kind{
  font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-soft);
  min-width:64px;font-weight:600;
}
.pal-label{ flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap }
.pal-sub{ font-size:11.5px;color:var(--ink-soft) }
.pal-empty{ padding:20px 17px;color:var(--ink-soft);font-size:13px;cursor:default }
.pal-foot{ margin:0;padding:9px 17px;border-top:1px solid var(--rule-soft);font-size:11px;color:var(--ink-soft) }
.pal-foot kbd{ font-family:"IBM Plex Mono",monospace;border:1px solid var(--rule);padding:1px 4px;margin-right:3px }

/* ---------- counting session ---------- */
.counter{ display:grid;grid-template-columns:1fr 300px;gap:16px;align-items:start }
.counter-head{ grid-column:1/-1;background:var(--paper);border:1px solid var(--rule);padding:14px 18px }
.lbl{ font-size:10.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--ink-soft);font-weight:600 }
.running{ font-size:30px;font-weight:500;margin:4px 0 2px;letter-spacing:-.02em }
.counter-pad{ background:var(--paper);border:1px solid var(--rule);padding:16px 18px 20px }
.seg{ display:flex;gap:6px;margin-bottom:9px }
.seg-btn{
  flex:1;font-family:inherit;font-size:14px;font-weight:600;padding:12px 6px;cursor:pointer;
  border:1px solid var(--rule);background:var(--paper);color:var(--ink-soft);
}
.seg-btn.small{ font-size:12.5px;padding:9px 6px;font-weight:500 }
.seg-btn.on{ background:var(--brand);border-color:var(--brand);color:#fff }
.amount-display{
  width:100%;font-size:38px;text-align:right;padding:12px 14px;margin:10px 0 8px;
  border:1px solid var(--rule);background:var(--ground);letter-spacing:-.02em;
}
.memno{ width:100%;margin-bottom:12px }
.keys{ display:grid;grid-template-columns:repeat(3,1fr);gap:8px }
.key{
  font-family:"IBM Plex Mono",monospace;font-size:22px;padding:16px 0;cursor:pointer;
  border:1px solid var(--rule);background:var(--paper);color:var(--ink);
  /* Thumb-sized on purpose: this is used standing up, one-handed. */
  min-height:56px;
}
.key:active{ background:var(--brand-tint) }
.key.wide{ font-size:18px }
.key.save{ grid-column:span 2;background:var(--brand);border-color:var(--brand);color:#fff;
  font-family:inherit;font-size:16px;font-weight:600 }
.pad-msg{ margin:11px 0 0;font-size:13px;min-height:1.4em }
.pad-msg.ok{ color:var(--brand) }
.pad-msg.bad{ color:var(--flame-text) }
.counter-list{ background:var(--paper);border:1px solid var(--rule);padding:14px 16px }
.counter-list ul{ list-style:none;margin:8px 0 0;padding:0 }
.counter-list li{ display:flex;justify-content:space-between;gap:10px;padding:7px 0;
  border-bottom:1px solid var(--rule-soft);font-size:13px }
.counter-list li em{ color:var(--ink-soft);font-style:normal }

/* ---------- cash tally ---------- */
.tally{ border:1px solid var(--rule-soft);padding:12px 14px;margin:0 0 14px }
.tally legend{ font-size:11.5px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:var(--ink-soft);padding:0 5px }
.den-group-label{ font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-soft);margin:10px 0 7px }
.den-group-label:first-of-type{ margin-top:2px }
.den-grid{ display:flex;flex-direction:column;gap:8px;margin-bottom:2px }
.den-row{ display:flex;align-items:center;gap:10px }
.den-v{ width:52px;text-align:right;font-size:13px;color:var(--ink-soft);flex-shrink:0 }
.den-stepper{ display:flex;align-items:center;gap:8px;flex-shrink:0 }
.den-btn{ width:44px;height:44px;border:1px solid var(--rule);border-radius:50%;background:var(--paper);
  color:var(--brand);font-size:19px;font-weight:600;line-height:1;cursor:pointer;display:flex;
  align-items:center;justify-content:center;transition:transform 100ms ease,background 100ms ease;
  -webkit-tap-highlight-color:transparent;flex-shrink:0;touch-action:manipulation }
.den-btn:active{ transform:scale(.88);background:var(--brand-tint) }
input.den-count{ width:56px;height:40px;text-align:center;border:1px solid var(--rule);font-size:16px;
  font-weight:600;-moz-appearance:textfield;background:var(--paper);color:var(--ink) }
.den-count::-webkit-outer-spin-button,.den-count::-webkit-inner-spin-button{ -webkit-appearance:none;margin:0 }
.den-subtotal{ flex:1;text-align:right;font-size:12.5px;color:var(--ink-soft);min-width:64px;
  font-variant-numeric:tabular-nums }
.den-subtotal.has-count{ color:var(--ink);font-weight:600 }
.tally-sum{ margin:9px 0 0;font-size:12.5px;color:var(--ink-soft) }
.tally-sum b.mismatch{ color:var(--flame-text) }
@media (max-width:420px){
  .den-v{ width:44px;font-size:12px }
  .den-subtotal{ min-width:56px;font-size:11.5px }
}
.big-input{ font-size:22px;padding:11px 13px }
.big{ font-family:"IBM Plex Mono",monospace;font-size:24px;margin:3px 0 0;letter-spacing:-.02em }
.btn.danger{ background:var(--flame);border-color:var(--flame) }
.btn.danger:hover{ background:var(--flame-text);border-color:var(--flame-text) }
.reveal{ animation:rise var(--t-slow) var(--ease) both }

@media (max-width:900px){
  .counter{ grid-template-columns:1fr }
  .tb-find-hint{ display:none }
  .tb-find{ padding:7px 10px }
  .tb-find kbd{ display:none }
}

.cannot{display:flex;gap:9px;align-items:flex-start;padding:11px 13px;margin-bottom:13px;
  background:var(--brand-wash);border:1px solid var(--rule-soft);font-size:12.5px;cursor:pointer}
.cannot input{width:auto;margin-top:2px;flex:0 0 auto}
.cannot em{display:block;font-style:normal;color:var(--ink-soft);font-size:11.5px;margin-top:3px}

/* ==========================================================================
   Modern surfaces.

   Adopted from the reference design: soft radii, low-alpha large-blur
   elevation, 135° gradients, generous container padding, pill-shaped status
   chips. Rebuilt on the crest palette rather than the sample's #1a5a96, and
   with two deliberate departures.

   1. The ledger keeps its density. Rounding a data table is free; padding out
      a thirteen-column financial report read across twelve districts is not —
      it pushes the Total column off a laptop screen, which costs more than the
      airiness gains.
   2. `transition: all` is not used anywhere. Only transform and opacity are
      animated, so the compositor handles them and a cheap Android phone does
      not drop frames scrolling a long report.
   ========================================================================== */

/* ---------- containers ---------- */
.panel{
  border:1px solid var(--panel-ring);
  border-radius:var(--r-lg);
  box-shadow:var(--e-1);
  padding:22px 24px 24px;
}
.panel:hover{ box-shadow:var(--e-2) }
.panel > .hint{ margin-bottom:18px }

/* A hairline of brand gradient along the top edge — what makes a plain white
   card read as part of a designed system.

   Positioned rather than sized with negative margins. Page-local styles set
   their own panel padding and load after this file, so a margin-based bar
   silently overhangs the card wherever the padding differs. Absolute
   positioning is immune to that. */
.panel{ position:relative }
.panel.wide::before,
.kpis::before{
  content:"";position:absolute;top:0;left:0;right:0;height:3px;
  border-radius:var(--r-squircle-lg) var(--r-squircle-lg) 0 0;
  background:var(--grad-accent);opacity:.9;pointer-events:none;
}
.kpis{ position:relative }

/* ---------- the figure strip ---------- */
.kpis{
  border:1px solid var(--panel-ring);
  border-radius:var(--r-squircle-lg);
  box-shadow:var(--e-1);
  overflow:hidden;
  background:var(--paper);
}
.kpi{
  border-right:1px solid var(--rule-soft);
  padding:18px 20px 20px;
  position:relative;
  background:var(--grad-wash);
}
.kpi .val{ font-size:25px }
.kpi.alert{ background:linear-gradient(135deg, var(--flame-tint), transparent 70%) }
.kpi.good{ background:linear-gradient(135deg, var(--brand-tint), transparent 70%) }

/* ---------- sidebar ---------- */
.side{ background:var(--grad-brand) }
.side-item{
  margin:1px 10px;
  padding:9px 12px;
  border-radius:var(--r-md);
  border-left:0;
}
.side-item.on{
  background:rgba(255,255,255,.16);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.10);
}
.side-item.on::before{
  content:"";position:absolute;left:0;width:3px;height:20px;
  border-radius:0 var(--r-sm) var(--r-sm) 0;background:var(--gold);
}
.side-item{ position:relative }
.si-badge{ border-radius:var(--r-pill);padding:1px 7px }
.side-brand{ padding:20px 20px 18px }

/* ---------- controls ---------- */
.btn{ border-radius:var(--r-squircle-sm);padding:10px 20px;min-height:44px;
  display:inline-flex;align-items:center;justify-content:center }
input[type=text],input[type=email],input[type=password],input[type=date],
input[type=number],select,textarea{
  border-radius:var(--r-md);
  padding:10px 12px;
}
input:focus,select:focus,textarea:focus{
  outline:none;
  border-color:var(--brand);
  box-shadow:0 0 0 3px var(--brand-tint);
}
.tb-find{ border-radius:var(--r-pill);padding:8px 15px;min-height:44px;min-width:44px }
.tb-find kbd{ border-radius:var(--r-sm) }
.tb-prefs > summary{ border-radius:var(--r-md) }
.prefs-pop{ border-radius:var(--r-lg);box-shadow:var(--e-3);padding:16px }
.tb-out button{ border-radius:var(--r-pill);padding:7px 14px;min-height:44px }

/* ---------- chips, tags, notices ---------- */
.tag,.chip{ border-radius:var(--r-pill);padding:3px 10px }
.notice{ border-radius:var(--r-md);padding:13px 16px }
.pager a{ border-radius:var(--r-md) }
.cand{ border-radius:var(--r-md) }
.seg-btn{ border-radius:var(--r-md) }
.key{ border-radius:var(--r-md) }
.secret{ border-radius:var(--r-lg) }
.avatar{ box-shadow:var(--e-1) }

/* ---------- progress ---------- */
.tb-track,.confbar,.mtrack,.conc-bar,.fund-bar,.split{
  border-radius:var(--r-pill);
  overflow:hidden;
}
.tb-fill,.confbar i,.mtrack i,.conc-bar i,.fund-bar i{ border-radius:var(--r-pill) }
.tb-fill{ background:var(--grad-accent);opacity:1 }
.fund-bar i,.conc-bar .c1{ background:var(--grad-accent) }

/* ---------- tables: rounded, but not loosened ---------- */
.ledger-wrap,.tscroll,.matrix-wrap{
  border-radius:var(--r-lg);
  border:1px solid var(--panel-ring);
}
.panel .tscroll,.panel .ledger-wrap,.panel .matrix-wrap{ border:0;border-radius:var(--r-md) }
table.matrix .m-cell{ border-radius:3px }
.m-cell:first-of-type{ border-top-left-radius:var(--r-sm) }

/* ---------- palette ---------- */
.pal-card{ border-radius:var(--r-lg);box-shadow:var(--e-3);overflow:hidden }
.pal-list li.on{ border-radius:var(--r-md);margin:0 6px }
.pal-input{ border-radius:0 }

/* ---------- drawer ---------- */
@media (max-width:900px){
  .side{ border-radius:0 var(--r-lg) var(--r-lg) 0 }
  .panel{ padding:18px 16px 20px }
}

/* The page must not scroll behind an open dialog. */
body.modal-open{ overflow:hidden }

/* ==========================================================================
   Display density.

   Resolves a genuine tension rather than choosing a side. The roomier spacing
   suits somebody reading; a treasurer reconciling twelve districts wants the
   tight grid. Compact tightens containers and rows — it never shrinks type
   below the readable, because that is what the text-size setting is for and
   confusing the two would make both worse.
   ========================================================================== */
body.den-compact .panel{ padding:14px 16px 16px }
body.den-compact .content{ padding:14px 16px 50px }
body.den-compact .grid{ gap:10px }
body.den-compact .kpi{ padding:12px 15px 13px }
body.den-compact .kpi .val{ font-size:21px }
body.den-compact table.t td{ padding:5px 7px }
body.den-compact table.ledger tbody td,
body.den-compact table.ledger thead th{ padding:4px 7px }
body.den-compact .panel > .hint{ margin-bottom:11px }
body.den-compact .txn{ padding:12px 14px }
body.den-compact .acct > summary{ padding:9px 12px }
body.den-compact .side-item{ padding:7px 12px }
body.den-compact .fund{ padding:11px 13px }

/* ---------- toasts ---------- */
.toasts{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:70;
  display:flex;flex-direction:column;gap:9px;align-items:center;pointer-events:none}
.toast{display:flex;align-items:center;gap:16px;background:var(--brand-deep);color:#fff;
  padding:12px 16px;border-radius:var(--r-pill);box-shadow:var(--e-3);font-size:13.5px;
  pointer-events:auto;animation:toast-in var(--t-mid) var(--ease) both;max-width:92vw}
.toast.going{animation:toast-out var(--t-fast) var(--ease-in-out) both}
.t-undo{background:none;border:1px solid rgba(255,255,255,.4);color:#fff;font-family:inherit;
  font-size:12.5px;font-weight:600;padding:5px 13px;border-radius:var(--r-pill);cursor:pointer}
.t-undo:hover{background:rgba(255,255,255,.14)}
.t-undo:disabled{opacity:.6;cursor:default}
@keyframes toast-in{from{opacity:0;transform:translate3d(0,14px,0)}to{opacity:1;transform:none}}
@keyframes toast-out{to{opacity:0;transform:translate3d(0,8px,0)}}
:root[data-theme="dark"] .toast{background:#2A2670}

/* Offline queue indicator. Shown wherever the person is, because an entry
   sitting unsent on a device is something they need to know about. */
.queue-badge{display:block;margin-top:5px;font-size:10.5px;color:var(--gold);font-weight:600}
body.is-offline .conn-label::after{content:" — working offline"}
.pending{font-family:"IBM Plex Sans",sans-serif;font-size:10px;color:var(--gold-text);
  text-transform:uppercase;letter-spacing:.06em}

/* ---------------------------------------------------------------------------
   Empty-state grammar: what this page will show, then what to do about it.
   A blank table on a first run is a defect, not a neutral — these two lines
   are the difference between "new" and "broken" in a client's first minutes.
   --------------------------------------------------------------------------- */
.empty .es-what,.empty .es-do{display:block;max-width:52ch;margin:0 auto}
.empty .es-what{font-family:"Source Serif 4",Georgia,serif;font-size:14px;
  line-height:1.55;color:var(--ink);margin-bottom:7px}
.empty .es-do{font-size:12px;line-height:1.55;color:var(--ink-soft)}

/* ---------------------------------------------------------------------------
   Practice-mode banner. Deliberately unmissable: a figure a client might quote
   in a session meeting must never be sample data they forgot was loaded.
   --------------------------------------------------------------------------- */
.practice-bar{background:var(--gold-wash,#fff6dd);border-bottom:2px solid var(--gold-text,#8a6b16);
  color:var(--gold-text,#8a6b16);font-size:12px;padding:7px 18px;text-align:center;
  position:sticky;top:0;z-index:45}
.practice-bar strong{font-weight:700}
.practice-bar a{color:inherit;text-decoration:underline}
@media print{ .practice-bar{position:static;border-bottom:2px solid #000;color:#000} }

/* The season strip: a caption, not a decoration. Ordinary time shows nothing —
   a label that never changes teaches nobody anything. */
.season-strip{padding:7px 18px 0;display:flex;justify-content:flex-end}
@media (max-width:640px){ .season-strip{padding:6px 12px 0;justify-content:flex-start} }

/* KPI accent follows the season; the FIGURE never does. */
.kpi{border-top:2px solid var(--season-accent, var(--brand))}

/* ---------- install-prompt banner ---------- */
.install-banner{
  position:fixed; left:12px; right:12px; bottom:12px; z-index:50;
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  background:var(--brand-plate); color:#fff; padding:12px 16px;
  border-radius:var(--r-squircle-md); box-shadow:var(--e-2);
  font-size:13px; line-height:1.35;
  transform:translateY(140%); opacity:0;
  transition:transform .28s var(--ease,ease-out), opacity .28s var(--ease,ease-out);
}
.install-banner.in{ transform:translateY(0); opacity:1 }
.install-banner-actions{ display:flex; gap:8px; flex-shrink:0 }
.install-banner .btn.tiny{ font-size:12px; padding:7px 14px }
.install-banner .btn.ghost{ color:#fff; border-color:rgba(255,255,255,.35) }
.install-banner .btn.ghost:hover{ background:rgba(255,255,255,.12) }
@media (prefers-reduced-motion: reduce){ .install-banner{ transition:opacity .1s } }
@media (max-width:480px){
  .install-banner{ flex-direction:column; align-items:stretch; text-align:center }
  .install-banner-actions{ justify-content:center }
}
