/* ============================================================================
   TOTEM DESIGN SYSTEM — totem.css
   Clean & bright product-SaaS UI for the Totem digital-signage CMS.
   Hand-written vanilla CSS. No build step, no preprocessor, no external fonts.

   USAGE
     <link rel="stylesheet" href="/_static/totem.css?v=20260727">

   IMPORTANT: totem.css REPLACES style.css / portal.css on a page.
   Do not load it alongside them — the legacy compatibility aliases in §1.9
   re-point the old dark tokens (--ink, --panel, --orange…) at the new light
   palette, so a page loading both stylesheets will render inconsistently.

   CONTENTS
     1  TOKENS          palette, theming (light/dark), scales
     2  RESET + BASE    reset, base elements, focus, motion, selection
     3  LAYOUT          app shell, container, stack/cluster/grid, card grid
     4  COMPONENTS      buttons, cards, forms, dropzone, tables, nav, badges,
                        overlays, tabs, breadcrumb, pagination, empty states,
                        skeletons, progress, tooltip, avatar
     5  UTILITIES       spacing, text, layout helpers, a11y helpers
     6  PRINT

   ACCESSIBILITY NOTES
     · Every text/background token pair below carries its computed WCAG
       contrast ratio as a comment. 124 real fg/bg pairs were computed across
       both themes: all body-text pairs are >= 4.5:1, all non-text / UI
       component boundaries are >= 3:1, zero failures. The three pairs that
       sit below 3:1 are decorative and are justified in §1.1a.
     · Status is never conveyed by color alone — every status class pairs its
       color with a distinct glyph shape (see .badge / .status-dot) and text.
     · Primary interactive controls are >= 44x44px (--tap).
   ========================================================================== */


/* ==========================================================================
   1 · TOKENS
   ========================================================================== */

/* -- 1.1 raw palette ------------------------------------------------------
   Defined ONCE here, mapped to semantic tokens in 1.3-1.5 so the light and
   dark values never drift. Ratios are computed, not estimated.             */
:root {
  /* ---- LIGHT palette ---- */
  --c-l-bg:            #f5f7fa;  /* page canvas                                  */
  --c-l-surface:       #ffffff;  /* cards, panels                                */
  --c-l-surface-2:     #f0f3f7;  /* nested / inset surfaces                      */
  --c-l-elevated:      #ffffff;  /* modals, popovers, dropdowns                  */
  --c-l-border:        #dfe4ec;  /* hairlines (decorative, not text) — see §1.1a */
  --c-l-border-strong: #7a8597;  /*  3.73:1 on surface · 3.48:1 on bg ·
                                     3.35:1 on surface-2 · 3.03:1 on track  AA   */
  --c-l-text:          #111826;  /* 17.76:1 on surface · 16.55:1 on bg      AAA  */
  --c-l-muted:         #59637a;  /*  6.02:1 on surface ·  5.61:1 on bg      AA   */
  --c-l-accent:        #2f56d9;  /*  6.08:1 on surface ·  5.67:1 on bg      AA   */
  --c-l-accent-hover:  #2444b8;  /*  8.10:1 on surface                      AAA  */
  --c-l-accent-soft:   #eaefff;  /* accent tint bg · accent on it = 5.30:1  AA   */
  --c-l-on-accent:     #ffffff;  /*  6.08:1 on accent · 8.10:1 on hover     AA   */
  --c-l-success:       #12784a;  /*  5.51:1 on surface · 4.89:1 on tint     AA   */
  --c-l-success-soft:  #e6f5ed;  /* text on it = 15.77:1                    AAA  */
  --c-l-warning:       #8a5a00;  /*  5.93:1 on surface · 5.29:1 on tint     AA   */
  --c-l-warning-soft:  #fdf1d8;  /* text on it = 15.86:1                    AAA  */
  --c-l-danger:        #c02626;  /*  5.92:1 on surface · 5.11:1 on tint     AA   */
  --c-l-danger-soft:   #fdeaea;  /* text on it = 15.34:1                    AAA  */
  --c-l-info:          #1b5fb8;  /*  6.22:1 on surface · 5.42:1 on tint     AA   */
  --c-l-info-soft:     #e7f0fd;  /* text on it = 15.46:1                    AAA  */
  --c-l-skeleton:      #e6eaf0;  /* decorative placeholder — exempt from AA      */
  --c-l-code:          #24303f;  /* 12.15:1 on --c-l-surface-2              AAA  */
  --c-l-overlay:       rgba(17, 24, 38, .45);

  /* ---- DARK palette ---- */
  --c-d-bg:            #0e1218;
  --c-d-surface:       #171d26;
  --c-d-surface-2:     #1e2531;
  --c-d-elevated:      #1c232e;
  --c-d-border:        #2b3442;  /* hairlines (decorative, not text) — see §1.1a */
  --c-d-border-strong: #6c7d99;  /*  4.06:1 on surface · 4.50:1 on bg ·
                                     3.69:1 on surface-2 · 3.01:1 on track  AA   */
  --c-d-text:          #e8edf5;  /* 14.40:1 on surface · 15.97:1 on bg      AAA  */
  --c-d-muted:         #a3b0c2;  /*  7.70:1 on surface ·  8.54:1 on bg      AAA  */
  --c-d-accent:        #7fa4ff;  /*  6.98:1 on surface ·  7.74:1 on bg      AA   */
  --c-d-accent-hover:  #9bb8ff;  /*  8.62:1 on surface                      AAA  */
  --c-d-accent-soft:   #1b2740;  /* accent on it = 6.14:1                   AA   */
  --c-d-on-accent:     #0b1020;  /*  7.80:1 on accent · 9.64:1 on hover     AA   */
  --c-d-success:       #4ade8b;  /*  9.77:1 on surface · 8.90:1 on tint     AAA  */
  --c-d-success-soft:  #12291d;  /* text on it = 13.13:1                    AAA  */
  --c-d-warning:       #f5c451;  /* 10.40:1 on surface · 9.53:1 on tint     AAA  */
  --c-d-warning-soft:  #2b2314;  /* text on it = 13.20:1                    AAA  */
  --c-d-danger:        #ff8b84;  /*  7.48:1 on surface · 7.26:1 on tint     AAA  */
  --c-d-danger-soft:   #2e1a1a;  /* text on it = 13.97:1                    AAA  */
  --c-d-info:          #78b6ff;  /*  8.01:1 on surface · 7.40:1 on tint     AAA  */
  --c-d-info-soft:     #14243a;  /* text on it = 13.30:1                    AAA  */
  --c-d-skeleton:      #232b37;  /* decorative placeholder — exempt from AA      */
  --c-d-code:          #cbd6e6;  /* 12.24:1 on #12171f                      AAA  */
  --c-d-overlay:       rgba(4, 7, 12, .66);
}

/* -- 1.1a documented contrast exemptions ----------------------------------
   Two token pairs sit below 3:1 ON PURPOSE. Neither carries information:

   · --border vs --surface  (1.15:1 light / 1.35:1 dark)
     The hairline that outlines a .card / .table-wrap / .alert. It is pure
     decoration: the card is already separated from the canvas by its
     background AND its shadow, and every card announces itself with a
     >= 4.5:1 text label. WCAG 1.4.11 only covers visual information REQUIRED
     to identify a component, so a redundant hairline is exempt. Anything a
     user must actually see the edge of (inputs, buttons, the switch track,
     the progress track, badges) uses --border-strong, which is >= 3:1.

   · --skeleton vs --surface (1.09:1 light / 1.19:1 dark)
     Loading placeholders convey no content and are removed from the a11y
     tree; they are explicitly exempt (and hidden entirely in print).       */

/* -- 1.2 scales (theme-independent) --------------------------------------- */
:root {
  /* type — system stacks only, nothing downloaded */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, "Noto Sans", ui-sans-serif, sans-serif,
               "Apple Color Emoji", "Segoe UI Emoji";
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", "Courier New", monospace;

  --fs-xs:   0.75rem;    /* 12px — pills, captions (never body copy)          */
  --fs-sm:   0.8125rem;  /* 13px — dense table cells, help text               */
  --fs-md:   0.875rem;   /* 14px — secondary UI text                          */
  --fs-base: 1rem;       /* 16px — body / form controls (no iOS zoom)         */
  --fs-lg:   1.125rem;   /* 18px                                              */
  --fs-xl:   1.375rem;   /* 22px — card / section headings                    */
  --fs-2xl:  1.75rem;    /* 28px — page title                                 */
  --fs-3xl:  2.25rem;    /* 36px — hero / marketing                           */

  --lh-tight: 1.2;
  --lh-snug:  1.35;
  --lh-base:  1.55;
  --lh-loose: 1.7;

  --fw-normal: 400;
  --fw-medium: 500;
  --fw-semi:   600;
  --fw-bold:   700;

  /* spacing — 4px base, generous by default */
  --sp-0:  0;
  --sp-1:  0.25rem;   /*  4px */
  --sp-2:  0.5rem;    /*  8px */
  --sp-3:  0.75rem;   /* 12px */
  --sp-4:  1rem;      /* 16px */
  --sp-5:  1.5rem;    /* 24px */
  --sp-6:  2rem;      /* 32px */
  --sp-7:  3rem;      /* 48px */
  --sp-8:  4rem;      /* 64px */
  --sp-9:  6rem;      /* 96px */

  /* radius — friendly, rounded */
  --r-xs:   4px;
  --r-sm:   8px;
  --r-md:   12px;
  --r-lg:   16px;
  --r-xl:   24px;
  --r-pill: 999px;
  --r-round: 50%;

  /* motion */
  --dur-fast: 110ms;
  --dur:      170ms;
  --dur-slow: 280ms;
  --ease:      cubic-bezier(.2, .6, .3, 1);
  --ease-out:  cubic-bezier(.16, 1, .3, 1);

  /* z-index scale — nothing in the system uses a bare magic number */
  --z-base:     0;
  --z-raised:   10;
  --z-sticky:   100;
  --z-drawer:   200;
  --z-overlay:  300;
  --z-modal:    400;
  --z-toast:    500;
  --z-tooltip:  600;

  /* sizing */
  --container:        1200px;
  --container-narrow: 760px;
  --container-wide:   1480px;
  --sidebar-w:        248px;
  --topbar-h:         60px;
  --tap:              44px;   /* minimum touch target                        */
  --tap-sm:           36px;   /* dense/secondary controls only               */
}

