/* =============================================================================
   brand.css — the ONE place design tokens are defined.

   Load order on every page, no exceptions:
     1. <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css">
     2. <link href="/assets/css/brand.css">
     3. the Google Fonts <link> for IBM Plex Sans Arabic

   Replaces: the vendored Argon fork (index/login/signup), the three separate
   bootstrap@5.3.0 LTR copies (analytics/guardians/reports), bootstrap@5.1.3
   (thank-you), and ~2,400 lines of per-page inline <style>.

   NOTE ON THE RTL BUILD: bootstrap.rtl.min.css is flipped at COMPILE time by
   RTLCSS — it is not direction-aware. Switching documentElement.dir to "ltr"
   at runtime leaves every ms-x, me-x, float-x and dropdown-menu-end rule mirrored.
   The language toggle must therefore swap the stylesheet href as well as the
   dir attribute. See swapBootstrapForLang() in brand.js.
   ========================================================================== */

:root {
  /* ---- BRAND: STEEL BLUE ---------------------------------------------------
     Sampled from assets/logos/charity-02.png. This is the only colour that
     carries both interaction and structure. Ratios are against #ffffff.      */
  --b-50:  #eef3fb;   /* selected row, info banner ground                     */
  --b-100: #d8e4f6;   /* avatar ground, hover on tinted surfaces              */
  --b-200: #b0c8ec;   /* chart fill, sidebar secondary text on navy           */
  --b-300: #7fa5dd;
  --b-400: #5382c6;
  --b-500: #3060a8;   /* LOGO BLUE.        6.24:1 — AA all text, focus ring   */
  --b-600: #27508f;   /* default button.   7.98:1 — AA all text               */
  --b-700: #1e3f73;   /* link text.       10.43:1 — AAA                       */
  --b-800: #172f56;   /* sidebar ground.  white on it = 13.31:1               */
  --b-900: #0f1f39;

  /* ---- BRAND: CYAN ---------------------------------------------------------
     #00d8d8 is 1.78:1 on white. FORBIDDEN as text or icon colour on a light
     ground. Legal: a filled chip with near-black on it (9.79:1), a 3-4px
     accent rule, the active-nav marker, a logo lockup, a chart series.       */
  --c-400: #00d8d8;   /* LOGO CYAN — graphic fills only                       */
  --c-500: #00b0b4;   /* chart series 2                                       */
  --c-700: #046f74;   /* 5.95:1 — the only cyan that may hold text on white   */

  /* ---- ACCENT --------------------------------------------------------------
     Gold is the sidebar count badge and nothing else. Not a button, not a
     link, not a status — statuses have their own colours below.              */
  --gold-300: #e3b457; /* badge fill; #151a25 on it = 9.06:1                  */
  --gold-600: #8e6312; /* 5.33:1 — AA, if the badge ever needs a text form    */

  /* ---- NEUTRALS (blue-tinted, so greys sit with the brand) ---------------- */
  --n-0:   #ffffff;
  --n-25:  #fbfcfd;   /* page ground behind cards                             */
  --n-50:  #f5f7fa;   /* table header, sidebar ground in light mode           */
  --n-100: #eceff4;   /* hover row, hairline inside a card                    */
  --n-200: #dfe4ec;   /* DECORATIVE borders only — 1.28:1, exempt from 1.4.11 */
  --n-300: #c5ccd9;   /* disabled control fill                                */
  --n-400: #868fa2;   /* CONTROL BOUNDARY — 3.25:1, passes WCAG 1.4.11 (3:1).
                         Input/select/checkbox borders MUST use this.         */
  --n-500: #646e87;   /* muted text. 5.09:1 white / 4.75:1 on --n-50 — AA.
                         (Was #6e7890 at 4.42:1, which failed AA on n-50.)    */
  --n-600: #525b70;   /* secondary text. 6.80:1 — AA                          */
  --n-700: #3c4356;   /* body text on tinted surfaces. 9.86:1 — AAA           */
  --n-800: #272d3c;
  --n-900: #151a25;   /* PRIMARY TEXT. 17.41:1 — AAA                          */

  /* ---- STATUS ------------------------------------------------------------- */
  --ok:        #1f7a4d;  --ok-bg:   #e7f5ee;  --ok-text:   #17603c;
  --warn:      #9a6400;  --warn-bg: #fdf3e0;  --warn-text: #7a4f00;
  --bad:       #b3261e;  --bad-bg:  #fbeceb;  --bad-text:  #8c1d17;

  /* ---- CHART ---------------------------------------------------------------
     Validated: guardians vs children ΔE 26.9 deutan / 26.5 tritan / 27.7
     normal. --ch-2 is 2.6:1 on white, so a 2-series chart MUST carry direct
     labels or a table view — which the legend rule requires anyway.          */
  --ch-1: #27508f;    /* guardians / series 1 */
  --ch-2: #00b0b4;    /* children  / series 2 */
  --ch-grid: #dfe4ec;
  --ch-axis: #868fa2;

  /* ---- TYPE ----------------------------------------------------------------
     One family for both scripts, so the ar/en toggle changes the words and
     nothing else. IBM Plex Sans Arabic is what the Saudi DGA's "كود المنصات"
     specifies, and it ships a Latin set derived from IBM Plex Sans.

     Leading floors come from the font's own metrics: sTypoAscender 1085 +
     sTypoDescender 415 = a 1.50em content box, with Arabic ink reaching
     1.612em. Anything under 1.55 overlaps on wrap.                           */
  --font: "IBM Plex Sans Arabic", "IBM Plex Sans", system-ui, sans-serif;

  --fs-display: 34px;  --lh-display: 1.55;
  --fs-h1:      27px;  --lh-h1:      1.6;
  --fs-h2:      21px;  --lh-h2:      1.6;
  --fs-h3:      17px;  --lh-h3:      1.6;
  --fs-body:    15px;  --lh-body:    1.8;
  --fs-sm:      14px;  --lh-sm:      1.75;
  --fs-xs:      13px;  --lh-xs:      1.65;

  /* ---- SPACE / SHAPE ------------------------------------------------------ */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
  --s-5: 20px; --s-6: 24px; --s-7: 32px; --s-8: 44px;

  --r-card: 12px;     /* cards, panels, tables            */
  --r-ctrl: 9px;      /* buttons, inputs, selects         */
  --r-chip: 22px;     /* pills and count badges           */

  --control-h: 44px;  /* every touch target, no exceptions */

  /* Borders, not shadows. One elevation step, used only for overlays. */
  --border: 1px solid var(--n-200);
  --shadow-overlay: 0 12px 32px rgba(21, 26, 37, .16);

  --dur: 160ms;
  --ease: cubic-bezier(.2, 0, .2, 1);

  --sidebar-w: 248px;
  --sidebar-w-rail: 68px;
  --topbar-h: 64px;
}

/* =============================================================================
   BOOTSTRAP BRIDGE
   Bootstrap 5.3 reads most of its chrome from --bs-*. Buttons are the
   exception: button-variant() bakes literal hex, so .btn-primary is restated
   explicitly below.
   ========================================================================== */
:root {
  --bs-body-font-family: var(--font);
  --bs-body-font-size: var(--fs-body);
  --bs-body-line-height: var(--lh-body);
  --bs-body-color: var(--n-900);
  --bs-body-bg: var(--n-25);
  --bs-border-color: var(--n-200);
  --bs-border-radius: var(--r-ctrl);
  --bs-primary: var(--b-600);
  --bs-primary-rgb: 39, 80, 143;
  --bs-link-color: var(--b-700);
  --bs-link-hover-color: var(--b-800);
  --bs-focus-ring-color: var(--b-500);   /* opaque — see :focus-visible */
  --bs-secondary-color: var(--n-600);
}

/* =============================================================================
   BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--n-900);
  background: var(--n-25);
  /* IBM Plex Sans Arabic ships no tnum/lnum features — its Latin figures are
     natively 600-unit monospaced, which is what aligns numeric columns. Any
     font swap must re-verify equal digit advances. */
  font-variant-numeric: lining-nums;
}

