  /* ────────────────────────────────────────────────────────────────────────
     v1.4.0 Rynox design system
     Brand: graphite + glowing cyan accent (#00B6FB used only for glow / focus)
     Theme: light/dark via [data-theme] attribute on <html>.

     This file provides ONLY:
       - CSS variables
       - Base typography
       - Reusable components (.btn, .badge, .card, .alert, table styles, inputs)
       - The .sidebar / .nav-item / .sb-* classes used by _sidebar.html
       - .admin-layout / .main / .topbar / .content layout primitives
     It does NOT render any layout markup. Pages render their own
     <div class="admin-layout">...</div> and include _sidebar.html themselves
     (matches the contract from the v1.3.x base.html).
     ──────────────────────────────────────────────────────────────────────── */
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

  :root {
    /* Brand: cyan as accent ONLY. Not used as page background, not used for
       large stat numbers, not used for category pills. Reserved for: glow on
       focus, active nav item left-border, brand mark, small interactive accents. */
    --brand:           #00B6FB;
    --brand-strong:    #00A4F4;
    --brand-bright:    #00D4FF;
    --brand-soft:      rgba(0, 182, 251, 0.08);
    --brand-glow:      rgba(0, 212, 255, 0.4);

    --success: #16a34a;
    --warning: #d97706;
    --danger:  #dc2626;
    --info:    #0284c7;

    --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;
    --radius:    8px;
    --radius-sm: 6px;
    --radius-lg: 12px;
  }

  /* ── Light theme — neutral graphite with cyan as accent only ─────────── */
  html[data-theme="light"] {
    --bg:             #f4f5f7;
    --surface:        #ffffff;
    --surface-2:      #f7f8fa;
    --border:         #e5e7eb;
    --border-strong:  #d1d5db;
    --text:           #111827;
    --text-2:         #374151;
    /* ⚠ v1.7.310 (item 2) — v1.7.309's D3, fixed. `--muted` `#6b7280` measured 4.43:1
       against --bg #f4f5f7, under the 4.5:1 floor; this is the owner hub's
       secondary text in the theme he uses every day. `#666e7b` clears it
       on every bed this shell puts it on. The 309 WCAG validator now
       runs against BOTH shells, so it cannot come back. */
    --muted:          #666e7b;
    --hover:          #f3f4f6;
    --sidebar:        #ffffff;
    --sidebar-border: #e5e7eb;
    --accent:         #4b5563;   /* graphite, used where cyan was overused */
    --shadow-sm:      0 1px 2px rgba(0,0,0,.04);
    --shadow:         0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);

    /* Back-compat aliases for v1.3.x templates: */
    --primary:        var(--accent);
    --primary-dark:   #1f2937;
    --primary-light:  #e5e7eb;
    --card:           var(--surface);
    --color-card:     var(--surface);
    --color-bg:       var(--bg);
    --color-text:     var(--text);
    --color-muted:    var(--muted);
    --color-border:   var(--border);
    --sidebar-bg:     var(--sidebar);
    --sidebar-text:   var(--text-2);
    --sidebar-active: var(--brand);

    /* v1.5.4.5: tame --brand-glow in light mode. The default value (set
       in the root block for dark mode) is a strong cyan halo that looks
       great around a white logo on a dark sidebar but washes out the
       dark-navy logo on a white sidebar — the body fades and only the
       pale-blue horn reads clearly. A near-transparent glow keeps the
       brand-accent intent without smudging the mark. */
    --brand-glow:     rgba(0, 182, 251, 0.10);

    /* ── v1.7.245 · the semantic sheet, completed ──────────────────────
       This tier had FOUR status tokens (--success/--warning/--danger/
       --info) and nothing else, so every page that needed a status BED
       or a status EDGE invented one: #fef3c7, #dcfce7, #fee2e2, #92400e
       and forty near-duplicates of each. The portal tier has carried
       four slots per family since v1.7.235; this is the same vocabulary,
       the same hues, re-tuned for SOLID surfaces instead of glass (the
       tint alphas are lower here because there is no 72%-opaque frost
       between the tint and the eye).

         -text  the ink        -chip  a chip / pill / small bed
         -bed   a panel or banner bed (weaker; large areas read stronger)
         -edge  the border round either
         -fill  a solid fill that carries WHITE ink

       --on-accent / --on-primary exist so no page has to guess. The
       house rule (never white text on the cyan accent) is a token here,
       not a convention. */
    --accent-text:    #005E80;   /* readable cyan ink — 6.24:1 on white */
    --on-accent:      #0B1220;   /* never white on cyan (2.31:1) */
    --on-primary:     #ffffff;   /* --primary is graphite here — white reads 7.4:1 */
    --focus-ring:     rgb(0 146 200 / .35);
    --ok-text:#0A5E3E;      --ok-chip:rgb(16 140 90 / .16);
    --ok-bed:rgb(16 140 90 / .12);   --ok-edge:rgb(15 92 62 / .40);
    --ok-fill:#0F7A50;
    --warn-text:#7A4200;    --warn-chip:rgb(180 83 9 / .16);
    --warn-bed:rgb(180 83 9 / .12);  --warn-edge:rgb(138 75 0 / .40);
    --warn-fill:#B45309;
    --danger-text:#A3170C;  --danger-chip:rgb(239 76 44 / .16);
    --danger-bed:rgb(230 66 22 / .12); --danger-edge:rgb(192 40 28 / .42);
    --danger-fill:#C0281C;
    --info-text:#005E80;    --info-chip:rgb(0 146 200 / .14);
    --info-bed:rgb(0 146 200 / .10); --info-edge:rgb(0 118 159 / .38);
    --info-fill:#00769F;
    /* ── v1.7.309 (item 1) — INK ON A SOLID STATUS FILL ────────────────
       The twin of the block in portal_base.html, and it has to be a twin
       because the bulk-action bar is defined TWICE — here for the owner
       and in portal_submissions.html for the company — and
       tests/test_v17221_bulk_bar.py exists to stop those two drifting.
       The five family slots never included the ink a coloured BUTTON
       needs, so every solid button guessed `#fff`. Measured 2026-08-31:
       white on #D97706 is 3.19:1, white on #16A34A is 3.30:1. Light:
       white clears every fill here (danger 5.90, ok 5.36, warn 5.02,
       info 5.13). SEND is an identity, not a status — see portal_base. */
    --on-danger:#FFFFFF; --on-ok:#FFFFFF;
    --on-warn:#FFFFFF;   --on-info:#FFFFFF;
    --send-fill:#7C3AED; --on-send:#FFFFFF;
    /* v1.7.245: the OK family under the name pages already say. Both
       bases answer to the same four slots so a shared partial cannot
       land on a token that exists on one tier only. */
    --success-text: var(--ok-text);  --success-chip: var(--ok-chip);
    --success-bed: var(--ok-bed);    --success-edge: var(--ok-edge);
    color-scheme: light;   /* v1.7.541 (batch 198): see the dark block */
  }

  /* ── Dark theme — deep navy/graphite with cyan as glow accent only ───── */
  html[data-theme="dark"] {
    --bg:             #0b0e14;
    --surface:        #11151d;
    --surface-2:      #161b25;
    --border:         #1e2230;
    --border-strong:  #2a2f3e;
    --text:           #e5e7eb;
    --text-2:         #b3b9c5;
    /* ⚠ v1.7.310 (item 2) — v1.7.309's D3, fixed. `--muted` `#6b7280` measured 3.57:1
       against --surface-2 #161b25, under the 4.5:1 floor; this is the owner hub's
       secondary text in the theme he uses every day. `#838a97` clears it
       on every bed this shell puts it on. The 309 WCAG validator now
       runs against BOTH shells, so it cannot come back. */
    --muted:          #838a97;
    --hover:          #1a1f2a;
    --sidebar:        #0d1117;
    --sidebar-border: #1a1f2a;
    --accent:         #9ca3af;   /* graphite text accent */
    --shadow-sm:      0 1px 2px rgba(0,0,0,.3);
    --shadow:         0 1px 3px rgba(0,0,0,.4), 0 2px 6px rgba(0,0,0,.2);
    --brand-soft:     rgba(0, 182, 251, 0.10);

    --primary:        var(--accent);
    --primary-dark:   #d1d5db;
    --primary-light:  #374151;
    --card:           var(--surface);
    --color-card:     var(--surface);
    --color-bg:       var(--bg);
    --color-text:     var(--text);
    --color-muted:    var(--muted);
    --color-border:   var(--border);
    --sidebar-bg:     var(--sidebar);
    --sidebar-text:   var(--text-2);
    --sidebar-active: var(--brand);

    /* v1.7.245 · the semantic sheet, dark. Same families, same hues,
       inks lightened to clear 4.5:1 on --surface (#11151d) rather than
       on white. Values are the portal tier's dark inks (v1.7.235) —
       one house palette, two tiers, not two palettes. */
    --accent-text:    #4FD1FF;   /* 6.18:1 */
    --on-accent:      #06121A;
    --on-primary:     #0b0e14;   /* --primary is a LIGHT graphite here */
    --focus-ring:     rgb(79 209 255 / .45);
    --ok-text:#86E4BC;      --ok-chip:rgb(47 176 130 / .20);
    --ok-bed:rgb(47 176 130 / .14);  --ok-edge:rgb(47 176 130 / .45);
    --ok-fill:#12704E;
    --warn-text:#F7CB86;    --warn-chip:rgb(232 166 77 / .20);
    --warn-bed:rgb(232 166 77 / .14); --warn-edge:rgb(232 166 77 / .45);
    --warn-fill:#8A5A12;
    --danger-text:#FF8A80;  --danger-chip:rgb(239 78 40 / .22);
    --danger-bed:rgb(239 80 38 / .16); --danger-edge:rgb(239 76 44 / .55);
    --danger-fill:#B01F12;
    --info-text:#4FD1FF;    --info-chip:rgb(17 184 246 / .18);
    --info-bed:rgb(17 184 246 / .12); --info-edge:rgb(79 209 255 / .45);
    --info-fill:#0FA2E0;
    /* v1.7.309 (item 1) — see the LIGHT block. Three take white (danger
       6.88, ok 6.08, warn 5.91); --info-fill is the bright cyan and
       white on it is 2.89:1, so it takes the near-black — 6.55:1. */
    --on-danger:#FFFFFF; --on-ok:#FFFFFF;
    --on-warn:#FFFFFF;   --on-info:#06121A;
    --send-fill:#7C3AED; --on-send:#FFFFFF;
    /* v1.7.245: the OK family under the name pages already say. Both
       bases answer to the same four slots so a shared partial cannot
       land on a token that exists on one tier only. */
    --success-text: var(--ok-text);  --success-chip: var(--ok-chip);
    --success-bed: var(--ok-bed);    --success-edge: var(--ok-edge);
    /* v1.7.541 (batch 198): THE NATIVE CONTROLS FOLLOW THE THEME. This shell never said
       `color-scheme`, so it computed `normal` (light) under the dark theme: Chrome drew a
       native <select> menu LIGHT while the options inherited this theme's near-white
       --text (the owner's "Part of" menu — only the highlighted option readable).
       portal.css has said it on both theme blocks since v1.7.235; now this file does too. */
    color-scheme: dark;
  }

  /* ── SERIOUS · the graphite version (v1.7.240) ──────────────────────
     The owner hub and every tier that extends this file (AM, TM, HR,
     the manager queue, settings, the flat activity page) get the same
     two-version choice the portal tier got, implemented the same way: a
     REMAP of this file's own custom properties onto the graphite family,
     nothing else touched.

     One attribute more than the block it overrides — (0,2,1) against
     (0,1,1) for the theme blocks and (0,1,0) for :root — so it wins
     without !important. With the attribute at "colorful" (or absent) not
     one rule here matches and every token above computes exactly as
     v1.7.239 shipped it.

     This tier is FLAT, not glass, so the remap is to the graphite
     family's solids rather than to its glass alphas: the same palette,
     worn the way this tier wears colour. */

  html[data-rx-version="serious"][data-theme="dark"] {
    --bg:             #040405;
    --surface:        #16181C;
    --surface-2:      #1D2025;
    --border:         #262A30;
    --border-strong:  #3A3E45;
    --text:           #F3F4F6;
    --text-2:         #A7ADB7;
    /* ⚠ v1.7.310 (item 2) — v1.7.309's D3, fixed. `--muted` `#7F8792` measured 4.50:1
       against --surface-2 #1D2025, under the 4.5:1 floor; this is the owner hub's
       secondary text in the theme he uses every day. `#858d99` clears it
       on every bed this shell puts it on. The 309 WCAG validator now
       runs against BOTH shells, so it cannot come back. */
    --muted:          #858d99;
    --hover:          #202329;
    --sidebar:        #0F1114;
    --sidebar-border: #212429;
    --accent:         #7E8B99;   /* steel — replaces the graphite accent */
    --shadow-sm:      0 1px 2px rgba(0,0,0,.40);
    --shadow:         0 1px 3px rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.35);
    /* brand: the utility cyan-steel, graphic only — never a page ground */
    --brand:          #6FA8C4;
    --brand-strong:   #5A93AF;
    --brand-bright:   #7CB8D6;
    --brand-soft:     rgba(124, 184, 214, 0.10);
    --brand-glow:     rgba(124, 184, 214, 0.22);
    /* status stays semantic, quietly */
    --success:        #5AAA87;
    --warning:        #C09550;
    --danger:         #B0505F;
    --info:           #5A93AF;
    --primary-dark:   #D5DAE0;
    --primary-light:  #333A42;
    /* v1.7.245 · the semantic sheet, Serious dark. Same slots, drained
       of chroma to match the tier: the portal's Serious values, verbatim. */
    --accent-text:    #7CB8D6;
    --on-accent:      #08131A;
    --on-primary:     #08131A;
    --focus-ring:     rgb(124 184 214 / .40);
    --ok-text:#A3CFBB;      --ok-chip:rgb(90 170 135 / .14);
    --ok-bed:rgb(90 170 135 / .12);  --ok-edge:rgb(90 170 135 / .42);
    --ok-fill:#1C5741;
    --warn-text:#E8CB9E;    --warn-chip:rgb(210 155 80 / .14);
    --warn-bed:rgb(210 155 80 / .12); --warn-edge:rgb(210 155 80 / .42);
    --warn-fill:#6A4A1C;
    --danger-text:#F0AFB9;  --danger-chip:rgb(225 90 110 / .14);
    --danger-bed:rgb(225 90 110 / .12); --danger-edge:rgb(225 90 110 / .45);
    --danger-fill:#7E2130;
    --info-text:#7CB8D6;    --info-chip:rgb(124 184 214 / .14);
    --info-bed:rgb(124 184 214 / .12); --info-edge:rgb(124 184 214 / .42);
    --info-fill:#5A93AF;
    /* v1.7.309 — the SERIOUS theme drains colour, so "violet sends" is a
       graphite-violet. Only the RAW value is restated; the four --on-*
       inks come through from the colorful block and measure 5.57-9.78
       against these fills. */
    /* v1.7.309 (item 1) — RESTATED IN FULL; see the serious-LIGHT
       block. Three take white (9.78 / 8.44 / 8.05); --info-fill is the
       pale steel #5A93AF and white on it is 3.37:1, so it takes the
       near-black at 5.61:1 — the same side the colorful dark block
       lands on, for the same reason. */
    --on-danger:#FFFFFF; --on-ok:#FFFFFF;
    --on-warn:#FFFFFF;   --on-info:#06121A;
    --send-fill:#544A6B; --on-send:#FFFFFF;   /* white ink 8.18:1 */
    /* v1.7.245: the OK family under the name pages already say. Both
       bases answer to the same four slots so a shared partial cannot
       land on a token that exists on one tier only. */
    --success-text: var(--ok-text);  --success-chip: var(--ok-chip);
    --success-bed: var(--ok-bed);    --success-edge: var(--ok-edge);
  }

  html[data-rx-version="serious"][data-theme="light"] {
    --bg:             #F2F3F5;
    --surface:        #FFFFFF;
    --surface-2:      #F4F5F7;
    --border:         #E1E3E8;
    --border-strong:  #CBCFD6;
    --text:           #14171C;
    --text-2:         #3A414B;
    /* ⚠ v1.7.310 (item 2) — v1.7.309's D3, fixed. `--muted` `#6B727C` measured 4.38:1
       against --bg #F2F3F5, under the 4.5:1 floor; this is the owner hub's
       secondary text in the theme he uses every day. `#626973` clears it
       on every bed this shell puts it on. The 309 WCAG validator now
       runs against BOTH shells, so it cannot come back. */
    --muted:          #626973;
    --hover:          #EAEBEF;
    --sidebar:        #FFFFFF;
    --sidebar-border: #E1E3E8;
    --accent:         #5C6672;
    --shadow-sm:      0 1px 2px rgba(17,19,24,.05);
    --shadow:         0 1px 3px rgba(17,19,24,.07), 0 1px 2px rgba(17,19,24,.05);
    --brand:          #3F7A96;
    --brand-strong:   #33657E;
    --brand-bright:   #2C5B72;
    --brand-soft:     rgba(63, 122, 150, 0.08);
    --brand-glow:     rgba(63, 122, 150, 0.10);
    --success:        #1D5A44;
    --warning:        #7C4A12;
    --danger:         #9B2233;
    --info:           #2C5B72;
    --primary-dark:   #2A3038;
    --primary-light:  #E1E3E8;
    /* v1.7.245 · the semantic sheet, Serious light. */
    --accent-text:    #2C5B72;
    --on-accent:      #0D1114;
    --on-primary:     #FFFFFF;
    --focus-ring:     rgb(63 122 150 / .32);
    --ok-text:#1D5A44;      --ok-chip:rgb(40 130 95 / .13);
    --ok-bed:rgb(40 130 95 / .10);   --ok-edge:rgb(29 90 68 / .36);
    --ok-fill:#1D5A44;
    --warn-text:#7C4A12;    --warn-chip:rgb(170 110 30 / .14);
    --warn-bed:rgb(170 110 30 / .11); --warn-edge:rgb(124 74 18 / .36);
    --warn-fill:#7C4A12;
    --danger-text:#9B2233;  --danger-chip:rgb(190 60 75 / .12);
    --danger-bed:rgb(190 60 75 / .10); --danger-edge:rgb(155 34 51 / .38);
    --danger-fill:#9B2233;
    --info-text:#2C5B72;    --info-chip:rgb(63 122 150 / .13);
    --info-bed:rgb(63 122 150 / .10); --info-edge:rgb(44 91 114 / .36);
    --info-fill:#33657E;
    /* v1.7.309 — see the serious-DARK block. */
    /* v1.7.309 (item 1) — RESTATED IN FULL, because v1.7.240's
       completeness contract is that SERIOUS restates every raw token
       the colorful block sets, and it is right: a token that inherits
       silently is a token nobody measured against the fill it lands on
       HERE. These four fills are darker than the colorful ones, so all
       four take white — measured 7.86 / 8.08 / 7.38 / 6.36. */
    --on-danger:#FFFFFF; --on-ok:#FFFFFF;
    --on-warn:#FFFFFF;   --on-info:#FFFFFF;
    --send-fill:#544A6B; --on-send:#FFFFFF;   /* white ink 8.18:1 */
    /* v1.7.245: the OK family under the name pages already say. Both
       bases answer to the same four slots so a shared partial cannot
       land on a token that exists on one tier only. */
    --success-text: var(--ok-text);  --success-chip: var(--ok-chip);
    --success-bed: var(--ok-bed);    --success-edge: var(--ok-edge);
  }

  /* Serious is still on this tier too. The owner dashboard's CSS-only
     dust (dashboard.html .rx-particles, v1.7.61) and the hub's aurora
     backdrop (hub.html .hub-aurora, v1.7.60) are the ambient layer here,
     and they go with the colour. Suppressed centrally so a new ambient
     element on this tier only ever has to be named once. */
  html[data-rx-version="serious"] .rx-particles,
  html[data-rx-version="serious"] .hub-aurora { display: none !important; }

  body {
    font-family: var(--font);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-body);
    line-height: 1.5;
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  h1, h2, h3, h4, h5, h6 { font-weight: var(--font-weight-head); letter-spacing: -0.01em; color: var(--text); }
  h1 { font-size: var(--font-size-h1); letter-spacing: -0.02em; }
  h2 { font-size: var(--font-size-h2); }
  small, .small-text, label { font-size: var(--font-size-sm); }
  h3 { font-size: 14px; }
  a { color: var(--brand); text-decoration: none; }
  a:hover { text-decoration: underline; }
  code, pre { font-family: var(--font-mono); font-size: 13px; }
  hr { border: none; border-top: 1px solid var(--border); margin: 20px 0; }

  /* ── Layout primitives (pages instantiate these themselves) ─────────── */
  .admin-layout { display: flex; min-height: 100vh; min-height: 100dvh; }
  .sidebar {
    width: 240px; flex-shrink: 0;
    background: var(--sidebar);
    border-right: 1px solid var(--sidebar-border);
    display: flex; flex-direction: column;
    position: sticky; top: 0; height: 100vh; height: 100dvh; overflow-y: auto;
  }
  .main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
  .topbar {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 14px 28px;
    display: flex; align-items: center; gap: 12px;
    min-height: 56px;
  }
  .topbar h1 { font-size: 16px; font-weight: 600; flex: 1; margin: 0; }
  .content { padding: 28px; flex: 1; overflow-y: auto; }

  /* ── Sidebar inner styles ────────────────────────────────────────────── */
  .sb-brand {
    padding: 16px 18px 14px;
    display: flex; align-items: center; gap: 10px;
    border-bottom: 1px solid var(--sidebar-border);
  }
  .sb-brand-mark-img {
    flex-shrink: 0;
    object-fit: contain;
    filter: drop-shadow(0 0 6px var(--brand-glow));
  }
  .sb-brand-text {
    font-size: 15px; font-weight: 700; letter-spacing: -0.01em;
    color: var(--text);
  }

  .sb-nav { padding: 12px 8px; flex: 1; overflow-y: auto; }
  .sb-section { margin-bottom: 8px; }
  .sb-section-title {
    font-size: 11px; font-weight: 600; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.06em;
    padding: 8px 12px 4px;
  }
  .sb-item {
    display: flex; align-items: center; gap: 10px;
    padding: 7px 12px;
    border-radius: var(--radius-sm);
    font-size: 13px; font-weight: 500;
    color: var(--text-2);
    text-decoration: none;
    cursor: pointer;
    border-left: 2px solid transparent;
  }
  .sb-item:hover { background: var(--hover); color: var(--text); text-decoration: none; }
  .sb-item.active {
    background: var(--brand-soft);
    color: var(--text);
    border-left-color: var(--brand);
    box-shadow: inset 0 0 12px rgba(0, 182, 251, 0.05);
  }
  .sb-item svg.icon { width: 16px; height: 16px; flex-shrink: 0; stroke-width: 1.75; opacity: 0.8; }
  .sb-item.active svg.icon { opacity: 1; color: var(--brand); }
  .sb-item .count {
    margin-left: auto;
    font-size: 11px; font-weight: 600;
    color: var(--muted);
    background: var(--surface-2);
    padding: 1px 7px; border-radius: 999px;
  }
  /* v1.7.245: white on the cyan brand measures 2.31:1 — the house rule
     this product has had since v1.7.235, now a token rather than a
     convention. --on-accent is near-black in every state. */
  .sb-item.active .count { background: var(--brand); color: var(--on-accent); }
  .sb-divider { height: 1px; background: var(--sidebar-border); margin: 8px 12px; }

  .sb-footer {
    padding: 12px;
    border-top: 1px solid var(--sidebar-border);
    display: flex; gap: 6px; align-items: center;
    flex-wrap: wrap;              /* v1.7.240: the picker takes its own row */
  }
  /* v1.7.240 — the appearance picker in the owner rail. It sits on its own
     full-width row above the sign-out row. Its light/dark segment is hidden
     HERE and only here: the moon/sun button directly below it is already
     this tier's mode control, and two of them in a 240px rail is clutter.
     The version segment is the new thing, so that is what shows. */
  .sb-appearance { flex: 1 0 100%; }
  .sb-appearance .rx-ap-compact { display: flex; }
  .sb-appearance .rx-ap-seg { flex: 1; justify-content: center; }
  .sb-appearance .rx-ap-seg[aria-label="Light or dark"] { display: none; }
  .sb-theme-toggle {
    flex: 1;
    display: flex; align-items: center; justify-content: center; gap: 6px;
    background: none; border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 7px 10px;
    color: var(--text-2); font-size: 12px; font-weight: 500;
    cursor: pointer; font-family: var(--font);
    box-sizing: border-box; min-height: 34px;   /* batch 224 (v1.7.570): the footer row is one height — "?" is 34 px, this was 32 */
  }
  .sb-theme-toggle:hover { background: var(--hover); color: var(--text); }
  .sb-theme-toggle svg { width: 14px; height: 14px; }
  html[data-theme="dark"] .sb-theme-toggle .sun-icon,
  html[data-theme="dark"] .sb-theme-toggle .sun-label { display: inline; }
  html[data-theme="dark"] .sb-theme-toggle .moon-icon,
  html[data-theme="dark"] .sb-theme-toggle .moon-label { display: none; }
  html[data-theme="light"] .sb-theme-toggle .sun-icon,
  html[data-theme="light"] .sb-theme-toggle .sun-label { display: none; }
  html[data-theme="light"] .sb-theme-toggle .moon-icon,
  html[data-theme="light"] .sb-theme-toggle .moon-label { display: inline; }
  .sb-logout {
    display: flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; box-sizing: border-box;   /* batch 224 (v1.7.570): was 32 — beside the 34-px "?" */
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--muted); text-decoration: none;
  }
  .sb-logout:hover { background: var(--hover); color: var(--danger); text-decoration: none; }
  .sb-logout svg { width: 14px; height: 14px; }

  /* ── Buttons ──────────────────────────────────────────────────────────── */
  .btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-family: var(--font);
    font-size: 13px; font-weight: 500;
    cursor: pointer;
    line-height: 1.4;
    transition: background .12s, border-color .12s, color .12s;
  }
  .btn:hover { background: var(--hover); text-decoration: none; border-color: var(--border-strong); }
  .btn-primary {
    background: var(--text); color: var(--bg); border-color: var(--text);
  }
  .btn-primary:hover { background: var(--text-2); border-color: var(--text-2); color: var(--bg); }
  .btn-accent {
    background: transparent; color: var(--brand);
    border-color: var(--brand);
  }
  .btn-accent:hover {
    background: var(--brand-soft); color: var(--brand-strong);
    border-color: var(--brand);
    box-shadow: 0 0 12px var(--brand-glow);
  }
  .btn-secondary { background: var(--surface); border-color: var(--border); color: var(--text); }
  .btn-secondary:hover { background: var(--hover); border-color: var(--border-strong); }
  .btn-ghost { background: transparent; color: var(--text-2); border-color: transparent; }
  .btn-ghost:hover { background: var(--hover); color: var(--text); }
  .btn-danger { background: var(--danger); color: white; border-color: var(--danger); }
  .btn-danger:hover { background: var(--danger-fill); border-color: var(--danger-fill); color: white; filter: saturate(1.15) brightness(.94); }
  .btn-sm { padding: 4px 10px; font-size: 12px; }
  .btn svg { width: 14px; height: 14px; }

  /* ── Icon-only buttons ────────────────────────────────────────────────
     Square buttons for repeated row actions (edit, delete, etc.). Use
     `.btn-icon` for neutral hover; add `.btn-icon-danger` for hover→red
     (destructive intent). ALWAYS pair with a `title` and `aria-label`
     so the action is discoverable for keyboard / screen-reader users. */
  .btn-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; padding: 0;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-2);
    cursor: pointer;
    font-family: var(--font);
    transition: background .12s, color .12s, border-color .12s;
  }
  .btn-icon:hover {
    background: var(--hover);
    color: var(--text);
    border-color: var(--border-strong);
    text-decoration: none;
  }
  .btn-icon-danger:hover {
    background: rgba(220, 38, 38, .08);
    color: var(--danger);
    border-color: rgba(220, 38, 38, .35);
  }
  .btn-icon svg { display: block; width: 14px; height: 14px; }
  .btn-icon.btn-sm { width: 26px; height: 26px; }
  .btn-icon.btn-sm svg { width: 12px; height: 12px; }

  /* ── Password reveal (v1.7.242) ───────────────────────────────────────
     Shared with portal_base.html — the markup comes from the ONE macro
     in _pw_toggle.html, so both shells style the same two classes. The
     button sits inside the wrapper, absolutely placed over the input's
     right padding; the padding-right is what stops the caret and the
     autofill text running underneath it. */
  .pw-wrap { position: relative; display: block; }
  .pw-wrap.pw-wrap-inline { display: inline-block; }
  .pw-wrap > input[type="password"],
  .pw-wrap > input[type="text"] { padding-right: 40px !important; }
  .pw-eye {
    position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
    display: flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; padding: 0; margin: 0;
    border: none; background: transparent; color: var(--muted);
    border-radius: 6px; cursor: pointer; line-height: 0;
  }
  .pw-eye:hover { color: var(--text); background: var(--surface-2); }
  .pw-eye:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
  .pw-eye svg { display: block; width: 17px; height: 17px; }

  /* ── Cards ────────────────────────────────────────────────────────────── */
  .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
  }
  .card-pad { padding: 18px 20px; }

  /* v1.5.5.2: horizontal-scroll wrapper for wide tables. Used by
     company_admins, dashboard and insights (the class was referenced
     but never defined, so wide action tables pushed the whole page
     sideways on mobile and right-hand buttons sat off-screen). With
     this, the table scrolls within its card instead. */
  .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

  /* ── Badges ───────────────────────────────────────────────────────────── */
  .badge {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 11px; font-weight: 600;
    background: var(--surface-2); color: var(--text-2);
    text-transform: uppercase; letter-spacing: 0.04em;
  }
  .badge-blue   { background: var(--brand-soft); color: var(--accent-text, var(--brand-strong)); }  /* v1.7.561 (batch 218 sweep): the readable ink — --brand-strong is #00A4F4 in colorful light (2.56:1 on the TM's passport badge) */
  .badge-green  { background: rgba(22,163,74,.1); color: var(--success); }
  .badge-orange { background: rgba(217,119,6,.1); color: var(--warning); }
  .badge-red    { background: rgba(220,38,38,.1); color: var(--danger); }
  .badge-gray   { background: var(--surface-2);   color: var(--muted); }

  /* ── Status pills ─────────────────────────────────────────────────────
     Like badges but lower-key: no uppercase, no letter-spacing.
     Use for inline statuses next to row actions ("Accredited", "Active",
     "Batch 2", "Ticket sent") where the badge text reads naturally as a
     state, not a label. Pair with a small SVG icon (11×11px) for clarity.

     Semantic variants (use these — they're theme-aware):
       .status-pill-success   green   (accredited, sent, active)
       .status-pill-warning   orange  (batch, ticket-ready, pending)
       .status-pill-info      blue    (informational)
       .status-pill-danger    red     (failed, inactive, expired)
       .status-pill-muted     gray    (neutral, draft)

     Feature aliases (for backwards compat with v1.4.0.7 dashboard):
       .status-pill-accredited  = success
       .status-pill-batch       = warning
       .status-pill-ticket      = info */
  .status-pill {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px; border-radius: 999px;
    font-size: 11px; font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
  }
  .status-pill svg { flex-shrink: 0; opacity: 0.9; width: 11px; height: 11px; }
  /* v1.7.245: the tint is a TOKEN. It used to be a literal rgba() per
     pill, tuned for the light theme and left to fend for itself in the
     dark one — the same tint over a near-black surface reads as a
     smudge. --*-chip is defined per theme AND per version, so all four
     states get a bed that was measured for them. */
  .status-pill-success    { background: var(--ok-chip);     color: var(--ok-text); }
  .status-pill-warning    { background: var(--warn-chip);   color: var(--warn-text); }
  .status-pill-info       { background: var(--info-chip);   color: var(--info-text); }
  .status-pill-danger     { background: var(--danger-chip); color: var(--danger-text); }
  .status-pill-muted      { background: var(--surface-2);   color: var(--muted); }
  /* Aliases */
  .status-pill-accredited { background: var(--ok-chip);     color: var(--ok-text); }
  .status-pill-batch      { background: var(--warn-chip);   color: var(--warn-text); }
  .status-pill-ticket     { background: var(--info-chip);   color: var(--info-text); }
  /* v1.7.209: a pill that is also a control (the batch pill, which now
     removes the person from the batch). The ✕ stays faint until hover so a
     list of pills does not read as a row of delete buttons. */
  .rx-batch-btn { border: 1px solid transparent; cursor: pointer; font-family: inherit; }
  .rx-batch-btn:hover { background: var(--warn-chip); border-color: var(--warn-edge); }
  .rx-batch-btn .rx-bb-x { opacity: .35; font-weight: 700; }
  .rx-batch-btn:hover .rx-bb-x { opacity: 1; }

  /* ── Compact actions cell ─────────────────────────────────────────────
     Two-tier layout for tables that mix action buttons with status pills.
     Top: action buttons. Bottom: status pills (only if status exists). */
  .actions-cell { vertical-align: top; padding-top: 10px; padding-bottom: 10px; }
  .actions-row {
    display: flex; align-items: center; gap: 4px;
    flex-wrap: nowrap;
  }
  .actions-pills {
    display: flex; gap: 4px; flex-wrap: wrap;
    margin-top: 6px;
  }

  /* ── Alerts ───────────────────────────────────────────────────────────── */
  .alert {
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    margin-bottom: 14px;
    font-size: 13px;
    border-left: 3px solid;
  }
  .alert-error   { background: rgba(220,38,38,.06); color: var(--danger);  border-color: var(--danger); }
  .alert-success { background: rgba(22,163,74,.06); color: var(--success); border-color: var(--success); }
  .alert-info    { background: var(--brand-soft);   color: var(--brand-strong); border-color: var(--brand); }

  /* ── Tables ───────────────────────────────────────────────────────────── */
  table { width: 100%; border-collapse: collapse; font-size: 13px; }
  th {
    padding: 10px 14px;
    text-align: left;
    font-size: 11px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--muted);
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
  }
  td {
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
    color: var(--text);
  }
  tr:last-child td { border-bottom: none; }
  tr:hover td { background: var(--hover); }

  /* ── Inputs ───────────────────────────────────────────────────────────── */
  input[type="text"], input[type="email"], input[type="password"],
  input[type="number"], input[type="date"], input[type="tel"],
  textarea, select {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font-family: var(--font); font-size: 13px;
  }
  input:focus, textarea:focus, select:focus {
    outline: none; border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-soft);
  }
  label { display: block; font-size: 12px; font-weight: 600; color: var(--text-2); margin-bottom: 5px; }

  /* ── Page-head pattern ────────────────────────────────────────────────── */
  .page-head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 16px; margin-bottom: 24px; flex-wrap: wrap;
  }
  .page-head .page-title-block h1 { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; margin: 0; }
  .page-head .page-sub { font-size: 13px; color: var(--muted); margin-top: 4px; }

  /* ── Segmented pill ───────────────────────────────────────────────────── */
  .seg {
    display: inline-flex; background: var(--surface-2);
    border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 2px;
  }
  .seg a, .seg button {
    background: transparent; border: none;
    padding: 5px 12px; border-radius: 4px;
    font-family: var(--font); font-size: 12px; font-weight: 500;
    color: var(--text-2); cursor: pointer; text-decoration: none;
  }
  .seg a.active, .seg button.active {
    background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm);
  }

  /* ── Mobile sidebar ───────────────────────────────────────────────────── */
  .mob-menu-btn {
    display: none;
    position: fixed;
    top: 12px; left: 12px;
    z-index: 501;
    background: var(--surface); border: 1px solid var(--border);
    width: 36px; height: 36px; border-radius: var(--radius-sm);
    align-items: center; justify-content: center;
    cursor: pointer; color: var(--text);
    box-shadow: var(--shadow-sm);
  }
  .mob-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 499; }
  .mob-overlay.open { display: block; }
  @media (max-width: 768px) {
    .sidebar {
      position: fixed; left: 0; top: 0; z-index: 500;
      transform: translateX(-260px); transition: transform .25s ease;
    }
    .sidebar.open { transform: translateX(0); }
    .mob-menu-btn { display: inline-flex; }
    .topbar { padding-left: 60px; }     /* clear the fixed hamburger */
    .content { padding: 18px; }
    /* v1.5.6: prevent iOS tap-to-zoom (inputs <16px trigger it on focus).
       Mobile-only so the compact desktop input sizing is preserved. */
    input[type="text"], input[type="email"], input[type="password"],
    input[type="number"], input[type="date"], input[type="tel"],
    textarea, select { font-size: 16px; }
    /* v1.5.6: horizontal-overflow guard. Safe at this breakpoint because the
       sidebar is position:fixed here, not sticky, so clipping can't break it. */
    html, body { overflow-x: hidden; max-width: 100%; }
  }

  /* ── Toned-down number-display style for stat numbers ────────────────── */
  /* Used where the OLD UI used cyan: stat values become text-colored (not cyan)
     so they don't shout. Cyan is reserved for glow accents and active states. */
  .stat-value-tone {
    font-size: 28px; font-weight: 700; letter-spacing: -.02em;
    color: var(--text);
  }

  /* Default Rynox mark — theme-aware. White on dark, graphite on light.
     Used as the fallback brand mark wherever a tenant hasn't uploaded
     their own logo. An <img class="rynox-mark"> with src set to a 1x1
     transparent pixel; the visible glyph comes from the background. */
  .rynox-mark {
    background: url('/static/rynox-mark.png') center / contain no-repeat;
  }
  html[data-theme="dark"] .rynox-mark {
    background-image: url('/static/rynox-mark-dark.png');
  }

  /* ── v1.7.309 (item 4) — THE HOUSE ICON, SIZED ─────────────────────
     templates/_rx_icons.html emits `.rx-ico` for every button icon
     that used to be an emoji. One size here rather than a width on
     every call site; `currentColor` means the button's own ink already
     paints it. `flex:none` because these live in inline-flex buttons
     that would otherwise squeeze them. */
  .rx-ico { width: 14px; height: 14px; flex: none; }
  .btn-sm .rx-ico, .bb-btn .rx-ico { width: 13px; height: 13px; }
  .rx-mi-ico .rx-ico { width: 16px; height: 16px; }
  .batch-badge .rx-ico { width: 11px; height: 11px; }
  /* v1.7.366 — the folded shell inside a frame (see the <body> script). */
  body.rx-framed .sidebar, body.rx-framed .mob-menu-btn, body.rx-framed .mob-overlay { display: none !important; }
  body.rx-framed .main { margin-left: 0; }