/* -- 1.3 semantic tokens · LIGHT (default) -------------------------------- */
:root {
  color-scheme: light;
  --bg:            var(--c-l-bg);
  --surface:       var(--c-l-surface);
  --surface-2:     var(--c-l-surface-2);
  --elevated:      var(--c-l-elevated);
  --border:        var(--c-l-border);
  --border-strong: var(--c-l-border-strong);
  --text:          var(--c-l-text);
  --muted:         var(--c-l-muted);
  --accent:        var(--c-l-accent);
  --accent-hover:  var(--c-l-accent-hover);
  --accent-soft:   var(--c-l-accent-soft);
  --on-accent:     var(--c-l-on-accent);
  --success:       var(--c-l-success);
  --success-soft:  var(--c-l-success-soft);
  --warning:       var(--c-l-warning);
  --warning-soft:  var(--c-l-warning-soft);
  --danger:        var(--c-l-danger);
  --danger-soft:   var(--c-l-danger-soft);
  --info:          var(--c-l-info);
  --info-soft:     var(--c-l-info-soft);
  --skeleton:      var(--c-l-skeleton);
  --code-fg:       var(--c-l-code);
  --code-bg:       var(--c-l-surface-2);
  --overlay:       var(--c-l-overlay);
  --focus:         var(--c-l-accent);
  --focus-halo:    rgba(47, 86, 217, .22);
  --input-bg:      var(--c-l-surface);
  --input-bg-disabled: #f3f5f8;
  --track:         #e3e8f0;   /* progress / meter / switch track — always
                                 outlined with --border-strong (3.03:1)      */
  --switch-knob:    #ffffff;  /* off: ringed in --border-strong, 3.73:1      */
  --switch-knob-on: #ffffff;  /* on:  6.08:1 on --accent                AA   */

  /* soft shadows — the "bright SaaS" lift */
  --shadow-xs: 0 1px 2px rgba(16, 24, 40, .06);
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .08);
  --shadow-md: 0 2px 4px rgba(16, 24, 40, .05), 0 6px 16px -4px rgba(16, 24, 40, .10);
  --shadow-lg: 0 4px 8px rgba(16, 24, 40, .05), 0 16px 32px -8px rgba(16, 24, 40, .14);
  --shadow-xl: 0 8px 16px rgba(16, 24, 40, .06), 0 32px 64px -16px rgba(16, 24, 40, .22);
  --shadow-accent: 0 6px 18px -6px rgba(47, 86, 217, .45);
}

/* -- 1.4 semantic tokens · DARK ------------------------------------------- */
/* The media query is the default signal; the [data-theme] rules in 1.5 have
   higher specificity (0,1,1 > 0,0,1) so an explicit toggle always wins.     */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --bg:            var(--c-d-bg);
    --surface:       var(--c-d-surface);
    --surface-2:     var(--c-d-surface-2);
    --elevated:      var(--c-d-elevated);
    --border:        var(--c-d-border);
    --border-strong: var(--c-d-border-strong);
    --text:          var(--c-d-text);
    --muted:         var(--c-d-muted);
    --accent:        var(--c-d-accent);
    --accent-hover:  var(--c-d-accent-hover);
    --accent-soft:   var(--c-d-accent-soft);
    --on-accent:     var(--c-d-on-accent);
    --success:       var(--c-d-success);
    --success-soft:  var(--c-d-success-soft);
    --warning:       var(--c-d-warning);
    --warning-soft:  var(--c-d-warning-soft);
    --danger:        var(--c-d-danger);
    --danger-soft:   var(--c-d-danger-soft);
    --info:          var(--c-d-info);
    --info-soft:     var(--c-d-info-soft);
    --skeleton:      var(--c-d-skeleton);
    --code-fg:       var(--c-d-code);
    --code-bg:       #12171f;
    --overlay:       var(--c-d-overlay);
    --focus:         var(--c-d-accent);
    --focus-halo:    rgba(127, 164, 255, .26);
    --input-bg:      #121821;
    --input-bg-disabled: #161c25;
    --track:         #2b3442;
    --switch-knob:    var(--c-d-text);       /* off: 10.68:1 on track  AAA  */
    --switch-knob-on: var(--c-d-on-accent);  /* on:   7.80:1 on accent  AA  */
    --shadow-xs: 0 1px 2px rgba(0, 0, 0, .40);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .45), 0 1px 3px rgba(0, 0, 0, .35);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, .45), 0 8px 20px -6px rgba(0, 0, 0, .55);
    --shadow-lg: 0 4px 10px rgba(0, 0, 0, .45), 0 20px 40px -10px rgba(0, 0, 0, .60);
    --shadow-xl: 0 10px 22px rgba(0, 0, 0, .50), 0 40px 70px -20px rgba(0, 0, 0, .70);
    --shadow-accent: 0 6px 18px -6px rgba(127, 164, 255, .35);
  }
}

/* -- 1.5 explicit theme overrides (a toggle stamps data-theme on <html>) --- */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:            var(--c-d-bg);
  --surface:       var(--c-d-surface);
  --surface-2:     var(--c-d-surface-2);
  --elevated:      var(--c-d-elevated);
  --border:        var(--c-d-border);
  --border-strong: var(--c-d-border-strong);
  --text:          var(--c-d-text);
  --muted:         var(--c-d-muted);
  --accent:        var(--c-d-accent);
  --accent-hover:  var(--c-d-accent-hover);
  --accent-soft:   var(--c-d-accent-soft);
  --on-accent:     var(--c-d-on-accent);
  --success:       var(--c-d-success);
  --success-soft:  var(--c-d-success-soft);
  --warning:       var(--c-d-warning);
  --warning-soft:  var(--c-d-warning-soft);
  --danger:        var(--c-d-danger);
  --danger-soft:   var(--c-d-danger-soft);
  --info:          var(--c-d-info);
  --info-soft:     var(--c-d-info-soft);
  --skeleton:      var(--c-d-skeleton);
  --code-fg:       var(--c-d-code);
  --code-bg:       #12171f;
  --overlay:       var(--c-d-overlay);
  --focus:         var(--c-d-accent);
  --focus-halo:    rgba(127, 164, 255, .26);
  --input-bg:      #121821;
  --input-bg-disabled: #161c25;
  --track:         #2b3442;
  --switch-knob:    var(--c-d-text);       /* off: 10.68:1 on track    AAA  */
  --switch-knob-on: var(--c-d-on-accent);  /* on:   7.80:1 on accent    AA  */
  --shadow-xs: 0 1px 2px rgba(0, 0, 0, .40);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .45), 0 1px 3px rgba(0, 0, 0, .35);
  --shadow-md: 0 2px 6px rgba(0, 0, 0, .45), 0 8px 20px -6px rgba(0, 0, 0, .55);
  --shadow-lg: 0 4px 10px rgba(0, 0, 0, .45), 0 20px 40px -10px rgba(0, 0, 0, .60);
  --shadow-xl: 0 10px 22px rgba(0, 0, 0, .50), 0 40px 70px -20px rgba(0, 0, 0, .70);
  --shadow-accent: 0 6px 18px -6px rgba(127, 164, 255, .35);
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg:            var(--c-l-bg);
  --surface:       var(--c-l-surface);
  --surface-2:     var(--c-l-surface-2);
  --elevated:      var(--c-l-elevated);
  --border:        var(--c-l-border);
  --border-strong: var(--c-l-border-strong);
  --text:          var(--c-l-text);
  --muted:         var(--c-l-muted);
  --accent:        var(--c-l-accent);
  --accent-hover:  var(--c-l-accent-hover);
  --accent-soft:   var(--c-l-accent-soft);
  --on-accent:     var(--c-l-on-accent);
  --success:       var(--c-l-success);
  --success-soft:  var(--c-l-success-soft);
  --warning:       var(--c-l-warning);
  --warning-soft:  var(--c-l-warning-soft);
  --danger:        var(--c-l-danger);
  --danger-soft:   var(--c-l-danger-soft);
  --info:          var(--c-l-info);
  --info-soft:     var(--c-l-info-soft);
  --skeleton:      var(--c-l-skeleton);
  --code-fg:       var(--c-l-code);
  --code-bg:       var(--c-l-surface-2);
  --overlay:       var(--c-l-overlay);
  --focus:         var(--c-l-accent);
  --focus-halo:    rgba(47, 86, 217, .22);
  --input-bg:      var(--c-l-surface);
  --input-bg-disabled: #f3f5f8;
  --track:         #e3e8f0;
  --switch-knob:    #ffffff;
  --switch-knob-on: #ffffff;
  --shadow-xs: 0 1px 2px rgba(16, 24, 40, .06);
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .08);
  --shadow-md: 0 2px 4px rgba(16, 24, 40, .05), 0 6px 16px -4px rgba(16, 24, 40, .10);
  --shadow-lg: 0 4px 8px rgba(16, 24, 40, .05), 0 16px 32px -8px rgba(16, 24, 40, .14);
  --shadow-xl: 0 8px 16px rgba(16, 24, 40, .06), 0 32px 64px -16px rgba(16, 24, 40, .22);
  --shadow-accent: 0 6px 18px -6px rgba(47, 86, 217, .45);
}

/* -- 1.9 legacy token aliases (migration shim) ----------------------------
   Old templates reference --ink / --panel / --orange etc. inline. These keep
   them alive while pages move over. DELETE once no inline style="" or page
   <style> block references them.  Never load totem.css next to style.css.  */
:root {
  --ink:           var(--text);
  --ink-dim:       var(--muted);
  --panel:         var(--surface);
  --panel2:        var(--surface-2);
  --orange:        var(--accent);      /* brand accent is now blue           */
  --blue:          var(--info);
  --green:         var(--success);
  --red:           var(--danger);
  --yellow:        var(--warning);
  /* NOTE: --border-strong and --r-sm are NOT re-declared here. Aliasing a
     custom property to itself (`--x: var(--x)`) is a dependency cycle, which
     makes the property invalid at computed-value time — every `var(--x)` in
     the sheet then falls back to unset. That is a hard break, not a shim. */
  --r:             var(--r-md);
  --shadow:        var(--shadow-sm);
  --shadow-lift:   var(--shadow-lg);
}