/* Arabic needs a slightly larger optical size than Latin at the same nominal
   px. Arabic is the default, so English steps DOWN rather than Arabic up. */
:lang(en) body { font-size: 14px; }

h1, h2, h3, h4, h5, h6 { margin: 0; font-weight: 600; }
h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); }
h2 { font-size: var(--fs-h2); line-height: var(--lh-h2); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-h3); }
p  { margin: 0; }

a { color: var(--b-700); text-decoration: none; }
a:hover { color: var(--b-800); text-decoration: underline; }

/* Arabic is cursive: positive tracking severs the joins, and uppercase is a
   no-op that leaves SHOUTING English beside normal-case Arabic. */
* { letter-spacing: normal; }
.text-uppercase, [class*="text-uppercase"] { text-transform: none !important; }

/* Mixed-direction data. Wrap the VALUE, never the whole cell — a dir=ltr cell
   aligns its text to the physical left, away from its own Arabic header. */
bdi { unicode-bidi: isolate; }

.num {
  text-align: end;
  font-variant-numeric: tabular-nums lining-nums;
}

/* =============================================================================
   FOCUS
   An alpha ring over these grounds measures ~1.58:1 against a 3:1 requirement.
   Opaque, 3px, offset — and a white inner ring so it survives on dark chrome.
   ========================================================================== */
