/* ==========================================================================
   DIGISCHOOL: Design Tokens
   Every colour, size, and timing in the app resolves to a variable declared
   here. Nothing downstream hardcodes a hex value.

   Two layers:
     1. Palette: raw scales. Theme-independent. Never used directly in
                       components; they exist so role tokens have something to
                       point at.
     2. Role tokens: semantic names (surface, border, text). These ARE used
                       by components, and they are what the dark theme
                       overrides. One override block, whole app re-themes.

   The scheme is Digisol Group's own, read off digisolgroup.net rather than
   invented here:

       --color-dblue    #31a2db   the brand cyan
       --color-dpurple  #3d3c7a   the deep indigo beside it
       --font-display   Space Grotesk
       --font-body      Inter

   and the site's own rhythm — near-black navy chrome carrying cyan, a light
   cool canvas underneath — is the rhythm this application takes.

   This matters beyond taste. DIGISCHOOL and DIGISCHOOL are two school
   management systems, sold by two companies, built by one person. They
   shared a palette (forest green on warm cream), a display face and a badge
   shape, which is a conflict of interest wearing a stylesheet. They now
   share none of the four: cyan on cool steel, Space Grotesk, square status
   tags, a dark rail. Nothing here is a tint of anything there.
   ========================================================================== */

/* --- Font ---------------------------------------------------------------- */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Space Grotesk: headings and the tracked-out micro labels, and Digisol's
   own display face — digisolgroup.net declares it as --font-display and sets
   every heading and eyebrow in it. Body copy, tables and tabular figures stay
   on Inter, which is that same site's --font-body. Two voices on purpose, so
   a heading is recognisable as one before it is read.

   Two files rather than one for the same reason Inter has two: a French
   school's accented headings live in the -ext range and an English one never
   downloads it. Variable weight 300-700, the full range the family ships. */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/space-grotesk-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/space-grotesk-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* ======================================================================
     LAYER 1: PALETTE
     ====================================================================== */

  /* Brand: Digisol cyan.
     Hue 200, saturation 70 - the hue and saturation of #31a2db, which sits
     at step 500 unaltered because it is the brand and not an approximation
     of it.

     500 is what the dark chrome and the dark theme use. 600 is what the
     light canvas uses, and it is a real step darker rather than a shade:
     white on #31a2db measures 2.87:1, which is not a button. White on
     #12719f measures 5.40:1, which is. Both read as the same colour because
     they are the same hue; only one of them can carry white text. */
  --ec-primary-50:  #eef8fd;
  --ec-primary-100: #d6eefb;
  --ec-primary-200: #addcf5;
  --ec-primary-300: #78c4ea;
  --ec-primary-400: #4fb0e2;
  --ec-primary-500: #31a2db;
  --ec-primary-600: #12719f;
  --ec-primary-700: #0e5b80;
  --ec-primary-800: #0d4a68;
  --ec-primary-900: #0e3c54;

  /* The dark theme's accent tint, and the ink that sits on the cyan wherever
     the cyan is a FILL. Neither belongs on the ramp: one is a desaturated
     wash, the other is a near-black chosen for contrast rather than for hue.

     --ec-primary-ink on --ec-primary-500 measures 5.84:1, which is what lets
     the brand cyan itself be a button, an active rail row and a chart fill
     instead of only a decoration. */
  --ec-primary-wash: #0d2534;
  --ec-primary-ink:  #06202e;

  /* Neutral, light field: cool steel. Two ramps rather than one, because the
     scheme is not one grey read from both ends - the canvas is a faintly
     blue near-white and the ink is a navy-cast grey, and forcing them onto a
     single scale would put a colour in the middle that neither theme uses. */
  --ec-paper-0:   #ffffff;
  --ec-paper-50:  #f4f7fa;
  --ec-paper-100: #eaeff5;
  --ec-paper-200: #dfe6ee;
  --ec-paper-300: #d2dbe5;
  --ec-paper-400: #b4c0ce;
  /* Off the end of the ramp on purpose. --ec-border-strong is the edge of a
     form field, which WCAG 1.4.11 asks to clear 3:1 against what it sits on,
     and no tint of the canvas does: #b4c0ce measures 1.6:1 on the canvas and
     1.8:1 on a white card. This is the lightest steel that clears 3:1 on
     both - 3.61:1 on white, 3.36:1 on the canvas. */
  --ec-paper-500: #7a8899;

  /* Neutral, ink and dark field: navy-cast greys, lightest first. These are
     also the chrome: the rail and the header are painted from the bottom of
     this ramp in BOTH themes, so the dark band is a constant of the design
     and not a mode. */
  --ec-ink-50:   #e8eef5;
  --ec-ink-200:  #9fb0c4;
  --ec-ink-300:  #8296ad;
  /* The dark theme's form-field edge, for the same reason --ec-paper-500
     exists: --ec-ink-700 reads 1.8:1 against the dark surface. */
  --ec-ink-350:  #64788f;
  --ec-ink-400:  #5a6b80;
  --ec-ink-500:  #4a5a6e;
  --ec-ink-600:  #3a4859;
  --ec-ink-700:  #2a3746;
  --ec-ink-750:  #212d3b;
  --ec-ink-800:  #1a2532;
  --ec-ink-850:  #142030;
  --ec-ink-900:  #0f1a27;
  --ec-ink-950:  #070e18;

  /* The chrome fill: the rail, the header and the family portal's tab bar.
     A step off --ec-ink-900 towards blue, because the band is large and flat
     and wants the brand's hue in it rather than a neutral charcoal. Cyan on
     it measures 6.53:1, --ec-ink-200 8.40:1, --ec-ink-300 6.13:1. */
  --ec-chrome:       #0b1320;
  --ec-chrome-2:     #131e2e;
  --ec-chrome-line:  #22314a;

  /* Semantic.
     Cyan now means "brand", which frees green to mean only "went well" and
     is why success is a plain green rather than a shade of anything here.

     Every ink is chosen against its own soft tint, because the pairing that
     has to clear AA is the badge - --ec-success on --ec-success-soft, not on
     the page. */
  --ec-success:      #15803d;
  --ec-success-soft: #dcfce7;
  --ec-warning:      #92400e;
  --ec-warning-soft: #fef3c7;
  --ec-danger:       #b91c1c;
  --ec-danger-soft:  #fee2e2;
  /* The ink that sits ON the danger colour, for the one control that fills
     with it: the destructive button on hover. It is a separate token for the
     same reason --ec-accent-on is — the dark theme's --ec-danger is a pale
     red, and white on it measures 2.0:1. Paper here, near-black there.
     #fff on #b91c1c is 7.06:1. */
  --ec-danger-on:    var(--ec-paper-0);
  /* Digisol's second brand colour, doing the job "info" used to do in cyan.
     It cannot stay cyan: cyan is the brand now, and a notice painted in the
     brand colour is indistinguishable from a primary action. #3d3c7a is
     digisolgroup.net's --color-dpurple unaltered - 9.88:1 on white, 8.01:1
     on its own tint. */
  --ec-info:         #3d3c7a;
  --ec-info-soft:    #e7e6f4;
  /* The marketer console's own colour. It used to be violet, which now sits
     a few degrees from --ec-info - so the two consoles outside the tenant
     boundary would have been told apart by reading the badge. Magenta is far
     from both the brand cyan and the indigo: 6.32:1 on the page. */
  --ec-marketer:     #a21caf;
  --ec-marketer-soft: #fae7fd;

  /* Categorical chart series. Ordered for maximum adjacent contrast, and
     chosen to stay distinguishable in both themes and for common colour
     vision deficiencies (no red/green adjacency, including the wrap from the
     last series back to the first in a pie).

     Series 1 is the brand cyan: the first series is the one the reader is
     told to care about, so it should be the colour the product is. Series 3
     is the brand's own second colour, which means a two-series chart is
     drawn entirely in Digisol's identity before it reaches for a third hue. */
  --ec-chart-1: #12719f;  /* digisol blue   */
  --ec-chart-2: #f4a71d;  /* amber          */
  --ec-chart-3: #3d3c7a;  /* digisol indigo */
  --ec-chart-4: #e11d48;  /* rose           */
  --ec-chart-5: #0c8579;  /* teal           */
  --ec-chart-6: #a21caf;  /* magenta        */

  /* The ink that goes ON each series colour, for the one place a series is
     used as a FILL with something drawn inside it: the module launcher's
     tiles. Same reasoning as --ec-accent-on, and the same trap - a glyph
     painted white on all six is fine on the blue and wrong on the amber,
     where white measures 2.0:1. Amber takes the dark ink instead, at 8.5:1.
     The other five clear 4.5 on white (blue 5.4, indigo 9.9, rose 4.7, teal
     4.5, magenta 6.3). */
  --ec-chart-1-on: var(--ec-paper-0);
  --ec-chart-2-on: var(--ec-primary-ink);
  --ec-chart-3-on: var(--ec-paper-0);
  --ec-chart-4-on: var(--ec-paper-0);
  --ec-chart-5-on: var(--ec-paper-0);
  --ec-chart-6-on: var(--ec-paper-0);

  /* Pastel tiles: the icon squares on the sign-in panel, on the feature grid
     and inside the drawn dashboard.

     The categorical shape of --ec-chart-*, inverted. There the token is a
     saturated FILL and -on is the ink laid over it; here the token is a pale
     fill and -on is a saturated stroke, because these draw a coloured line
     icon on a tinted square rather than a white glyph on a solid one.

     Four, not six: the sign-in panel has four slots and the feature grid
     repeats those four rather than inventing two more hues nobody can name.

     Measured, ink on tile:
       1 sky     #0e5b80 on #dcf0fb   6.3:1
       2 indigo  #3d3c7a on #e7e6f4   8.0:1
       3 teal    #0a6d63 on #d6f1ed   5.2:1
       4 sand    #92400e on #fdecd2   6.1:1

     The names are kept from the scheme this replaces so nothing downstream
     re-keys, and every hue under them moved. They are declared on the ramp
     rather than written straight into --ec-tint-* for a reason that is not
     tidiness: the sign-in panel is painted a fixed colour in BOTH themes, so
     it re-keys the tints back to their light values locally - and it cannot
     do that if the only name for a light value is a token the dark block
     overrides. Layer 1 never moves; that is what makes it referenceable from
     a pinned surface. */
  --ec-lilac-100: #e7e6f4;
  --ec-lilac-700: #3d3c7a;
  --ec-sky-100:   #dcf0fb;
  --ec-sky-700:   #0e5b80;
  --ec-sand-100:  #fdecd2;
  --ec-sand-700:  #92400e;
  --ec-teal-100:  #d6f1ed;
  --ec-teal-700:  #0a6d63;

  --ec-tint-1: var(--ec-sky-100);       /* sky    */
  --ec-tint-2: var(--ec-lilac-100);     /* indigo */
  --ec-tint-3: var(--ec-teal-100);      /* teal   */
  --ec-tint-4: var(--ec-sand-100);      /* sand   */

  --ec-tint-1-on: var(--ec-sky-700);
  --ec-tint-2-on: var(--ec-lilac-700);
  --ec-tint-3-on: var(--ec-teal-700);
  --ec-tint-4-on: var(--ec-sand-700);

  /* ======================================================================
     LAYER 2: ROLE TOKENS (light theme)
     ====================================================================== */

  --ec-bg:             var(--ec-paper-50);
  --ec-surface:        var(--ec-paper-0);
  --ec-surface-2:      var(--ec-paper-200);
  --ec-surface-hover:  var(--ec-paper-100);

  --ec-border:         var(--ec-paper-300);
  --ec-border-strong:  var(--ec-paper-500);

  /* The tray: a block set INTO a card to say "this is a summary of what
     follows" - the KPI card's footer split, a meter's surround, a comparison
     panel, a row in the supervision list.

     It is the page colour here: a strip of the canvas showing through the
     card. Measured against the white card that is 1.07:1, so the fill is not
     what makes it a block - the hairline is. Both together read; neither
     alone does, which is why every tray in components.css carries both.

     The dark theme cannot use its page colour for this: --ec-bg is DARKER
     than --ec-surface there, so a tray would recede into a hole in the card
     rather than sit on it. It lifts instead, which is the direction a nested
     block goes on a dark ground. */
  --ec-tray-bg:        var(--ec-bg);

  --ec-text:           var(--ec-ink-850);
  --ec-text-muted:     var(--ec-ink-500);
  /* Not decoration: --ec-text-subtle carries normal-weight body copy, so it
     has to clear 4.5:1 rather than the 3:1 a hairline gets. It measures
     5.45:1 on white and 5.07:1 on the canvas, and still reads a step lighter
     than --ec-text-muted at 7.05:1. */
  --ec-text-subtle:    var(--ec-ink-400);
  --ec-text-inverse:   var(--ec-paper-0);

  --ec-accent:         var(--ec-primary-600);
  --ec-accent-hover:   var(--ec-primary-700);
  --ec-accent-soft:    var(--ec-primary-100);
  --ec-accent-on:      var(--ec-paper-0);

  /* The focus ring's colour, as a variable rather than --ec-accent directly.
     The ring is drawn in the accent, which contrasts with every surface in
     the scheme except the accent itself - so anything painted --ec-accent
     re-keys this to --ec-accent-on and draws the ring inside its own fill.
     See the block at the end of layout.css. */
  --ec-focus:          var(--ec-accent);

  /* --- Chrome -----------------------------------------------------------
     The 2026 redesign moved the rail and the header off the navy band. The
     rail is a white surface beside a soft canvas and the header sits on the
     canvas itself, so the work, not the chrome, is the darkest thing on the
     page. Both follow the theme: every value here is a role token, so dark
     mode needs no second copy of this block.

     The consoles keep their identity as a faint wash of their own colour
     over the surface: amber says "can suspend a school", the indigo says
     "reads and changes nothing", violet is the marketer's alone.
     --------------------------------------------------------------------- */
  --ec-sidebar-bg:          var(--ec-surface);
  --ec-sidebar-owner-bg:    color-mix(in srgb, var(--ec-warning) 7%, var(--ec-surface));
  --ec-sidebar-secretariat-bg: color-mix(in srgb, var(--ec-info) 7%, var(--ec-surface));
  --ec-sidebar-marketer-bg: color-mix(in srgb, var(--ec-marketer) 6%, var(--ec-surface));
  --ec-sidebar-border:      var(--ec-border);
  --ec-sidebar-text:        var(--ec-text);
  --ec-sidebar-text-muted:  var(--ec-text-muted);
  --ec-sidebar-hover-bg:    var(--ec-surface-2);
  /* The accent's own soft tint with the deep cyan ink, plus the cyan bar
     drawn by layout.css: found by colour and by shape. */
  --ec-sidebar-active-bg:   var(--ec-accent-soft);
  --ec-sidebar-active-text: var(--ec-primary-700);
  --ec-sidebar-section:     var(--ec-text-subtle);

  /* The header is the canvas, continued: no band, no rule. Opaque, because
     it is sticky and a translucent bar turns grey over a scrolling table. */
  --ec-header-bg:      var(--ec-bg);
  --ec-header-border:  transparent;

  --ec-overlay:        rgb(7 14 24 / 0.55);

  /* ======================================================================
     TYPE
     ====================================================================== */

  --ec-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    'Helvetica Neue', Arial, sans-serif;
  /* Headings. Falls through to the body stack rather than to a generic, so a
     heading on a machine that has not finished the download is still set in
     Inter and not in Times. */
  --ec-font-display: 'Space Grotesk', 'Inter', -apple-system,
    BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --ec-font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

  --ec-text-2xs: 0.6875rem; /* 11 */
  --ec-text-xs:  0.75rem;   /* 12 */
  --ec-text-sm:  0.8125rem; /* 13 */
  --ec-text-md:  0.875rem;  /* 14: body default */
  --ec-text-base:1rem;      /* 16 */
  --ec-text-lg:  1.25rem;   /* 20 */
  --ec-text-xl:  1.5rem;    /* 24 */
  --ec-text-2xl: 2rem;      /* 32 */

  /* ----------------------------------------------------------------------
     The named steps of the type system: the Stitch sheet's roles, set in
     Digisol's two faces, plus one role Digisol brings of its own.

     --ec-text-* above is a size and nothing else, which is why every
     component had to restate a weight, a line height and a tracking beside
     it and no two quite agreed. These are the roles, each one a size AND the
     line height and tracking that go with it, so a rule says what a thing IS
     rather than how big it is.

     Headline steps shrink on phones - the mobile values are re-declared in
     the narrow block at the foot of layout.css, so a component never writes
     a media query to set its own heading size.
     ---------------------------------------------------------------------- */

  /* Page titles: 32/40, -0.02em, 700, display face. */
  --ec-headline-xl:        2rem;
  --ec-headline-xl-lh:     2.5rem;
  --ec-headline-xl-track:  -0.02em;
  /* Section headings and big figures: 24/32, -0.015em, 600. */
  --ec-headline-lg:        1.5rem;
  --ec-headline-lg-lh:     2rem;
  --ec-headline-lg-track:  -0.015em;
  /* Card titles: 18/26, -0.01em, 600. */
  --ec-headline-md:        1.125rem;
  --ec-headline-md-lh:     1.625rem;
  --ec-headline-md-track:  -0.01em;

  /* Body: 16/24, 14/20, 12/16. */
  --ec-body-lg:    1rem;      --ec-body-lg-lh: 1.5rem;
  --ec-body-md:    0.875rem;  --ec-body-md-lh: 1.25rem;
  --ec-body-sm:    0.75rem;   --ec-body-sm-lh: 1rem;

  /* Labels: semibold and tracked out, because a column header or a status
     tag is scanned rather than read. 13/18 and 11/14. */
  --ec-label-md:       0.8125rem;
  --ec-label-md-lh:    1.125rem;
  --ec-label-md-track: 0.01em;
  --ec-label-sm:       0.6875rem;
  --ec-label-sm-lh:    0.875rem;
  --ec-label-sm-track: 0.03em;

  /* The eyebrow: 10px, 600, uppercase, tracked far out, display face. The
     one type role that is Digisol's rather than the Stitch sheet's -
     digisolgroup.net labels every section with one ("OUR SERVICES", "BUILT
     FOR") at 0.18em to 0.35em, and it is the single cheapest thing that makes
     a screen from this product recognisable across a room. Section
     headings, card eyebrows, table column heads and the rail's group labels
     take it. */
  --ec-eyebrow:        0.625rem;
  --ec-eyebrow-lh:     0.875rem;
  --ec-eyebrow-track:  0.22em;

  /* Figures: 14/20, 500, tracked 0.02em, lining tabular numerals. Marks,
     money, dates, clock times and matriculation numbers all take it so a
     column of them lines up digit under digit. */
  --ec-tabular:        0.875rem;
  --ec-tabular-lh:     1.25rem;
  --ec-tabular-track:  0.02em;

  --ec-weight-normal:   400;
  --ec-weight-medium:   500;
  --ec-weight-semibold: 600;
  --ec-weight-bold:     700;

  --ec-leading-tight:  1.25;
  --ec-leading-normal: 1.5;
  --ec-leading-loose:  1.7;

  /* ======================================================================
     SPACE: 4px base
     ====================================================================== */

  --ec-space-1:  0.25rem;  /*  4 */
  --ec-space-2:  0.5rem;   /*  8 */
  --ec-space-3:  0.75rem;  /* 12 */
  --ec-space-4:  1rem;     /* 16 */
  --ec-space-5:  1.25rem;  /* 20 */
  --ec-space-6:  1.5rem;   /* 24 */
  --ec-space-8:  2rem;     /* 32 */
  --ec-space-12: 3rem;     /* 48 */

  /* ======================================================================
     RADIUS / SHADOW / MOTION
     ====================================================================== */

  /* Softened in the 2026 redesign (docs/REDESIGN_PLAN.md). The 2/6/10 scale
     made a dense product feel denser: forty square cards on a dashboard read
     as a spreadsheet. The redesign keeps the scale's order and moves every
     step up, so nothing changes rank, only edge:

       sm   4px  inputs, buttons, table headers, chips, badges
       md  10px  filter bars, trays, small containers
       lg  16px  cards, modals, drawers, quick-action tiles
       xl  22px  the page hero, the softest surface on the page
       full      avatars and the progress track

     Badges still take sm, not full: a pill tag reads as a button, and a
     state is not something you press. */
  --ec-radius-sm:   4px;
  --ec-radius-md:   10px;
  --ec-radius-lg:   16px;
  --ec-radius-xl:   22px;
  --ec-radius-full: 999px;

  /* The rule: a 2px cyan bar, Digisol's one piece of ornament. It sits above
     a section heading, down the leading edge of an active row and under an
     eyebrow. Declared here so the width and the colour agree everywhere they
     appear. */
  --ec-rule-w:      2px;
  --ec-rule:        var(--ec-primary-500);

  /* Elevation is a 1px outline plus a shallow ambient layer, not a deep drop.
     Three levels:
       sm  resting card / module shell
       md  flyout: switcher menus, calendar popovers, filter menus
       lg  modal and overlay
     Each is stated in the ink navy rather than black so it tints rather than
     greys the cool field it falls on. */
  --ec-shadow-sm: 0 1px 3px 0 rgb(20 32 48 / 0.06);
  --ec-shadow-md: 0 4px 12px -2px rgb(20 32 48 / 0.10);
  --ec-shadow-lg: 0 20px 25px -5px rgb(20 32 48 / 0.12),
                  0 8px 10px -6px rgb(20 32 48 / 0.06);

  --ec-ring: 0 0 0 3px color-mix(in srgb, var(--ec-focus) 32%, transparent);

  --ec-transition-fast: 120ms cubic-bezier(0.4, 0, 0.2, 1);
  --ec-transition:      200ms cubic-bezier(0.4, 0, 0.2, 1);
  /* Lifts and entrances: decelerates into place, which reads as settling
     rather than sliding. Transform and opacity only. */
  --ec-transition-lift: 220ms cubic-bezier(0.2, 0.8, 0.2, 1);

  /* ======================================================================
     LAYOUT DIMENSIONS
     ====================================================================== */

  /* 260px is the Stitch sheet's figure for the persistent rail, and the
     drawer is the same object at a narrow width, so there is one width and
     not two. */
  --ec-sidebar-w:        260px;
  --ec-header-h:         64px;
  /* The sheet pins the grid inside 1440px. */
  --ec-content-max:      1440px;

  /* The page gutter, by breakpoint: 32px desktop margin / 24px gutter, 24/16
     tablet, 16/12 phone. Declared once here and stepped down in the narrow
     blocks at the foot of layout.css, so a page never sets its own padding. */
  --ec-page-margin:      2rem;
  --ec-page-gutter:      1.5rem;

  /* The family portal's bottom tab bar, which exists below 768px only. The
     content column reserves this much room at its foot so the last card is
     not sitting under the bar. */
  --ec-tabbar-h:         64px;

  /* ======================================================================
     Z-INDEX: declared together so stacking bugs are a lookup, not a hunt
     ====================================================================== */

  --ec-z-sidebar: 1040;
  --ec-z-overlay: 1035;
  --ec-z-header:  1030;
  /* Under the scrim on purpose: opening the drawer over a phone dims the tab
     bar with everything else, rather than leaving one lit strip at the foot
     of a darkened page. */
  --ec-z-tabbar:  1020;
  --ec-z-dropdown:1050;
}