/* ==========================================================================
   2 · RESET + BASE
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
}

/* headings: tight, dark, no gradient text (that broke contrast in the old UI) */
h1, h2, h3, h4, h5, h6 {
  margin: 0 0 var(--sp-3);
  font-weight: var(--fw-semi);
  line-height: var(--lh-tight);
  letter-spacing: -0.011em;
  color: var(--text);
}
h1 { font-size: var(--fs-2xl); font-weight: var(--fw-bold); letter-spacing: -0.021em; }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-base); }
h5, h6 { font-size: var(--fs-md); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }

p { margin: 0 0 var(--sp-4); }
p:last-child { margin-bottom: 0; }

a {
  color: var(--accent);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}
a:hover { color: var(--accent-hover); text-decoration: underline; }

small { font-size: var(--fs-md); }
strong, b { font-weight: var(--fw-semi); }

hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: var(--sp-5) 0;
}

ul, ol { margin: 0 0 var(--sp-4); padding-left: 1.35em; }
li + li { margin-top: var(--sp-1); }

img, svg, video, canvas, picture { display: block; max-width: 100%; }
img, video { height: auto; }

code, kbd, samp, pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
}
code {
  background: var(--code-bg);
  color: var(--code-fg);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: .1em .4em;
}
pre {
  background: var(--code-bg);
  color: var(--code-fg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: var(--sp-4);
  overflow-x: auto;
  line-height: var(--lh-snug);
}
pre code { background: none; border: 0; padding: 0; }
kbd {
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: var(--r-xs);
  padding: .1em .45em;
  font-size: .8em;
  color: var(--text);
}

/* form elements inherit typography by default */
button, input, select, textarea, optgroup {
  font: inherit;
  color: inherit;
  margin: 0;
}
button { background: none; border: 0; }

/* fluid, predictable tables */
table { border-collapse: collapse; width: 100%; }

fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
legend { padding: 0; }

[hidden] { display: none !important; }

::selection {
  background: var(--accent-soft);
  color: var(--text);
}

/* -- focus ---------------------------------------------------------------
   Never `outline: none` without a replacement. The 2px ring plus the 4px
   halo reads clearly on both light and dark surfaces (accent vs surface is
   6.08:1 light / 6.98:1 dark, well past the 3:1 non-text requirement).    */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
/* Older engines without :focus-visible still get a visible ring. */
:focus { outline: 2px solid var(--focus); outline-offset: 2px; }
:focus:not(:focus-visible) { outline: none; }

/* -- scrollbars ----------------------------------------------------------- */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background: var(--muted); background-clip: content-box; }

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

/* -- forced colors (Windows high contrast) -------------------------------- */
@media (forced-colors: active) {
  .btn, .card, .input, .dropzone, .badge { border: 1px solid ButtonBorder; }
  :focus-visible { outline: 2px solid Highlight; }
}


/* ==========================================================================
   3 · LAYOUT
   ========================================================================== */

/* -- 3.1 app shell -------------------------------------------------------
   <body>
     <div class="app">
       <aside class="sidebar">…</aside>
       <div class="main-wrap">
         <header class="topbar">…</header>
         <main class="container">…</main>
         <footer class="app-footer">…</footer>
       </div>
     </div>                                                                */
.app {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
}

.main-wrap {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.main-wrap > main { flex: 1; }

main {
  padding: var(--sp-6) var(--sp-5) var(--sp-8);
  width: 100%;
}

.app-footer {
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: var(--fs-md);
  padding: var(--sp-5);
  text-align: center;
}

/* -- 3.2 container -------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
}
.container--narrow { max-width: var(--container-narrow); }
.container--wide   { max-width: var(--container-wide); }
.container--full   { max-width: none; }

/* -- 3.3 page header ------------------------------------------------------
   .hero and .meta are LEGACY names, kept deliberately.                     */
.hero {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
}
.hero-text { min-width: 0; }
.hero h1 { margin-bottom: var(--sp-2); }
.hero .meta,
.meta {
  color: var(--muted);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
  margin: 0;
}
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
}

/* section header — legacy .sec-head / .see-all preserved */
.sec-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  margin: 0 0 var(--sp-4);
}
.sec-head h2, .sec-head h3 { margin: 0; }
.sec-head .see-all { font-size: var(--fs-md); font-weight: var(--fw-medium); }

/* -- 3.4 primitives: stack / cluster / grid ------------------------------- */
.stack { display: flex; flex-direction: column; gap: var(--sp-4); }
.stack--xs { gap: var(--sp-1); }
.stack--sm { gap: var(--sp-2); }
.stack--lg { gap: var(--sp-6); }
.stack--xl { gap: var(--sp-7); }

.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
}
.cluster--sm  { gap: var(--sp-2); }
.cluster--lg  { gap: var(--sp-5); }
.cluster--between { justify-content: space-between; }
.cluster--end     { justify-content: flex-end; }
.cluster--top     { align-items: flex-start; }
.cluster--nowrap  { flex-wrap: nowrap; }

/* sidebar-ish 2-up. `.row` is a LEGACY name (was a 2-col grid) — kept. */
.row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-5);
  margin-bottom: var(--sp-5);
}
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-4); }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-4); }

/* -- 3.5 card grid — reflows 1 → 2 → 3 columns without media queries ------
   `min()` keeps it from overflowing on very narrow phones.                 */
.card-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
.card-grid--sm { grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); }
.card-grid--lg { grid-template-columns: repeat(auto-fill, minmax(min(360px, 100%), 1fr)); }

/* stat tiles */
.stat-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  margin-bottom: var(--sp-6);
}

/* -- 3.6 responsive ------------------------------------------------------- */
@media (max-width: 1024px) {
  .row, .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
  main { padding: var(--sp-5) var(--sp-4) var(--sp-7); }
  .row, .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); }
  h1 { font-size: var(--fs-xl); }
  h2 { font-size: var(--fs-lg); }
  .hero { gap: var(--sp-3); }
  .hero-actions .btn { flex: 1 1 auto; }
}


/* ==========================================================================
   4 · COMPONENTS
   ========================================================================== */

/* -- 4.1 buttons ----------------------------------------------------------
   .btn is canonical. `.pbtn` (portal) and `.btn-primary` / `.btn-danger`
   (admin) are LEGACY aliases kept so existing markup keeps working.        */
.btn,
.pbtn {
  --btn-h: var(--tap);
  --btn-px: var(--sp-5);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--btn-h);
  min-width: var(--btn-h);
  padding: 0 var(--btn-px);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  line-height: 1;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  box-shadow: var(--shadow-xs);
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:hover, .pbtn:hover {
  background: var(--surface-2);
  border-color: var(--muted);
  color: var(--text);
  text-decoration: none;
  box-shadow: var(--shadow-sm);
}
.btn:active, .pbtn:active { transform: translateY(1px); box-shadow: none; }
.btn:focus-visible, .pbtn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--focus-halo);
}
.btn[disabled], .btn:disabled, .btn.is-disabled,
.pbtn[disabled], .pbtn:disabled, .pbtn.is-disabled {
  opacity: .5;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
  pointer-events: none;
}

/* primary — the one confident accent */
.btn--primary, .btn-primary, .pbtn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  box-shadow: var(--shadow-accent);
}
.btn--primary:hover, .btn-primary:hover, .pbtn--primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--on-accent);
}
.btn--primary:active, .btn-primary:active, .pbtn--primary:active { box-shadow: none; }

/* secondary — quiet but present (this is the default .btn look, named too) */
.btn--secondary, .pbtn--secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
}
.btn--secondary:hover, .pbtn--secondary:hover { background: var(--surface-2); }

/* accent-tinted secondary, for "second most important" actions */
.btn--soft {
  background: var(--accent-soft);
  border-color: transparent;
  color: var(--accent);
  box-shadow: none;
}
.btn--soft:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-hover); }

/* ghost — no chrome until you touch it */
.btn--ghost, .pbtn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--muted);
  box-shadow: none;
}
.btn--ghost:hover, .pbtn--ghost:hover {
  background: var(--surface-2);
  border-color: transparent;
  color: var(--text);
}