:focus { outline: none; }
:focus-visible {
  outline: 3px solid var(--b-500);
  outline-offset: 2px;
  border-radius: var(--r-ctrl);
}
.sidebar :focus-visible,
.on-dark :focus-visible {
  outline-color: var(--c-400);
  box-shadow: 0 0 0 1px var(--b-800);
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--s-4);
  top: -100px;
  z-index: 100;
  background: var(--n-0);
  color: var(--b-700);
  border: 2px solid var(--b-500);
  border-radius: var(--r-ctrl);
  padding: var(--s-3) var(--s-4);
  font-weight: 600;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--s-4); }

/* =============================================================================
   APP SHELL
   Grid, not float. In RTL the sidebar is column 1 and lands on the right with
   no [dir] override — that is the whole point of logical layout.
   ========================================================================== */
.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
}
.app-shell.is-rail { grid-template-columns: var(--sidebar-w-rail) 1fr; }

.sidebar {
  background: var(--b-800);
  color: var(--n-0);
  display: flex;
  flex-direction: column;
  padding: var(--s-5) 0;
  /* The grid column is as tall as the page, so without this the nav and the
     credit scroll away on any page longer than the viewport. */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.sidebar__brand {
  display: flex; align-items: center; gap: var(--s-3);
  padding: 0 var(--s-5) var(--s-5) var(--s-5);
}
/* The association's mark, white, in a bubble.
   The bubble is --b-700 rather than white: a bright disc this size reads as an app icon
   dropped onto the sidebar, where a lighter shade of the sidebar's own navy reads as part
   of it. The mark stays a flat white silhouette, which is why it needs no light ground to
   be legible in the first place. */
.sidebar__mark {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--b-700);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.sidebar__mark img { width: 24px; height: 24px; object-fit: contain; display: block; }
.sidebar__nav { display: flex; flex-direction: column; gap: 2px; padding: 0 var(--s-3); }

.nav-item {
  display: flex; align-items: center; gap: 11px;
  min-height: var(--control-h);
  padding: 0 var(--s-3);
  border-radius: var(--r-ctrl);
  color: var(--b-100);
  font-size: var(--fs-sm);
  position: relative;
  transition: background var(--dur) var(--ease);
}
.nav-item:hover { background: rgba(255,255,255,.07); color: var(--n-0); text-decoration: none; }
.nav-item[aria-current="page"] {
  background: rgba(0, 216, 216, .14);
  color: var(--n-0);
  font-weight: 600;
}
.nav-item[aria-current="page"]::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 10px; bottom: 10px;
  width: 3px; border-radius: 3px;
  background: var(--c-400);
}
.nav-item__count {
  margin-inline-start: auto;
  background: var(--gold-300);
  color: var(--n-900);
  font-size: 12px; font-weight: 700;
  border-radius: var(--r-chip);
  padding: 1px var(--s-2);
}

.sidebar__credit {
  margin-top: auto;
  padding: var(--s-4) var(--s-5) 0 var(--s-5);
  margin-inline: var(--s-3);
  border-top: 1px solid rgba(255,255,255,.1);
  display: flex; flex-direction: column; gap: var(--s-2);
}
.sidebar__credit-label { font-size: 12px; color: var(--b-200); line-height: var(--lh-body); }
/* The Marahel wordmark is purple and does not read on --b-800. */
.sidebar__credit-chip {
  background: var(--n-0);
  border-radius: var(--s-2);
  padding: 9px var(--s-3);
  align-self: flex-start;
}
.sidebar__credit-chip img { height: 22px; width: auto; display: block; }

.topbar {
  height: var(--topbar-h);
  background: var(--n-0);
  border-bottom: var(--border);
  display: flex; align-items: center; gap: var(--s-4);
  padding: 0 var(--s-7);
}
.topbar__title { font-size: var(--fs-h3); font-weight: 600; }
.page { padding: var(--s-6) var(--s-7); display: flex; flex-direction: column; gap: var(--s-5); }

/* =============================================================================
   CONTROLS
   ========================================================================== */