/* ==========================================================================
   DARK THEME
   Only role tokens are overridden. Palette and every component rule stay
   untouched. That is the entire point of the two-layer split.

   The chrome block is deliberately short here: the rail, the header and the
   tab bar are already dark in the light theme, so dark mode has almost
   nothing to say about them. It moves the canvas, the card and the ink.
   ========================================================================== */

[data-theme='dark'] {
  --ec-bg:             var(--ec-ink-950);
  --ec-surface:        var(--ec-ink-900);
  --ec-surface-2:      var(--ec-ink-800);
  --ec-surface-hover:  var(--ec-ink-750);

  --ec-border:         var(--ec-ink-700);
  --ec-border-strong:  var(--ec-ink-350);

  /* Up, not down. See the note beside the light value. */
  --ec-tray-bg:        var(--ec-surface-hover);

  --ec-text:           var(--ec-ink-50);
  --ec-text-muted:     var(--ec-ink-200);
  --ec-text-subtle:    var(--ec-ink-300);
  --ec-text-inverse:   var(--ec-ink-950);

  --ec-accent:         var(--ec-primary-500);
  --ec-accent-hover:   var(--ec-primary-400);
  --ec-accent-soft:    var(--ec-primary-wash);
  /* The dark theme lifts --ec-accent to the brand cyan, which leaves white
     on it at 2.9:1 everywhere components.css pairs the two - the primary
     button, the skip link, the active rail row. The ink goes dark to match
     instead, which measures 5.84:1. */
  --ec-accent-on:      var(--ec-primary-ink);

  /* The rail and header follow the role tokens above, so only the active
     row's ink needs lifting: the deep cyan is too dark on a near-black wash. */
  --ec-sidebar-active-text: var(--ec-primary-300);

  --ec-overlay:        rgb(3 7 14 / 0.72);

  /* The light theme's semantic inks are chosen to sit on a pale tint and are
     far too dark for a near-black surface - #b91c1c measures 3.3:1 there. So
     the dark theme lifts each ink and drops the wash behind it to 10%, which
     is what puts the badge pairing back over 4.5:1. */
  --ec-success:      #4ade80;
  --ec-warning:      #fbbf24;
  --ec-danger:       #ff9b9b;
  --ec-info:         #a5a3e8;
  --ec-marketer:     #e879f9;

  /* #06202e on #ff9b9b is 9.33:1; --ec-paper-0 on it would be 2.03:1. */
  --ec-danger-on:    var(--ec-primary-ink);

  --ec-success-soft: color-mix(in srgb, var(--ec-success) 10%, transparent);
  --ec-warning-soft: color-mix(in srgb, var(--ec-warning) 10%, transparent);
  --ec-danger-soft:  color-mix(in srgb, var(--ec-danger)  10%, transparent);
  --ec-info-soft:    color-mix(in srgb, var(--ec-info)    10%, transparent);
  --ec-marketer-soft: color-mix(in srgb, var(--ec-marketer) 10%, transparent);

  /* Nudge chart hues brighter so they hold up against a near-black plot area */
  --ec-chart-1: #4fb0e2;
  --ec-chart-2: #fbbf24;
  --ec-chart-3: #a5a3e8;
  --ec-chart-4: #fb7185;
  --ec-chart-5: #2dd4bf;
  --ec-chart-6: #e879f9;

  /* Every one of those six is a light colour, so every one of them takes the
     dark ink - the same flip --ec-accent-on makes, for the same reason. */
  --ec-chart-1-on: var(--ec-primary-ink);
  --ec-chart-2-on: var(--ec-primary-ink);
  --ec-chart-3-on: var(--ec-primary-ink);
  --ec-chart-4-on: var(--ec-primary-ink);
  --ec-chart-5-on: var(--ec-primary-ink);
  --ec-chart-6-on: var(--ec-primary-ink);

  /* The tints take exactly the treatment the semantic block above takes: the
     ink lifts to a colour that survives a near-black field, and the tile
     becomes a thin wash of that same ink instead of a pale pastel. A pale
     blue square on #070e18 is the light theme leaking through.

     Every lifted ink here is one the dark theme already declares, so a fifth
     hue is not introduced: sky borrows --ec-primary-400, indigo and teal
     borrow the lifted chart hues, sand borrows the lifted warning. 12%, one
     step above the semantic block's 10%, because a tile is a shape the eye
     has to find and a badge is text the eye is already on. */
  --ec-tint-1-on: var(--ec-primary-400);
  --ec-tint-2-on: var(--ec-chart-3);
  --ec-tint-3-on: var(--ec-chart-5);
  --ec-tint-4-on: var(--ec-warning);

  --ec-tint-1: color-mix(in srgb, var(--ec-tint-1-on) 12%, transparent);
  --ec-tint-2: color-mix(in srgb, var(--ec-tint-2-on) 12%, transparent);
  --ec-tint-3: color-mix(in srgb, var(--ec-tint-3-on) 12%, transparent);
  --ec-tint-4: color-mix(in srgb, var(--ec-tint-4-on) 12%, transparent);

  --ec-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.35);
  --ec-shadow-md: 0 2px 8px rgb(0 0 0 / 0.4);
  --ec-shadow-lg: 0 8px 28px rgb(0 0 0 / 0.5);
}