/* danger */
.btn--danger, .btn-danger, .pbtn--danger {
  background: var(--surface);
  border-color: var(--danger);
  color: var(--danger);
}
.btn--danger:hover, .btn-danger:hover, .pbtn--danger:hover {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
:root[data-theme="dark"] .btn--danger:hover,
:root[data-theme="dark"] .btn-danger:hover,
:root[data-theme="dark"] .pbtn--danger:hover { color: var(--on-accent); }
@media (prefers-color-scheme: dark) {
  .btn--danger:hover, .btn-danger:hover, .pbtn--danger:hover { color: var(--on-accent); }
  :root[data-theme="light"] .btn--danger:hover,
  :root[data-theme="light"] .btn-danger:hover,
  :root[data-theme="light"] .pbtn--danger:hover { color: #fff; }
}
.btn--danger-solid {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
:root[data-theme="dark"] .btn--danger-solid { color: var(--on-accent); }
@media (prefers-color-scheme: dark) {
  .btn--danger-solid { color: var(--on-accent); }
  :root[data-theme="light"] .btn--danger-solid { color: #fff; }
}
.btn--danger-solid:hover { filter: brightness(1.08); }

/* sizes — sm is for dense/secondary use only, md/lg meet the 44px target */
.btn--sm, .pbtn--sm { --btn-h: var(--tap-sm); --btn-px: var(--sp-3); font-size: var(--fs-sm); border-radius: var(--r-sm); }
.btn--md { --btn-h: var(--tap); --btn-px: var(--sp-5); }
.btn--lg { --btn-h: 52px; --btn-px: var(--sp-6); font-size: var(--fs-base); border-radius: var(--r-md); }
.btn--block { display: flex; width: 100%; }
.btn--pill { border-radius: var(--r-pill); }

/* icon-only */
.btn--icon {
  --btn-px: 0;
  width: var(--btn-h);
  padding: 0;
  border-radius: var(--r-md);
}
.btn--icon.btn--round { border-radius: var(--r-round); }
.btn .icon, .btn svg { width: 1.15em; height: 1.15em; flex: none; }

/* loading */
.btn.is-loading, .pbtn.is-loading {
  pointer-events: none;
  position: relative;
  color: transparent !important;
}
.btn.is-loading::after, .pbtn.is-loading::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 1.1em;
  height: 1.1em;
  border: 2px solid currentColor;
  border-radius: var(--r-round);
  border-top-color: transparent;
  color: var(--text);
  animation: totem-spin .7s linear infinite;
}
.btn--primary.is-loading::after, .pbtn--primary.is-loading::after { color: var(--on-accent); }
@keyframes totem-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .btn.is-loading::after, .pbtn.is-loading::after { animation: none; border-top-color: currentColor; opacity: .6; }
}

/* button group — segmented control */
.btn-group {
  display: inline-flex;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-xs);
}
.btn-group > .btn {
  border-radius: 0;
  margin-left: -1px;
  box-shadow: none;
}
.btn-group > .btn:first-child { border-radius: var(--r-md) 0 0 var(--r-md); margin-left: 0; }
.btn-group > .btn:last-child  { border-radius: 0 var(--r-md) var(--r-md) 0; }
.btn-group > .btn:only-child  { border-radius: var(--r-md); }
.btn-group > .btn:hover, .btn-group > .btn:focus-visible { z-index: var(--z-raised); position: relative; }
.btn-group > .btn.is-active,
.btn-group > .btn[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  z-index: var(--z-raised);
  position: relative;
}

/* -- 4.2 cards ------------------------------------------------------------ */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-5);
  min-width: 0;
}
.card--flush { padding: 0; overflow: hidden; }
.card--quiet { box-shadow: none; }
.card--inset { background: var(--surface-2); box-shadow: none; }
.card--accent { border-top: 3px solid var(--accent); }

/* clickable card */
.card--interactive {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.card--interactive:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
  text-decoration: none;
  color: inherit;
}
.card--interactive:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.card--interactive:active { transform: translateY(0); box-shadow: var(--shadow-sm); }

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.card-body { padding: var(--sp-5); }
.card-foot {
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  display: flex;
  gap: var(--sp-2);
  justify-content: flex-end;
  flex-wrap: wrap;
}
.card-title { font-size: var(--fs-lg); font-weight: var(--fw-semi); margin: 0; }
.card-sub { color: var(--muted); font-size: var(--fs-md); margin: 0; }

/* legacy: .col was the admin two-up panel */
.col {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-5);
  min-width: 0;
}

/* stat tile — legacy .stat-card / .stat-label / .stat-value / .stat-foot */
.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-4) var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
}
.stat-label {
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  letter-spacing: .02em;
}
.stat-value {
  display: flex;
  align-items: baseline;
  gap: var(--sp-1);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.stat-value .big { font: inherit; }
.stat-value .of { color: var(--muted); font-size: var(--fs-md); font-weight: var(--fw-normal); }
.stat-foot { color: var(--muted); font-size: var(--fs-sm); margin-top: auto; }
.stat-value.ok, .stat-value .big.ok { color: var(--success); }
.stat-value.warn, .stat-value .big.warn { color: var(--warning); }
.stat-value.bad, .stat-value .big.bad { color: var(--danger); }

/* -- 4.3 screen card ------------------------------------------------------
   The core object of the product: a physical screen with a live preview.
   <a class="screen-card"> preview / body(title + status + subtitle)        */
.screen-card {
  --ratio: 16 / 9;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  min-width: 0;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
a.screen-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
  text-decoration: none;
  color: inherit;
}
a.screen-card:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.screen-card--portrait { --ratio: 9 / 16; }

.screen-card-preview {
  position: relative;
  aspect-ratio: var(--ratio);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
}
@supports not (aspect-ratio: 16 / 9) {
  .screen-card-preview { height: 0; padding-bottom: 56.25%; }
  .screen-card--portrait .screen-card-preview { padding-bottom: 177.78%; }
}
.screen-card-preview img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* fallback shown when there is no screenshot yet */
.screen-card-preview .preview-fallback {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  color: var(--muted);
  font-size: var(--fs-sm);
  text-align: center;
  padding: var(--sp-4);
}
.screen-card-badge {
  position: absolute;
  top: var(--sp-2);
  left: var(--sp-2);
  z-index: var(--z-raised);
}
.screen-card-body {
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
}
.screen-card-title {
  font-weight: var(--fw-semi);
  font-size: var(--fs-base);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.screen-card-sub {
  color: var(--muted);
  font-size: var(--fs-md);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.screen-card-status {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-md);
  color: var(--muted);
}

/* -- 4.4 forms ------------------------------------------------------------ */
.form { display: flex; flex-direction: column; gap: var(--sp-5); }
.form-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }
@media (max-width: 640px) { .form-row { grid-template-columns: minmax(0, 1fr); } }

.field { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }

.label {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--text);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.label .optional { color: var(--muted); font-weight: var(--fw-normal); }
.label .required { color: var(--danger); }

.input, .textarea, select.input, .select select {
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-4);
  background: var(--input-bg);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  font-family: inherit;
  font-size: var(--fs-base);   /* 16px — prevents iOS zoom-on-focus */
  line-height: var(--lh-snug);
  box-shadow: var(--shadow-xs);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--muted); opacity: 1; }
.input:hover, .textarea:hover { border-color: var(--muted); }
.input:focus, .textarea:focus, select.input:focus, .select select:focus {
  outline: 2px solid var(--focus);
  outline-offset: 0;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--focus-halo);
}
.input:disabled, .textarea:disabled, select.input:disabled {
  background: var(--input-bg-disabled);
  color: var(--muted);
  cursor: not-allowed;
  box-shadow: none;
}
.input[readonly] { background: var(--surface-2); }
.textarea { min-height: 7.5rem; resize: vertical; }
.input--mono { font-family: var(--font-mono); font-size: var(--fs-md); }
.input--sm { min-height: var(--tap-sm); padding: var(--sp-2) var(--sp-3); font-size: var(--fs-md); border-radius: var(--r-sm); }

/* select: keep the native arrow (it follows color-scheme) but re-skin the box.
   `.select` wraps it when you want the designed chevron instead.           */
select.input { padding-right: var(--sp-4); cursor: pointer; }
.select { position: relative; display: block; }
.select select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: var(--sp-8);
  cursor: pointer;
}
.select::after {
  content: "";
  position: absolute;
  right: var(--sp-4);
  top: 50%;
  width: .5rem;
  height: .5rem;
  margin-top: -.35rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  pointer-events: none;
}

/* input with a leading icon / prefix, or a trailing button.
   <div class="input-group">
     <span class="input-addon">https://</span>
     <input class="input"> <button class="btn">Check</button>
   </div>                                                                    */
.input-group { display: flex; align-items: stretch; min-width: 0; }
.input-group > .input { flex: 1; min-width: 0; border-radius: 0; }
.input-group > :first-child { border-radius: var(--r-md) 0 0 var(--r-md); }
.input-group > :last-child  { border-radius: 0 var(--r-md) var(--r-md) 0; }
.input-group > :only-child  { border-radius: var(--r-md); }
.input-group > * + * { margin-left: -1px; }
.input-group > .btn { box-shadow: none; }
.input-group > .input:focus,
.input-group > .btn:focus-visible { position: relative; z-index: var(--z-raised); }
.input-addon {
  display: inline-flex;
  align-items: center;
  flex: none;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  color: var(--muted);          /* 5.41:1 light / 7.00:1 dark on surface-2 */
  font-size: var(--fs-md);
  white-space: nowrap;
}
.input-addon .icon { color: var(--muted); }

/* leading icon sitting INSIDE the field (no border between icon and input) */
.input-icon { position: relative; display: block; min-width: 0; }
.input-icon > .icon {
  position: absolute;
  left: var(--sp-4);
  top: 50%;
  margin-top: -10px;
  color: var(--muted);
  pointer-events: none;
  z-index: var(--z-raised);
}
.input-icon > .input { padding-left: calc(var(--sp-4) * 2 + 20px); }

/* help + error text */
.field-help { color: var(--muted); font-size: var(--fs-md); line-height: var(--lh-snug); }
.field-error {
  color: var(--danger);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
}
.field-error::before {
  content: "!";                /* shape + text, never color alone */
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15em;
  height: 1.15em;
  margin-top: .12em;
  border: 1.5px solid currentColor;
  border-radius: var(--r-round);
  font-size: .85em;
  font-weight: var(--fw-bold);
  line-height: 1;
}
.field.is-invalid .input,
.field.is-invalid .textarea,
.input.is-invalid,
[aria-invalid="true"].input {
  border-color: var(--danger);
}
.field.is-invalid .input:focus, .input.is-invalid:focus {
  outline-color: var(--danger);
  box-shadow: 0 0 0 4px rgba(192, 38, 38, .18);
}
.field.is-valid .input { border-color: var(--success); }