.btn {
  min-height: var(--control-h);
  border-radius: var(--r-ctrl);
  font-family: var(--font);
  font-size: var(--fs-body);
  font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: 0 var(--s-5);
  border: 1px solid transparent;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
  cursor: pointer;
}
.btn-primary, .btn-primary:focus {
  background: var(--b-600); border-color: var(--b-600); color: var(--n-0);
}
.btn-primary:hover { background: var(--b-700); border-color: var(--b-700); color: var(--n-0); }
.btn-secondary, .btn-outline-secondary {
  background: var(--n-0); border-color: var(--n-400); color: var(--n-700); font-weight: 500;
}
.btn-secondary:hover, .btn-outline-secondary:hover { background: var(--n-50); color: var(--n-900); }
.btn-danger { background: var(--bad); border-color: var(--bad); color: var(--n-0); }
.btn:disabled, .btn.disabled {
  background: var(--n-50); border-color: var(--n-200); color: var(--n-400);
  cursor: not-allowed;
}
.btn-lg { min-height: 50px; font-size: 16px; }

.form-label { font-size: var(--fs-sm); font-weight: 500; margin-bottom: 7px; display: block; }
.form-control, .form-select {
  min-height: var(--control-h);
  border: 1px solid var(--n-400);
  border-radius: var(--r-ctrl);
  padding: 0 var(--s-4);
  font-family: var(--font);
  font-size: var(--fs-body);
  color: var(--n-900);
  background-color: var(--n-0);
}
.form-select {
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 10'%3e%3cpath d='M2 2.5 7 7.5l5-5' fill='none' stroke='%23525b70' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right var(--s-4) center;
  background-size: 14px 10px;
  padding-inline-end: calc(var(--s-4) + 1.5rem);
  cursor: pointer;
}
[dir="rtl"] .form-select { background-position: left var(--s-4) center; }
.form-control::placeholder { color: var(--n-500); }
.form-control:focus, .form-select:focus {
  border-color: var(--b-500);
  box-shadow: none;             /* the :focus-visible ring is the indicator */
}

.badge-status {
  display: inline-flex; align-items: center;
  font-size: var(--fs-xs); font-weight: 600;
  border-radius: 6px; padding: 4px 11px;
}
.badge-ok   { background: var(--ok-bg);   color: var(--ok-text); }
.badge-warn { background: var(--warn-bg); color: var(--warn-text); }
.badge-bad  { background: var(--bad-bg);  color: var(--bad-text); }
.badge-idle { background: var(--n-50);    color: var(--n-700); }

/* =============================================================================
   SURFACES
   ========================================================================== */
.card-flat {
  background: var(--n-0);
  border: var(--border);
  border-radius: var(--r-card);
  padding: var(--s-5) var(--s-6);
}

.data-table { width: 100%; border-collapse: collapse; }
.data-table thead th {
  background: var(--n-50);
  border-bottom: var(--border);
  font-size: var(--fs-xs); font-weight: 600; color: var(--n-700);
  padding: var(--s-3) var(--s-4);
  text-align: start;
}
/* A numeric column's header has to follow its values to the end edge. Without
   this, `.data-table thead th` (more specific) wins over `.num` and the header
   sits at the start of a column whose figures sit at the end. */
.data-table thead th.num { text-align: end; }
.data-table tbody td {
  border-bottom: 1px solid var(--n-100);
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-sm);
}
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: var(--n-50); }

.empty-state {
  border: 1px dashed var(--n-200);
  border-radius: var(--r-card);
  padding: var(--s-7);
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  background: var(--n-25);
  text-align: center;
}

/* =============================================================================
   ACCESSIBILITY / MOTION / PRINT
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Arrows and chevrons point the other way in RTL. [dir] rather than :dir()
   for reach — :dir() is unsupported in older Android WebViews, and one of
   these pages is a public form aimed at mothers on whatever handset they own. */
[dir="rtl"] .icon-mirror { transform: scaleX(-1); }

@media print {
  .sidebar, .topbar, .btn { display: none !important; }
  .card-flat { border: 1px solid #999; break-inside: avoid; }
  body { background: #fff; }
}

@media (max-width: 991.98px) {
  .app-shell, .app-shell.is-rail { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;     /* RIGHT in RTL — the side it lives on */
    width: var(--sidebar-w);
    z-index: 40;
    transform: translateX(100%);   /* off-screen toward the inline-start edge */
    transition: transform var(--dur) var(--ease);
  }
  [dir="ltr"] .sidebar { transform: translateX(-100%); }
  .sidebar.is-open { transform: none; }
  .page { padding: var(--s-5) var(--s-4); }
}

/* =============================================================================
   APP PAGE COMPONENTS
   Added for analytics / guardians / reports / dashboard. These replace the
   per-page inline <style> blocks (533 + 557 + 791 lines) that defined the
   same things three slightly different ways.
   ========================================================================== */

/* The page heading. Replaces the blue-to-teal gradient banner the three newer
   pages each carried — Ehsan-style surfaces are flat, and a gradient band that
   says nothing is the first thing to go. */
.page-head { display: flex; align-items: flex-end; gap: var(--s-4); flex-wrap: wrap; }
.page-head__title { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: 700; }
.page-head__sub { color: var(--n-600); font-size: var(--fs-sm); line-height: var(--lh-sm); }

/* Stat tiles. One shape, one size — the guardians page had a full-width card
   above a 2x2 grid for the same class of number. */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--s-4); }