/* checkbox + radio — native controls, resized for touch */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  min-height: var(--tap);
  padding: var(--sp-2) 0;
  cursor: pointer;
  font-size: var(--fs-base);
  line-height: var(--lh-snug);
}
.check input[type="checkbox"],
.check input[type="radio"] {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  margin: .15rem 0 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.check input:disabled { cursor: not-allowed; }
.check:has(input:disabled) { color: var(--muted); cursor: not-allowed; }
.check input:disabled ~ .check-text { color: var(--muted); }  /* :has() fallback */
.check-text { min-width: 0; }
.check-hint { display: block; color: var(--muted); font-size: var(--fs-md); }

/* switch / toggle */
.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap);
  cursor: pointer;
  user-select: none;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.switch-track {
  position: relative;
  flex: none;
  width: 46px;
  height: 28px;
  border-radius: var(--r-pill);
  background: var(--track);
  border: 1px solid var(--border-strong);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
/* The knob is the only thing that carries on/off position, so it needs a
   >= 3:1 boundary in BOTH states and BOTH themes. Off: --switch-knob ringed
   in --border-strong (3.73:1 light / 4.06:1 dark against the knob fill,
   3.03:1 / 3.01:1 against the track). On: --switch-knob-on straight against
   --accent (6.08:1 light / 7.80:1 dark). A bare #fff knob on the dark accent
   was only 2.43:1 — that is why the knob is tokenised.                     */
.switch-track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 22px;
  height: 22px;
  border-radius: var(--r-round);
  background: var(--switch-knob);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease),
              background var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.switch input:checked + .switch-track { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch-track::after {
  transform: translateX(18px);
  background: var(--switch-knob-on);
  border-color: transparent;
}
.switch input:focus-visible + .switch-track {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--focus-halo);
}
.switch input:disabled + .switch-track { opacity: .5; cursor: not-allowed; }
.switch-label { font-size: var(--fs-base); }
/* state is also announced in text so it is never color-only */
.switch-state { color: var(--muted); font-size: var(--fs-md); }

/* file input (plain) */
.file-input {
  width: 100%;
  padding: var(--sp-2);
  background: var(--input-bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  font-size: var(--fs-md);
  cursor: pointer;
}
.file-input::file-selector-button {
  margin-right: var(--sp-3);
  min-height: var(--tap-sm);
  padding: 0 var(--sp-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-weight: var(--fw-semi);
  cursor: pointer;
}
.file-input::file-selector-button:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

/* fieldset */
.fieldset {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  background: var(--surface);
}
.fieldset > legend,
.fieldset-legend {
  font-size: var(--fs-base);
  font-weight: var(--fw-semi);
  padding: 0 var(--sp-2);
  margin-left: calc(var(--sp-2) * -1);
}
.fieldset-help { color: var(--muted); font-size: var(--fs-md); margin: 0 0 var(--sp-4); }

/* legacy portal input alias */
.pin {
  min-height: var(--tap-sm);
  padding: var(--sp-2) var(--sp-3);
  background: var(--input-bg);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  font: inherit;
  font-size: var(--fs-md);
}
.pin:focus { outline: 2px solid var(--focus); outline-offset: 0; border-color: var(--accent); }

/* -- 4.5 DROPZONE ---------------------------------------------------------
   THE component. A restaurant owner dropping a menu JPG must succeed here.

   <label class="dropzone" for="file">
     <input id="file" type="file" class="dropzone-input" accept="image/jpeg,image/png">
     <div class="dropzone-state dropzone-idle">     icon + title + hint     </div>
     <div class="dropzone-state dropzone-busy">     filename + progress     </div>
     <div class="dropzone-state dropzone-done">     success + replace       </div>
     <div class="dropzone-state dropzone-fail">     error + retry           </div>
   </label>

   States: (default) · :hover · :focus-within · .is-dragover · .is-uploading
           · .is-success · .is-error                                        */
.dropzone {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 260px;
  padding: var(--sp-7) var(--sp-5);
  background: var(--surface);
  border: 2px dashed var(--border-strong);
  border-radius: var(--r-xl);
  color: var(--text);
  cursor: pointer;
  overflow: hidden;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.dropzone-input {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* hover + keyboard focus read identically — the whole tile is the target */
.dropzone:hover,
.dropzone:focus-within {
  background: var(--accent-soft);
  border-color: var(--accent);
  box-shadow: var(--shadow-md);
}
.dropzone:focus-within { outline: 2px solid var(--focus); outline-offset: 3px; }
.dropzone.is-dragover {
  background: var(--accent-soft);
  border-color: var(--accent);
  border-style: solid;
  box-shadow: 0 0 0 4px var(--focus-halo), var(--shadow-lg);
  transform: scale(1.005);
}
.dropzone.is-disabled, .dropzone[aria-disabled="true"] {
  opacity: .55;
  cursor: not-allowed;
  pointer-events: none;
}

.dropzone-state {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  max-width: 34rem;
}
.dropzone .dropzone-idle { display: flex; }
.dropzone.is-uploading .dropzone-idle,
.dropzone.is-success   .dropzone-idle,
.dropzone.is-error     .dropzone-idle { display: none; }
.dropzone.is-uploading .dropzone-busy { display: flex; }
.dropzone.is-success   .dropzone-done { display: flex; }
.dropzone.is-error     .dropzone-fail { display: flex; }

.dropzone-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: var(--r-lg);
  background: var(--accent-soft);
  color: var(--accent);
  margin-bottom: var(--sp-1);
  transition: transform var(--dur) var(--ease-out), background var(--dur) var(--ease);
}
.dropzone-icon svg { width: 32px; height: 32px; }
.dropzone:hover .dropzone-icon,
.dropzone.is-dragover .dropzone-icon { transform: translateY(-3px) scale(1.04); }
.dropzone.is-dragover .dropzone-icon { background: var(--surface); }

.dropzone-title {
  font-size: var(--fs-xl);
  font-weight: var(--fw-semi);
  letter-spacing: -.01em;
}
.dropzone-hint {
  color: var(--muted);
  font-size: var(--fs-base);
  line-height: var(--lh-snug);
  max-width: 30rem;
}
.dropzone-browse {
  color: var(--accent);
  font-weight: var(--fw-semi);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.dropzone-formats {
  color: var(--muted);
  font-size: var(--fs-md);
}

/* uploading */
.dropzone.is-uploading { cursor: progress; border-style: solid; border-color: var(--accent); }
.dropzone-file {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  text-align: left;
  background: var(--surface-2);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
}
.dropzone-file .thumb { flex: none; }
.dropzone-filename {
  font-weight: var(--fw-semi);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1;
}
.dropzone-filesize { color: var(--muted); font-size: var(--fs-md); flex: none; }
.dropzone-progress { width: 100%; }
.dropzone-pct {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: var(--fs-md);
}

/* success */
.dropzone.is-success {
  border-style: solid;
  border-color: var(--success);
  background: var(--success-soft);
}
.dropzone.is-success .dropzone-icon { background: var(--surface); color: var(--success); }

/* error */
.dropzone.is-error {
  border-style: solid;
  border-color: var(--danger);
  background: var(--danger-soft);
}
.dropzone.is-error .dropzone-icon { background: var(--surface); color: var(--danger); }
.dropzone-error-msg { color: var(--danger); font-weight: var(--fw-medium); }

/* compact variant for inline "replace this image" spots */
.dropzone--sm { min-height: 150px; padding: var(--sp-5) var(--sp-4); border-radius: var(--r-lg); }
.dropzone--sm .dropzone-icon { width: 44px; height: 44px; }
.dropzone--sm .dropzone-icon svg { width: 22px; height: 22px; }
.dropzone--sm .dropzone-title { font-size: var(--fs-base); }
.dropzone--sm .dropzone-hint { font-size: var(--fs-md); }

/* thumbnails of what has been uploaded, under the dropzone */
.upload-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.upload-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.upload-item .stack { flex: 1; min-width: 0; gap: var(--sp-1); }

/* -- 4.6 tables -----------------------------------------------------------
   `.table` is canonical; legacy `table.grid` and `.compact` still work.    */
.table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
.table, table.grid {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-md);
  font-variant-numeric: tabular-nums;
}
.table th, .table td,
table.grid th, table.grid td {
  padding: var(--sp-3) var(--sp-4);
  text-align: left;
  vertical-align: middle;
}
.table thead th, table.grid thead th {
  background: var(--surface-2);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
.table--sticky thead th {
  position: sticky;
  top: 0;
  z-index: var(--z-raised);
}
.table tbody td, table.grid tbody td { border-top: 1px solid var(--border); }
.table tbody tr, table.grid tbody tr { transition: background var(--dur-fast) var(--ease); }
.table tbody tr:hover, table.grid tbody tr:hover { background: var(--surface-2); }
.table tbody tr.is-selected { background: var(--accent-soft); }
.table--compact th, .table--compact td,
table.grid.compact th, table.grid.compact td { padding: var(--sp-2) var(--sp-3); }
.table--zebra tbody tr:nth-child(even) { background: var(--surface-2); }
.table--zebra tbody tr:nth-child(even):hover { background: var(--accent-soft); }
.table td.num, .table th.num { text-align: right; }

/* sortable header affordance: a real <button> inside the <th> */
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  background: none;
  border: 0;
}
.th-sort:hover { color: var(--text); }
.th-sort::after {
  content: "\2195";                 /* ↕ neutral */
  font-size: .95em;
  opacity: .45;
  line-height: 1;
}
th[aria-sort="ascending"]  .th-sort::after { content: "\2191"; opacity: 1; color: var(--accent); }
th[aria-sort="descending"] .th-sort::after { content: "\2193"; opacity: 1; color: var(--accent); }
th[aria-sort] .th-sort { color: var(--text); }

/* empty state inside a table — legacy .empty / td.empty preserved */
.table .table-empty td,
.table td.empty, table.grid td.empty {
  text-align: center;
  color: var(--muted);
  padding: var(--sp-7) var(--sp-4);
  font-style: normal;
}
.empty { color: var(--muted); }

/* responsive: stacked cards, no horizontal page scroll.
   Add data-label="Column name" to each <td>.                              */
@media (max-width: 720px) {
  .table--stack, .table--stack thead, .table--stack tbody,
  .table--stack tr, .table--stack th, .table--stack td { display: block; }
  .table--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); }
  .table--stack tbody tr {
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
    margin-bottom: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
  }
  .table--stack tbody td {
    border: 0;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-2) 0;
    text-align: right;
  }
  .table--stack tbody td:last-child { border-bottom: 0; }
  .table--stack tbody td::before {
    content: attr(data-label);
    flex: none;
    color: var(--muted);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semi);
    text-transform: uppercase;
    letter-spacing: .04em;
    text-align: left;
  }
  .table--stack tbody td[data-label=""]::before { content: none; }
  /* Drop the outer frame so the rows read as standalone cards. `:has()` is a
     convenience only — add .table-wrap--stack for engines without it. Neither
     is load-bearing: the stacked table is display:block, so it can never
     overflow horizontally whether or not this rule applies.               */
  .table-wrap--stack,
  .table-wrap:has(.table--stack) { border: 0; background: none; box-shadow: none; overflow-x: visible; }
}