.stat {
  background: var(--n-0); border: var(--border); border-radius: var(--r-card);
  padding: var(--s-4) var(--s-5); display: flex; flex-direction: column; gap: 2px;
}
.stat__label { font-size: var(--fs-xs); line-height: var(--lh-xs); color: var(--n-600); }
.stat__value { font-size: 24px; line-height: var(--lh-h2); font-weight: 700; font-variant-numeric: tabular-nums; }

/* Segmented control — the report subject switch and the chart grain switch. */
.seg { display: inline-flex; gap: var(--s-2); background: var(--n-50); border-radius: var(--r-ctrl); padding: 4px; }
.seg__opt {
  min-height: 40px; padding: 0 var(--s-4); border: 0; background: transparent;
  border-radius: 7px; font-family: var(--font); font-size: var(--fs-sm); font-weight: 500;
  color: var(--n-700); cursor: pointer;
}
.seg__opt[aria-pressed="true"] { background: var(--b-600); color: var(--n-0); font-weight: 600; }

.chip {
  display: inline-flex; align-items: center; min-height: var(--control-h);
  padding: 0 var(--s-4); border-radius: var(--r-chip);
  border: 1px solid var(--n-200); background: var(--n-0);
  font-size: var(--fs-xs); color: var(--n-700); cursor: pointer;
}
.chip[aria-pressed="true"] { background: var(--b-50); border-color: var(--b-50); color: var(--b-700); font-weight: 600; }

/* Chart card. The plot needs an explicit height or Chart.js grows unbounded. */
.chart-card { background: var(--n-0); border: var(--border); border-radius: var(--r-card); padding: var(--s-5); }
.chart-card__head { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-4); }
.chart-card__body { position: relative; height: 320px; }
.chart-card__body--sm { height: 260px; }

/* Drawer toggle: the sidebar is a fixed drawer under 992px (see the shell
   rules above). Hidden on desktop, where the sidebar is a grid column. */
.drawer-btn { display: none; }
.drawer-scrim { display: none; }
@media (max-width: 991.98px) {
  .drawer-btn { display: inline-flex; }
  .drawer-scrim.is-open {
    display: block; position: fixed; inset: 0; z-index: 30;
    background: rgba(21, 26, 37, .45);
  }
}

/* Detail overlay — used for the guardian record. Not Bootstrap's modal: these
   pages never load Bootstrap's JS bundle, only its CSS. */
.sheet { position: fixed; inset: 0; z-index: 60; display: none; }
.sheet.is-open { display: block; }
.sheet__scrim { position: absolute; inset: 0; background: rgba(21, 26, 37, .45); }
.sheet__panel {
  position: absolute; inset-block: 0; inset-inline-start: 0;
  width: min(560px, 100%); background: var(--n-0);
  box-shadow: var(--shadow-overlay);
  display: flex; flex-direction: column; overflow-y: auto;
}
.sheet__head {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-5) var(--s-6); border-bottom: var(--border); position: sticky; top: 0; background: var(--n-0);
}
.sheet__body { padding: var(--s-5) var(--s-6); display: flex; flex-direction: column; gap: var(--s-5); }

/* Key/value rows inside the detail sheet. */
.kv { display: grid; grid-template-columns: 130px 1fr; gap: var(--s-2) var(--s-4); font-size: var(--fs-sm); }
.kv dt { color: var(--n-600); font-weight: 400; }
.kv dd { margin: 0; color: var(--n-900); }

.table-wrap { background: var(--n-0); border: var(--border); border-radius: var(--r-card); overflow: hidden; }
/* The header's two OUTER corners. These were the other way round - the first cell rounded
   the corner next to the second, the last cell the corner next to the one before it - which
   cut a small notch into the header band between columns on every table. */
.table-wrap .data-table thead th:first-child { border-start-start-radius: var(--r-card); }
.table-wrap .data-table thead th:last-child  { border-start-end-radius: var(--r-card); }

.avatar {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  background: var(--b-50); color: var(--b-700);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: 600;
}