/* key/value list — legacy .kv preserved */
.kv { display: grid; gap: 0; }
.kv > div {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-md);
}
.kv > div:last-child { border-bottom: 0; }
.kv > div > span:first-child, .kv .k { color: var(--muted); }
.kv .v { text-align: right; min-width: 0; }

/* -- 4.7 navigation ------------------------------------------------------- */
/* topbar */
.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: var(--topbar-h);
  padding: var(--sp-2) var(--sp-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.topbar-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--text);
  font-weight: var(--fw-bold);
  font-size: var(--fs-lg);
  letter-spacing: -.02em;
  text-decoration: none;
}
.topbar-brand:hover { text-decoration: none; color: var(--text); }
.topbar-brand .logo { height: 28px; width: auto; }
.brand-sub { color: var(--muted); font-size: var(--fs-md); font-weight: var(--fw-normal); }
.topbar-nav { display: flex; gap: var(--sp-1); flex: 1; min-width: 0; }
.topbar-nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-sm);
  padding: 0 var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  text-decoration: none;
}
.topbar-nav a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.topbar-nav a.active { background: var(--accent-soft); color: var(--accent); }
.topbar-actions { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; }

/* sidebar */
.sidebar {
  display: flex;
  flex-direction: column;
  flex: none;
  width: var(--sidebar-w);
  height: 100vh;
  position: sticky;
  top: 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
  z-index: var(--z-drawer);
}
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
  color: var(--text);
  font-weight: var(--fw-bold);
  font-size: var(--fs-lg);
  letter-spacing: -.02em;
  text-decoration: none;
  min-height: var(--topbar-h);
}
.sidebar-brand:hover { text-decoration: none; color: var(--text); }
.sidebar-nav {
  flex: 1;
  overflow-y: auto;
  padding: var(--sp-4) var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}
.nav-group { display: flex; flex-direction: column; gap: 2px; }
.nav-group-h {
  color: var(--muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  text-transform: uppercase;
  letter-spacing: .1em;
  padding: 0 var(--sp-3) var(--sp-2);
}
.sidebar-nav a {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  border-radius: var(--r-md);
  color: var(--text);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.sidebar-nav a:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.sidebar-nav a.active {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: var(--fw-semi);
}
.sidebar-nav a.active::before {
  content: "";
  position: absolute;
  left: calc(var(--sp-3) * -1 + 2px);
  top: 20%;
  bottom: 20%;
  width: 3px;
  border-radius: var(--r-pill);
  background: var(--accent);
}
.sidebar-nav a .icon { flex: none; width: 18px; height: 18px; }
.sidebar-nav a .nav-count {
  margin-left: auto;
  color: var(--muted);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.sidebar-foot {
  border-top: 1px solid var(--border);
  padding: var(--sp-3) var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.sf-user { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.sf-user .sf-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
}
.sf-icons { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; }

/* mobile drawer */
.mobile-navtoggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
.nav-scrim { display: none; }
@media (max-width: 900px) {
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    transform: translateX(-100%);
    transition: transform var(--dur) var(--ease);
    box-shadow: var(--shadow-xl);
    z-index: var(--z-drawer);
  }
  body.nav-open .sidebar { transform: none; }
  body.nav-open .nav-scrim {
    display: block;
    position: fixed;
    inset: 0;
    background: var(--overlay);
    z-index: calc(var(--z-drawer) - 1);
  }
  .mobile-navtoggle { display: inline-flex; }
  .topbar { padding-left: var(--sp-3); }
}

/* -- 4.8 badges / status pills -------------------------------------------
   Every variant carries a distinct glyph SHAPE as well as a color, and the
   label text is always present. Never color alone.                        */
.badge, .pill {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .25em .7em;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  background: var(--surface-2);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: middle;
}
.badge::before, .pill::before { font-size: .9em; line-height: 1; }
.badge--neutral   { background: var(--surface-2);   color: var(--muted);   border-color: var(--border); }
.badge--neutral::before   { content: "\25CB"; }        /* ○ hollow circle  */
.badge--online,
.badge--success   { background: var(--success-soft); color: var(--success); border-color: var(--success); }
.badge--online::before, .badge--success::before { content: "\25CF"; }  /* ● filled */
.badge--offline   { background: var(--surface-2);    color: var(--muted);   border-color: var(--border-strong); }
.badge--offline::before   { content: "\25CB"; }        /* ○ hollow         */
.badge--live      { background: var(--danger-soft);  color: var(--danger);  border-color: var(--danger); }
.badge--live::before      { content: "\25B6"; }        /* ▶ play           */
.badge--scheduled { background: var(--info-soft);    color: var(--info);    border-color: var(--info); }
.badge--scheduled::before { content: "\25C6"; }        /* ◆ diamond        */
.badge--paused    { background: var(--warning-soft); color: var(--warning); border-color: var(--warning); }
.badge--paused::before    { content: "\25A0"; }        /* ■ square         */
.badge--warning   { background: var(--warning-soft); color: var(--warning); border-color: var(--warning); }
.badge--warning::before   { content: "\25B2"; }        /* ▲ triangle       */
.badge--error,
.badge--danger    { background: var(--danger-soft);  color: var(--danger);  border-color: var(--danger); }
.badge--error::before, .badge--danger::before { content: "\00D7"; font-weight: var(--fw-bold); }  /* × */
.badge--info      { background: var(--info-soft);    color: var(--info);    border-color: var(--info); }
.badge--info::before      { content: "i"; font-style: italic; font-weight: var(--fw-bold); }
.badge--accent    { background: var(--accent-soft);  color: var(--accent);  border-color: var(--accent); }
.badge--accent::before    { content: "\25C6"; }
.badge--plain::before { content: none; }
.badge--sm { font-size: var(--fs-xs); padding: .15em .55em; }
.badge--mono { font-family: var(--font-mono); font-weight: var(--fw-normal); }

/* bare status dot — legacy .dot / .status-dot names preserved.
   Shapes differ per state so the dot alone still carries meaning.         */
.status-dot, .dot {
  display: inline-block;
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: var(--r-round);
  border: 2px solid var(--muted);
  background: transparent;
  vertical-align: middle;
}
.status-dot.is-online, .dot.up {
  background: var(--success);
  border-color: var(--success);
}
.status-dot.is-stale, .dot.stale {
  background: var(--warning);
  border-color: var(--warning);
  border-radius: 2px;                 /* square = different shape          */
}
.status-dot.is-offline, .dot.down {
  background: transparent;
  border-color: var(--muted);         /* hollow ring                       */
}
.status-dot.is-error {
  background: var(--danger);
  border-color: var(--danger);
  border-radius: 2px;
  transform: rotate(45deg);           /* diamond                           */
}

/* legacy text-status colors */
.ok      { color: var(--success); font-weight: var(--fw-semi); }
.bad     { color: var(--danger);  font-weight: var(--fw-semi); }
.warn, .pending { color: var(--warning); font-weight: var(--fw-semi); }

/* -- 4.9 alerts ----------------------------------------------------------- */
.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-left-width: 4px;
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--text);
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
}
.alert-icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--r-round);
  font-weight: var(--fw-bold);
  font-size: var(--fs-md);
  line-height: 1;
}
.alert-body { min-width: 0; flex: 1; }
.alert-title { font-weight: var(--fw-semi); display: block; margin-bottom: .15em; }
.alert-close { flex: none; }
.alert--info    { background: var(--info-soft);    border-color: var(--info); }
.alert--info    .alert-icon { background: var(--info); color: var(--surface); }
.alert--success { background: var(--success-soft); border-color: var(--success); }
.alert--success .alert-icon { background: var(--success); color: var(--success-soft); }
.alert--warning { background: var(--warning-soft); border-color: var(--warning); }
.alert--warning .alert-icon { background: var(--warning); color: var(--warning-soft); }
.alert--error   { background: var(--danger-soft);  border-color: var(--danger); }
.alert--error   .alert-icon { background: var(--danger); color: var(--danger-soft); }

/* -- 4.10 modal / dialog -------------------------------------------------- */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  background: var(--overlay);
}
.modal-backdrop.is-open { display: flex; }
.modal {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 560px;
  max-height: calc(100vh - var(--sp-7));
  background: var(--elevated);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  animation: totem-pop var(--dur) var(--ease-out);
}
.modal--wide { max-width: 880px; }
.modal--narrow { max-width: 420px; }
@keyframes totem-pop {
  from { opacity: 0; transform: translateY(8px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
}
.modal-title { font-size: var(--fs-xl); font-weight: var(--fw-semi); margin: 0; }
.modal-body { padding: 0 var(--sp-5) var(--sp-5); overflow-y: auto; }
.modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  flex-wrap: wrap;
}
/* native <dialog> works too */
dialog.modal { padding: 0; color: var(--text); }
dialog.modal::backdrop { background: var(--overlay); }

/* -- 4.11 drawer ---------------------------------------------------------- */
.drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: none;
  background: var(--overlay);
}
.drawer-backdrop.is-open { display: block; }
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: calc(var(--z-overlay) + 1);
  width: min(440px, 92vw);
  display: flex;
  flex-direction: column;
  background: var(--elevated);
  border-left: 1px solid var(--border);
  box-shadow: var(--shadow-xl);
  transform: translateX(100%);
  transition: transform var(--dur-slow) var(--ease-out);
}
.drawer.is-open { transform: none; }
.drawer--left { right: auto; left: 0; border-left: 0; border-right: 1px solid var(--border); transform: translateX(-100%); }
.drawer--left.is-open { transform: none; }
.drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.drawer-body { flex: 1; overflow-y: auto; padding: var(--sp-5); }
.drawer-foot {
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  display: flex;
  gap: var(--sp-2);
  justify-content: flex-end;
}