/* =============================================================================
   ACCOUNT MENU — the profile control in the topbar.

   The language switch and sign-out used to sit in the topbar as two more
   outline buttons, which put three or four equally-weighted controls next to
   the page title and made signing out look as routine as adding a user. They
   are account actions, so they live behind the avatar.

   Positioned with inset-inline-end so it hangs from the correct corner in both
   directions without a [dir] override.
   ========================================================================== */
.account { position: relative; flex-shrink: 0; }

.account__btn {
  width: 40px; height: 40px; padding: 0;
  border-radius: 50%;
  border: 1px solid var(--n-400);
  background: var(--n-0);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.account__btn:hover { background: var(--n-50); border-color: var(--n-500); }
.account__btn[aria-expanded="true"] { background: var(--n-50); border-color: var(--b-600); }
.account__btn .avatar { width: 32px; height: 32px; }
/* No name to show: a glyph rather than a placeholder initial. */
.account__btn svg { color: var(--n-700); }

.account__menu {
  position: absolute;
  top: calc(100% + var(--s-2));
  inset-inline-end: 0;
  z-index: 70;
  min-width: 232px;
  background: var(--n-0);
  border: var(--border);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-overlay);
  padding: var(--s-2);
  display: none;
}
.account__menu.is-open { display: block; }