/* -- 4.12 toast ----------------------------------------------------------- */
.toast-region {
  position: fixed;
  right: var(--sp-5);
  bottom: var(--sp-5);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  width: min(380px, calc(100vw - var(--sp-6)));
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--elevated);
  border: 1px solid var(--border);
  border-left: 4px solid var(--muted);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-md);
  animation: totem-toast-in var(--dur-slow) var(--ease-out);
}
@keyframes totem-toast-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
/* the stripe is reinforced by a glyph, so a toast is never colour-only */
.toast--success { border-left-color: var(--success); }
.toast--warning { border-left-color: var(--warning); }
.toast--error   { border-left-color: var(--danger); }
.toast--info    { border-left-color: var(--info); }
.toast--success .alert-icon { background: var(--success-soft); color: var(--success); }
.toast--warning .alert-icon { background: var(--warning-soft); color: var(--warning); }
.toast--error   .alert-icon { background: var(--danger-soft);  color: var(--danger); }
.toast--info    .alert-icon { background: var(--info-soft);    color: var(--info); }
.toast-title { font-weight: var(--fw-semi); }
.toast-body { flex: 1; min-width: 0; }
.toast-msg { color: var(--muted); }
@media (max-width: 640px) {
  .toast-region { left: var(--sp-3); right: var(--sp-3); bottom: var(--sp-3); width: auto; }
}

/* -- 4.13 tabs ------------------------------------------------------------ */
.tabs {
  display: flex;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  padding: 0 var(--sp-4);
  border: 0;
  background: none;
  color: var(--muted);
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}
.tab:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }
.tab[aria-selected="true"], .tab.active { color: var(--accent); }
.tab[aria-selected="true"]::after, .tab.active::after {
  content: "";
  position: absolute;
  left: var(--sp-3);
  right: var(--sp-3);
  bottom: -1px;
  height: 3px;
  border-radius: var(--r-pill) var(--r-pill) 0 0;
  background: var(--accent);
}
/* the strip scrolls horizontally, so an outset ring would be clipped */
.tab:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.tab[disabled], .tab[aria-disabled="true"] { opacity: .5; cursor: not-allowed; pointer-events: none; }
.tab-panel { padding-top: var(--sp-5); }
.tab-panel[hidden] { display: none; }

/* -- 4.14 breadcrumb ------------------------------------------------------ */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  list-style: none;
  margin: 0 0 var(--sp-4);
  padding: 0;
  font-size: var(--fs-md);
  color: var(--muted);
}
.breadcrumb li { display: flex; align-items: center; gap: var(--sp-2); margin: 0; }
.breadcrumb li + li::before {
  content: "\002F";
  color: var(--border-strong);
}
.breadcrumb a { color: var(--muted); }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb [aria-current="page"] { color: var(--text); font-weight: var(--fw-semi); }

/* -- 4.15 pagination ------------------------------------------------------ */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1);
  list-style: none;
  margin: var(--sp-5) 0 0;
  padding: 0;
}
.pagination li { margin: 0; }
.page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0 var(--sp-3);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  color: var(--text);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  cursor: pointer;
  background: none;
}
.page-link:hover { background: var(--surface-2); text-decoration: none; }
.page-link[aria-current="page"], .page-link.is-current {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.page-link[aria-disabled="true"], .page-link:disabled { opacity: .45; pointer-events: none; }
.page-gap { color: var(--muted); padding: 0 var(--sp-2); }

/* -- 4.16 empty states ---------------------------------------------------- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--sp-3);
  padding: var(--sp-8) var(--sp-5);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-xl);
}
.empty-state--bare { background: none; border: 0; padding: var(--sp-7) var(--sp-4); }
.empty-state-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: var(--r-round);
  background: var(--accent-soft);
  color: var(--accent);
  margin-bottom: var(--sp-2);
}
.empty-state-icon svg { width: 34px; height: 34px; }
.empty-state-title {
  font-size: var(--fs-xl);
  font-weight: var(--fw-semi);
  margin: 0;
}
.empty-state-text {
  color: var(--muted);
  max-width: 42ch;
  margin: 0;
  font-size: var(--fs-base);
}
.empty-state .btn { margin-top: var(--sp-2); }

/* -- 4.17 skeletons + spinner --------------------------------------------- */
.skeleton {
  display: block;
  background: var(--skeleton);
  border-radius: var(--r-sm);
  position: relative;
  overflow: hidden;
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .45), transparent);
  animation: totem-shimmer 1.4s infinite;
}
@media (prefers-color-scheme: dark) {
  .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .07), transparent); }
}
:root[data-theme="dark"] .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .07), transparent); }
:root[data-theme="light"] .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .45), transparent); }
@keyframes totem-shimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } }
.skeleton-text  { height: .85em; margin: .35em 0; border-radius: var(--r-pill); }
.skeleton-text--short { width: 45%; }
.skeleton-text--half  { width: 65%; }
.skeleton-title { height: 1.4em; width: 55%; border-radius: var(--r-xs); margin-bottom: var(--sp-3); }
.skeleton-thumb { width: 100%; aspect-ratio: 16 / 9; border-radius: var(--r-md); }
.skeleton-avatar { width: 40px; height: 40px; border-radius: var(--r-round); }
.skeleton-btn { height: var(--tap); width: 120px; border-radius: var(--r-md); }

.spinner {
  display: inline-block;
  width: 22px;
  height: 22px;
  border: 2.5px solid var(--border-strong);
  border-top-color: var(--accent);
  border-radius: var(--r-round);
  animation: totem-spin .7s linear infinite;
  vertical-align: middle;
}
.spinner--sm { width: 16px; height: 16px; border-width: 2px; }
.spinner--lg { width: 36px; height: 36px; border-width: 3px; }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 1.8s; }
}

/* -- 4.18 progress + meter ------------------------------------------------ */
/* The track is a light tint (1.23:1 on a card), so the component boundary is
   carried by a 1px --border-strong outline instead — 3.73:1 light / 4.06:1
   dark. The fill vs the track is 4.94:1 / 5.18:1.                          */
.progress {
  width: 100%;
  height: 10px;
  background: var(--track);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.progress-bar {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  border-radius: inherit;
  transition: width var(--dur-slow) var(--ease);
}
.progress--lg { height: 16px; }
.progress-bar.is-success { background: var(--success); }
.progress-bar.is-error   { background: var(--danger); }
/* indeterminate */
.progress.is-indeterminate .progress-bar {
  width: 35%;
  animation: totem-indeterminate 1.2s var(--ease) infinite;
}
@keyframes totem-indeterminate {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(300%); }
}
@media (prefers-reduced-motion: reduce) {
  .progress.is-indeterminate .progress-bar { animation: none; width: 100%; opacity: .5; }
}

/* meter — segmented capacity indicator (e.g. storage used) */
.meter {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--fs-md);
}
.meter-track {
  flex: 1;
  height: 10px;
  background: var(--track);
  border: 1px solid var(--border-strong);   /* 3:1 component boundary */
  border-radius: var(--r-pill);
  overflow: hidden;
  min-width: 80px;
}
.meter-fill { height: 100%; background: var(--success); border-radius: inherit; }
.meter-fill.is-warning { background: var(--warning); }
.meter-fill.is-danger  { background: var(--danger); }
.meter-value { font-variant-numeric: tabular-nums; color: var(--muted); flex: none; }

/* -- 4.19 tooltip ---------------------------------------------------------
   CSS-only, shows on hover AND keyboard focus. Decorative duplicate of an
   aria-label — never the only place information lives.                    */
.tooltip { position: relative; display: inline-flex; }
.tooltip::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  z-index: var(--z-tooltip);
  padding: var(--sp-2) var(--sp-3);
  max-width: 260px;
  width: max-content;
  background: var(--text);
  color: var(--surface);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  white-space: normal;
  text-align: center;
  box-shadow: var(--shadow-md);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.tooltip:hover::after,
.tooltip:focus-visible::after,
.tooltip:focus-within::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.tooltip--right::after { bottom: auto; top: 50%; left: calc(100% + 8px); transform: translateY(-50%); }
.tooltip--right:hover::after, .tooltip--right:focus-within::after { transform: translateY(-50%); }

/* -- 4.20 avatar + thumbnail ---------------------------------------------- */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--r-round);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: .02em;
  overflow: hidden;
  user-select: none;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 28px; height: 28px; font-size: var(--fs-xs); }
.avatar--lg { width: 56px; height: 56px; font-size: var(--fs-lg); }
.avatar--square { border-radius: var(--r-md); }

.thumb {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 64px;
  aspect-ratio: 16 / 9;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  overflow: hidden;
  color: var(--muted);
  font-size: var(--fs-xs);
  text-align: center;
}
.thumb img { width: 100%; height: 100%; object-fit: cover; }
.thumb--lg { width: 120px; }
.thumb--square { aspect-ratio: 1; }
@supports not (aspect-ratio: 16 / 9) {
  .thumb { height: 36px; }
  .thumb--lg { height: 68px; }
}

/* -- 4.21 misc ------------------------------------------------------------ */
/* inline "banner" strip above a page section */
.notice-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--accent-soft);
  color: var(--text);
  font-size: var(--fs-md);
  margin-bottom: var(--sp-5);
}

/* serial numbers etc. — legacy .sn / .dev-sn preserved */
.sn, .dev-sn, .totem-sn {
  font-family: var(--font-mono);
  font-size: .95em;
  color: var(--muted);
}