.account__head {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3);
}
.account__name {
  font-size: var(--fs-sm); line-height: var(--lh-sm); font-weight: 600;
  /* A long username must not stretch the menu off the edge of the screen. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 148px;
}
.account__role { font-size: var(--fs-xs); line-height: var(--lh-xs); color: var(--n-600); }

.account__sep { height: 1px; background: var(--n-200); margin: var(--s-2) 0; border: 0; }

.account__item {
  width: 100%;
  min-height: var(--control-h);
  padding: 0 var(--s-3);
  border: 0; border-radius: var(--r-ctrl);
  background: transparent;
  color: var(--n-800);
  font-family: var(--font); font-size: var(--fs-sm); font-weight: 500;
  display: flex; align-items: center; gap: var(--s-3);
  text-align: start;
  cursor: pointer;
  transition: background var(--dur) var(--ease);
}
.account__item:hover, .account__item:focus-visible { background: var(--n-50); }
.account__item svg { flex-shrink: 0; color: var(--n-600); }
.account__item--danger { color: var(--bad); }
.account__item--danger svg { color: var(--bad); }
.account__item--danger:hover, .account__item--danger:focus-visible { background: #fdf3f2; }
/* The current value of a switch-style row, e.g. which language it moves to. */
.account__item-value {
  margin-inline-start: auto;
  font-size: var(--fs-xs); font-weight: 600; color: var(--b-700);
}

@media (max-width: 640px) {
  /* Narrower so the menu still clears the topbar's 32px gutter on a small handset.
     It hangs from the button's inline-end edge and opens inward, so it cannot run
     off the near edge of the screen in either direction. */
  .account__menu { min-width: 208px; }
  .account__name { max-width: 124px; }
}

/* =============================================================================
   DATA TABLE: TOOLBAR, SELECTION, PAGINATION
   The old register was a 22-column horizontally-scrolling grid with the row
   number in the first cell, coloured "Activate"/"Deactivate" buttons floating
   above it, and Argon's indigo pagination pills. This is the replacement.
   ========================================================================== */

.toolbar {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
}
.toolbar__search {
  flex-grow: 1; min-width: 240px; max-width: 520px;
  display: flex; align-items: center; gap: var(--s-3);
  background: var(--n-0); border: 1px solid var(--n-400);
  border-radius: var(--r-ctrl); padding-inline: var(--s-4); height: var(--control-h);
}
.toolbar__search:focus-within { border-color: var(--b-500); }
.toolbar__search input {
  flex-grow: 1; border: 0; outline: none; background: transparent;
  font-family: var(--font); font-size: var(--fs-body); color: var(--n-900); min-width: 0;
}
.toolbar__search input::placeholder { color: var(--n-500); }

/* Selection is announced by a bar that replaces the toolbar, rather than by
   two coloured buttons that are always present and usually do nothing. */
.bulkbar {
  display: none; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  background: var(--b-50); border: 1px solid var(--b-100);
  border-radius: var(--r-ctrl); padding: var(--s-2) var(--s-4); min-height: var(--control-h);
}
.bulkbar.is-shown { display: flex; }
.bulkbar__count { font-weight: 600; font-size: var(--fs-sm); color: var(--b-700); }

.data-table tbody tr.is-selected { background: var(--b-50); }
.data-table td.cell-select, .data-table th.cell-select { width: 44px; padding-inline-end: 0; }

/* A native checkbox, restyled — no icon font, no plugin. */
.check {
  appearance: none; -webkit-appearance: none;
  width: 18px; height: 18px; border-radius: 5px;
  border: 1.5px solid var(--n-400); background: var(--n-0);
  cursor: pointer; display: inline-grid; place-content: center; flex-shrink: 0;
}
.check::before {
  content: ""; width: 10px; height: 10px; transform: scale(0);
  transition: transform var(--dur) var(--ease);
  box-shadow: inset 1em 1em var(--n-0);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.check:checked, .check:indeterminate { background: var(--b-600); border-color: var(--b-600); }
.check:checked::before { transform: scale(1); }
.check:indeterminate::before { transform: scale(1); clip-path: polygon(15% 45%, 85% 45%, 85% 58%, 15% 58%); }

/* Pagination: text and a border, never a filled indigo pill. */
.pager { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.pager__info { font-size: var(--fs-sm); color: var(--n-600); font-variant-numeric: tabular-nums; }
.pager__btns { display: flex; gap: var(--s-2); margin-inline-start: auto; }
.pager__btn {
  min-width: var(--control-h); height: var(--control-h);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding-inline: var(--s-3);
  border: 1px solid var(--n-400); border-radius: var(--r-ctrl);
  background: var(--n-0); color: var(--n-700);
  font-family: var(--font); font-size: var(--fs-sm); font-weight: 500; cursor: pointer;
}
.pager__btn:hover:not(:disabled) { background: var(--n-50); color: var(--n-900); }
.pager__btn:disabled { color: var(--n-300); border-color: var(--n-200); cursor: not-allowed; }

/* A status pill that is also the control. Replaces the pair of red/green
   buttons: the row states what it is, and the switch changes it. */
.switch { display: inline-flex; align-items: center; gap: var(--s-2); cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch__track {
  width: 38px; height: 22px; border-radius: 999px; background: var(--n-300);
  position: relative; transition: background var(--dur) var(--ease); flex-shrink: 0;
}
.switch__track::after {
  content: ""; position: absolute; top: 3px; inset-inline-start: 3px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--n-0);
  transition: transform var(--dur) var(--ease);
}
.switch input:checked + .switch__track { background: var(--ok); }
[dir="rtl"] .switch input:checked + .switch__track::after { transform: translateX(-16px); }
[dir="ltr"] .switch input:checked + .switch__track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch__track { outline: 3px solid var(--b-500); outline-offset: 2px; }
/* A switch whose subject can no longer change - a one-off schedule whose date has gone.
   It keeps its place so the row still reads like its neighbours, but it must not look
   live: --n-300 is the disabled-control fill, so the track stays grey even when the
   underlying checkbox is checked. */
.switch.is-locked { cursor: default; }
.switch.is-locked .switch__track,
.switch.is-locked input:checked + .switch__track { background: var(--n-300); }

/* =============================================================================
   DIALOG
   A real <dialog>: the browser handles the backdrop, focus trap, Escape and
   inertness. Replaces seven Bootstrap modals driven by a jQuery plugin.
   ========================================================================== */
dialog.sheet-dialog {
  border: 0; padding: 0; border-radius: var(--r-card);
  width: min(640px, calc(100vw - 2 * var(--s-5)));
  max-height: calc(100vh - 2 * var(--s-6));
  background: var(--n-0); color: var(--n-900);
  box-shadow: var(--shadow-overlay);
  overflow: visible;
}
dialog.sheet-dialog::backdrop { background: rgba(21, 26, 37, .45); }
dialog.sheet-dialog[data-size="wide"] { width: min(880px, calc(100vw - 2 * var(--s-5))); }
.dlg { display: flex; flex-direction: column; max-height: calc(100vh - 2 * var(--s-6)); }
.dlg__head {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-5) var(--s-6); border-bottom: var(--border); flex-shrink: 0;
}
.dlg__title { font-size: var(--fs-h3); line-height: var(--lh-h3); font-weight: 600; }
.dlg__body { padding: var(--s-5) var(--s-6); overflow-y: auto; display: flex; flex-direction: column; gap: var(--s-4); }
.dlg__foot {
  display: flex; gap: var(--s-3); justify-content: flex-end;
  padding: var(--s-4) var(--s-6); border-top: var(--border); flex-shrink: 0;
}

/* Form layout inside a dialog. */
.fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--s-4); }
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field--wide { grid-column: 1 / -1; }
.field__error { font-size: var(--fs-xs); color: var(--bad-text); display: none; }
.field.is-invalid .form-control, .field.is-invalid .form-select { border-color: var(--bad); }
.field.is-invalid .field__error { display: block; }

/* =============================================================================
   TOAST
   Replaces 20 $('#x').toast('show') calls and the jQuery plugin bridge.
   ========================================================================== */
.toast-stack {
  position: fixed; z-index: 80;
  inset-block-end: var(--s-5); inset-inline-start: var(--s-5);
  display: flex; flex-direction: column; gap: var(--s-2);
}
.toast-item {
  display: flex; align-items: flex-start; gap: var(--s-3);
  min-width: 280px; max-width: 420px;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-ctrl); border: 1px solid var(--n-200);
  background: var(--n-0); box-shadow: var(--shadow-overlay);
  font-size: var(--fs-sm); line-height: var(--lh-sm);
}
.toast-item--ok   { border-inline-start: 4px solid var(--ok);   }
.toast-item--bad  { border-inline-start: 4px solid var(--bad);  }
.toast-item--warn { border-inline-start: 4px solid var(--warn); }

/* A section heading with an action on the far side. */
.sec-head { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
.sec-head__title { font-size: var(--fs-h3); line-height: var(--lh-h3); font-weight: 600; }
.sec-head__end { margin-inline-start: auto; display: flex; gap: var(--s-3); align-items: center; }

/* Share/proportion meter, used by the analytics tables. */
.meter { height: 6px; border-radius: 3px; background: var(--n-100); overflow: hidden; min-width: 60px; }
.meter__fill { height: 100%; background: var(--b-500); border-radius: 3px; }

.chart-card__body--xs { height: 200px; }

/* A square, borderless button for an icon alone — dialog close, row actions. */
.btn-icon {
  width: 34px; height: 34px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--r-ctrl); background: transparent;
  color: var(--n-600); cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.btn-icon:hover { background: var(--n-100); color: var(--n-900); }
.btn-icon--danger:hover { background: var(--bad-bg); color: var(--bad-text); }

/* Row actions live at the end of a row and stay quiet until the row is hovered
   — the old table put four coloured buttons in every row, permanently. */
.row-actions { display: flex; gap: var(--s-1); justify-content: flex-end; }
.data-table tbody tr .row-actions .btn-icon { opacity: .55; }
.data-table tbody tr:hover .row-actions .btn-icon,
.data-table tbody tr:focus-within .row-actions .btn-icon { opacity: 1; }

/* Two-column page split: a main region and a narrower aside. */
.split { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--s-5); align-items: start; }
@media (max-width: 991.98px) { .split { grid-template-columns: minmax(0, 1fr); } }

/* A definition row inside a detail sheet. */
.kv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--s-4); }

/* =============================================================================
   FORM GROUPS
   A record that describes two different people — the guardian and the child —
   was being presented as one undifferentiated grid of thirteen inputs, which
   is what made it read as a database row rather than a form.
   ========================================================================== */
.fgroup { border: 0; padding: 0; margin: 0; min-width: 0; }
.fgroup + .fgroup { margin-top: var(--s-6); }
.fgroup__legend {
  display: flex; align-items: center; gap: var(--s-3);
  width: 100%; padding: 0 0 var(--s-3) 0; margin-bottom: var(--s-4);
  border-bottom: var(--border);
  font-size: var(--fs-body); line-height: var(--lh-h3); font-weight: 600;
  color: var(--n-900);
}
.fgroup__legend svg { color: var(--b-600); flex-shrink: 0; }
.fgroup__hint {
  margin-inline-start: auto; font-weight: 400;
  font-size: var(--fs-xs); line-height: var(--lh-xs); color: var(--n-600);
}

/* Helper text under a control: the expected format, or an echo of what the
   browser's own date widget is actually showing. */
.field__hint { font-size: var(--fs-xs); line-height: var(--lh-xs); color: var(--n-600); }
.field__hint strong { color: var(--n-900); font-weight: 600; }

/* Required is marked once, visibly, rather than left for the user to discover
   on submit. Optional fields say so in their label. */
.form-label .req { color: var(--bad); margin-inline-start: 2px; }

/* Referenced by three pages' .page-head but never defined, so the primary
   page action sat against the subtitle instead of at the far end. */
.page-head__actions {
  display: flex; align-items: center; gap: var(--s-3);
  flex-wrap: wrap; margin-inline-start: auto;
}

/* .quick:hover asked for this token, which did not exist, so the hover
   elevation never rendered. */
:root { --shadow-card: 0 1px 2px rgba(21, 26, 37, .06), 0 6px 16px rgba(21, 26, 37, .08); }