details.disclosure {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  padding: var(--sp-3) var(--sp-4);
}
details.disclosure > summary {
  cursor: pointer;
  font-weight: var(--fw-semi);
  min-height: var(--tap-sm);
  display: flex;
  align-items: center;
}
details.disclosure > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* icon sizing helper for inline SVG */
.icon { width: 20px; height: 20px; flex: none; display: inline-block; vertical-align: middle; }
.icon--sm { width: 16px; height: 16px; }
.icon--lg { width: 28px; height: 28px; }
svg.icon { fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
svg.icon--filled { fill: currentColor; stroke: none; }

/* -- 4.22 touch targets ---------------------------------------------------
   Everything primary is >= 44x44 (--tap) at every viewport. The few dense
   controls that sit at 36px (--tap-sm) are for mouse-dense admin screens;
   on a touch device they are promoted to the full 44px so a restaurant owner
   on a phone never has to aim. `pointer: coarse` is progressive enhancement:
   an engine that does not support it simply keeps the desktop sizes.       */
@media (pointer: coarse) {
  .btn--sm, .pbtn--sm { --btn-h: var(--tap); --btn-px: var(--sp-4); }
  .input--sm, .pin { min-height: var(--tap); }
  .topbar-nav a { min-height: var(--tap); padding: 0 var(--sp-4); }
  .file-input::file-selector-button { min-height: var(--tap); }
  details.disclosure > summary { min-height: var(--tap); }
  .th-sort { min-height: var(--tap-sm); }
  .badge, .pill { padding: .4em .8em; }
}


/* ==========================================================================
   5 · UTILITIES
   ========================================================================== */

/* -- 5.1 spacing (t-shirt scale matches --sp-*) --------------------------- */
.m-0  { margin: 0 !important; }
.mt-0 { margin-top: 0 !important; }    .mb-0 { margin-bottom: 0 !important; }
.mt-1 { margin-top: var(--sp-1); }     .mb-1 { margin-bottom: var(--sp-1); }
.mt-2 { margin-top: var(--sp-2); }     .mb-2 { margin-bottom: var(--sp-2); }
.mt-3 { margin-top: var(--sp-3); }     .mb-3 { margin-bottom: var(--sp-3); }
.mt-4 { margin-top: var(--sp-4); }     .mb-4 { margin-bottom: var(--sp-4); }
.mt-5 { margin-top: var(--sp-5); }     .mb-5 { margin-bottom: var(--sp-5); }
.mt-6 { margin-top: var(--sp-6); }     .mb-6 { margin-bottom: var(--sp-6); }
.mt-7 { margin-top: var(--sp-7); }     .mb-7 { margin-bottom: var(--sp-7); }
.mx-auto { margin-inline: auto; }
.ml-auto { margin-left: auto; }        /* legacy name, kept */
.mr-auto { margin-right: auto; }

.p-0 { padding: 0 !important; }
.p-2 { padding: var(--sp-2); }
.p-3 { padding: var(--sp-3); }
.p-4 { padding: var(--sp-4); }
.p-5 { padding: var(--sp-5); }
.p-6 { padding: var(--sp-6); }

.gap-1 { gap: var(--sp-1); }
.gap-2 { gap: var(--sp-2); }
.gap-3 { gap: var(--sp-3); }
.gap-4 { gap: var(--sp-4); }
.gap-5 { gap: var(--sp-5); }
.gap-6 { gap: var(--sp-6); }

/* -- 5.2 text ------------------------------------------------------------- */
.text-left   { text-align: left; }
.text-center { text-align: center; }
.text-right  { text-align: right; }

.text-xs   { font-size: var(--fs-xs); }
.text-sm   { font-size: var(--fs-sm); }
.text-md   { font-size: var(--fs-md); }
.text-base { font-size: var(--fs-base); }
.text-lg   { font-size: var(--fs-lg); }
.text-xl   { font-size: var(--fs-xl); }
.text-2xl  { font-size: var(--fs-2xl); }
.text-3xl  { font-size: var(--fs-3xl); }

.fw-normal { font-weight: var(--fw-normal); }
.fw-medium { font-weight: var(--fw-medium); }
.fw-semi   { font-weight: var(--fw-semi); }
.fw-bold   { font-weight: var(--fw-bold); }

.muted        { color: var(--muted); }   /* legacy name, kept */
.text-muted   { color: var(--muted); }
.text-default { color: var(--text); }
.text-accent  { color: var(--accent); }
.text-success { color: var(--success); }
.text-warning { color: var(--warning); }
.text-danger  { color: var(--danger); }
.text-info    { color: var(--info); }

.mono  { font-family: var(--font-mono); }
.nums  { font-variant-numeric: tabular-nums; }
.upper { text-transform: uppercase; letter-spacing: .06em; }
.nowrap { white-space: nowrap; }
.break-word { overflow-wrap: anywhere; word-break: break-word; }

.truncate {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* multi-line clamp (progressive enhancement — degrades to full text) */
.clamp-2, .clamp-3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.clamp-2 { -webkit-line-clamp: 2; }
.clamp-3 { -webkit-line-clamp: 3; }

/* -- 5.3 layout helpers --------------------------------------------------- */
.flex      { display: flex; }
.inline-flex { display: inline-flex; }
.block     { display: block; }
.inline-block { display: inline-block; }
.grid-flow { display: grid; }
.flex-col  { flex-direction: column; }
.flex-wrap { flex-wrap: wrap; }
.flex-nowrap { flex-wrap: nowrap; }
.items-start   { align-items: flex-start; }
.items-center  { align-items: center; }
.items-end     { align-items: flex-end; }
.items-baseline{ align-items: baseline; }
.justify-start   { justify-content: flex-start; }
.justify-center  { justify-content: center; }
.justify-end     { justify-content: flex-end; }
.justify-between { justify-content: space-between; }
.grow    { flex: 1 1 auto; min-width: 0; }
.no-grow { flex: none; }
.full    { width: 100%; }
.relative { position: relative; }

.surface { background: var(--surface); }
.rounded { border-radius: var(--r-md); }
.bordered { border: 1px solid var(--border); }
.shadow  { box-shadow: var(--shadow-sm); }

/* -- 5.4 a11y / visibility ------------------------------------------------ */
.visually-hidden, .sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* visible again when focused — for skip links */
.visually-hidden-focusable:not(:focus):not(:focus-within) {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: absolute;
  left: var(--sp-3);
  top: var(--sp-3);
  z-index: var(--z-toast);
  padding: var(--sp-3) var(--sp-4);
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--r-md);
  font-weight: var(--fw-semi);
}
.hidden { display: none !important; }
@media (max-width: 760px) { .hide-sm { display: none !important; } }
@media (min-width: 761px) { .show-sm { display: none !important; } }

/* -- 5.9 legacy class aliases (migration shims) ---------------------------
   Keep old templates rendering sanely until they are rebuilt. Safe to drop
   once every template uses the new names.                                  */
.portal-card    { background: var(--surface); border: 1px solid var(--border);
                  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
                  padding: var(--sp-5); margin-bottom: var(--sp-5); }
.portal-card--accent { border-top: 3px solid var(--accent); }
.portal-section { margin-bottom: var(--sp-6); }
.portal-stats   { display: grid; gap: var(--sp-4); margin-bottom: var(--sp-6);
                  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); }
.portal-stat    { background: var(--surface); border: 1px solid var(--border);
                  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
                  padding: var(--sp-4) var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-1); }
.portal-stat .l { color: var(--muted); font-size: var(--fs-sm); font-weight: var(--fw-medium); }
.portal-stat .n { font-size: var(--fs-2xl); font-weight: var(--fw-bold); line-height: var(--lh-tight);
                  font-variant-numeric: tabular-nums; }
.portal-stat .sub { color: var(--muted); font-size: var(--fs-md); }
.portal-inline  { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2); }
.portal-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-top: var(--sp-4); }
.portal-field-label { display: block; color: var(--muted); font-size: var(--fs-md);
                  font-weight: var(--fw-semi); margin-bottom: var(--sp-2); }
.portal-loading, .portal-empty { color: var(--muted); font-size: var(--fs-md); }
.portal-error   { color: var(--danger); font-size: var(--fs-md); }
.portal-status  { color: var(--muted); font-size: var(--fs-md); }
.portal-status.ok  { color: var(--success); }
.portal-status.warn{ color: var(--warning); }
.portal-status.err { color: var(--danger); }
.portal-status.dim { color: var(--muted); }


/* ==========================================================================
   6 · PRINT — reports pages should come out as clean documents
   ========================================================================== */
@media print {
  /* force the light palette; printers do not do dark mode */
  :root, :root[data-theme="dark"], :root[data-theme="light"] {
    color-scheme: light;
    --bg: #fff;
    --surface: #fff;
    --surface-2: #fff;
    --elevated: #fff;
    --border: #b9c0cc;
    --border-strong: #8b93a1;
    --text: #000;
    --muted: #333;
    --accent: #10399c;
    --accent-soft: #fff;
    --success-soft: #fff; --warning-soft: #fff; --danger-soft: #fff; --info-soft: #fff;
    --shadow-xs: none; --shadow-sm: none; --shadow-md: none;
    --shadow-lg: none; --shadow-xl: none; --shadow-accent: none;
  }

  @page { margin: 16mm 14mm; }

  html, body { background: #fff !important; color: #000 !important; }
  body { font-size: 11pt; line-height: 1.4; }

  /* chrome that must not print */
  .sidebar, .topbar, .mobile-navtoggle, .nav-scrim, .toast-region, .drawer,
  .drawer-backdrop, .modal-backdrop, .tabs, .pagination, .app-footer,
  .hero-actions, .btn, .pbtn, .btn-group, .dropzone, .skip-link,
  .no-print { display: none !important; }

  .app { display: block; }
  .main-wrap { display: block; }
  main { padding: 0; max-width: none; }
  .container, .container--wide, .container--narrow { max-width: none; }

  .card, .col, .stat-card, .portal-card, .table-wrap, .fieldset {
    border: 1px solid #b9c0cc !important;
    box-shadow: none !important;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  h1, h2, h3 { break-after: avoid; page-break-after: avoid; }
  table { break-inside: auto; }
  thead { display: table-header-group; }
  tr, img { break-inside: avoid; page-break-inside: avoid; }
  .table thead th, table.grid thead th {
    background: #eef1f5 !important;
    color: #000 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .table tbody tr:hover { background: none !important; }

  /* badges keep their glyph, lose the fill */
  .badge, .pill {
    border: 1px solid #8b93a1 !important;
    background: #fff !important;
    color: #000 !important;
  }

  a { color: #000; text-decoration: underline; }
  /* expose destinations for real links only */
  a[href^="http"]::after, a[href^="/"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    color: #444;
    word-break: break-all;
  }
  a[href^="#"]::after, a[href^="javascript"]::after, .no-print-url::after { content: ""; }

  .skeleton, .spinner { display: none !important; }
  .progress { border: 1px solid #8b93a1; }
  .progress-bar { background: #666 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
