/* ============================================================================
   Portal appearance: density, text size, theme, and the per-page wash.

   Three axes the user controls, one scale everything derives from:

     --zen-density    multiplies every spacing step
     --zen-root-size  sets the rem, so type and spacing scale together
     [data-appearance] redefines colour tokens only

   Loaded after app.css so these overrides win. app.css still carries ~600
   hardcoded spacing values; the rules converted here are the ones that dominate
   what a page feels like (view stack, panels, tables, stat cards). The long tail
   is a follow-up, and until it is converted those places simply will not respond
   to density — which is visible, not silent.
   ============================================================================ */

:root {
    --zen-density: 0.85;
    --zen-root-size: 16px;

    /* Seven steps replacing the 59 ad-hoc values in app.css. */
    --zen-s1: calc(0.25rem * var(--zen-density));
    --zen-s2: calc(0.5rem * var(--zen-density));
    --zen-s3: calc(0.75rem * var(--zen-density));
    --zen-s4: calc(1rem * var(--zen-density));
    --zen-s5: calc(1.5rem * var(--zen-density));
    --zen-s6: calc(2rem * var(--zen-density));
    --zen-s7: calc(3rem * var(--zen-density));

    /* ────────────────────────────────────────────────────────────────────
       One colour, three accents, resolved once.

         --zen-theme-h  the hue the user picked
         --zen-h        what every colour below reads
         --zen-h2       its warm partner
         --zen-h3       its cool counterpoint

       The two intervals are Zenvee's own. The brand is rose #ff4f87 at hue 341,
       and +34 lands exactly on the coral #ff8f70 it already pairs with, so the
       default theme reproduces the sign-in palette from the hue alone rather
       than from a second hand-written list that can drift from it.

       +195 replaces the old violet #5c44ff. A rose/coral pair needs a cool third
       to stay legible — chips and charts need a colour that is not another warm
       neighbour — and at the default that interval gives a teal rather than the
       violet, which now reads as the older brand.

       Pages used to shift this hue and could override it individually. Both are
       gone: one shop had a different colour on every screen, which made the hue
       decorative rather than something you could recognise the product by.
       ──────────────────────────────────────────────────────────────────── */
    --zen-theme-h: 341;
    --zen-h: var(--zen-theme-h);
    --zen-h2: calc(var(--zen-h) + 34);
    --zen-h3: calc(var(--zen-h) + 195);

    /* The wash travels the same three hues the accents do, so the ground drifts from the primary
       through its warm partner to the cool counterpoint instead of being one flat tint. Kept
       within a point and a half of lightness — a page ground that reads as a gradient competes
       with the content sitting on it. */
    --zen-wash: linear-gradient(118deg,
        hsl(var(--zen-h) 60% 97.6%) 0%,
        hsl(var(--zen-h2) 52% 96.6%) 54%,
        hsl(var(--zen-h3) 38% 96.4%) 100%);
    --zen-rail-veil: hsl(var(--zen-h) 52% 98.6% / 0.68);
    --zen-rail-solid: hsl(var(--zen-h) 52% 98.6%);
    --zen-hero-glow: rgba(255, 255, 255, 0.6);

    /* Header ribbons, the login shell's silhouette applied inside the hero. */
    --zen-rib-1: hsl(var(--zen-h) 76% 62%);
    --zen-rib-2: hsl(calc(var(--zen-h) + 28) 70% 66%);
    --zen-rib-3: hsl(calc(var(--zen-h) + 55) 74% 62%);
    --zen-rib-glow: hsl(var(--zen-h) 86% 92%);
    --zen-rib-opacity: 0.5;
    --zen-rib-highlight: #ffffff;
    --zen-rib-highlight-opacity: 0.3;
}

html {
    font-size: var(--zen-root-size);
}

/* ── colour themes ───────────────────────────────────────────────────────
   A theme is now a hue, not a hand-written palette. Everything downstream —
   wash, accents, chips, borders, focus rings, ribbons — is derived, so adding a
   colour is one line rather than a block that has to be kept consistent.

   Deliberately not spacing: a theme can never quietly alter density. */

/* Values must match AppearanceColours in MainLayout.razor, which draws the swatches.
   Zenvee's own rose is the default on :root and carries no attribute of its own — "rose" here is
   the swatch the UI labels Red, a true red rather than the brand colour. */
[data-appearance="rose"]   { --zen-theme-h: 358; }
[data-appearance="warm"]   { --zen-theme-h: 26; }
[data-appearance="amber"]  { --zen-theme-h: 45; }
[data-appearance="lime"]   { --zen-theme-h: 96; }
[data-appearance="green"]  { --zen-theme-h: 142; }
[data-appearance="teal"]   { --zen-theme-h: 176; }
[data-appearance="sky"]    { --zen-theme-h: 200; }
[data-appearance="blue"]   { --zen-theme-h: 222; }
[data-appearance="indigo"] { --zen-theme-h: 250; }
[data-appearance="violet"] { --zen-theme-h: 278; }

/* ── mode ────────────────────────────────────────────────────────────────
   Separate axis from colour, so every hue has a night version rather than dark
   being two fixed violet palettes. Same hue, inverted lightness relationships:
   a page someone coloured teal is still recognisably theirs at night. */

[data-mode="dim"], [data-mode="midnight"] {
    --zen-wash: linear-gradient(118deg,
        hsl(var(--zen-h) 17% 7.5%) 0%,
        hsl(var(--zen-h2) 15% 7%) 54%,
        hsl(var(--zen-h3) 14% 6.5%) 100%);
    --zen-rail-veil: hsl(var(--zen-h) 16% 8.5% / 0.82);
    --zen-rail-solid: hsl(var(--zen-h) 16% 8.5%);
    --zen-hero-glow: rgba(255, 255, 255, 0.06);
    --zen-rib-1: hsl(var(--zen-h) 52% 52%);
    --zen-rib-2: hsl(calc(var(--zen-h) + 28) 48% 54%);
    --zen-rib-3: hsl(calc(var(--zen-h) + 55) 50% 50%);
    --zen-rib-glow: hsl(var(--zen-h) 40% 32%);
    --zen-rib-opacity: 0.62;
    --zen-rib-highlight-opacity: 0.12;
}

[data-mode="midnight"] {
    --zen-wash: linear-gradient(118deg,
        hsl(var(--zen-h) 21% 3.4%) 0%,
        hsl(var(--zen-h2) 19% 3%) 54%,
        hsl(var(--zen-h3) 18% 2.8%) 100%);
    --zen-rail-veil: hsl(var(--zen-h) 20% 4% / 0.84);
    --zen-rail-solid: hsl(var(--zen-h) 20% 4%);
    --zen-hero-glow: rgba(255, 255, 255, 0.05);
}

/* Not another colour. Flat, hairlined, and opted out of the wash, the ribbons and
   the blur entirely — a diluted version of an effect is what a contrast mode must
   not have, and a user-chosen hue must never be able to defeat it. Last in the
   file's mode group so it wins over the hue rules above. */
[data-mode="contrast"] {
    --zen-wash: #ffffff;
    --zen-rail-veil: #ffffff;
    --zen-rail-solid: #ffffff;
    --zen-hero-glow: transparent;
    --zen-rib-opacity: 0;
    --zen-rib-highlight-opacity: 0;
    --zen-border-subtle: #000000;
    --zen-border-faint: #000000;
    /* The surfaces and text this used to restate live with the other grounds further down, in the
       one contrast block that covers all of them. Declaring them in two places meant the later
       block silently decided the outcome and this one only looked like it did. */
}

[data-mode="contrast"] .zs-shell,
[data-mode="contrast"] .zs-sidebar,
[data-mode="contrast"] .admin-panel,
[data-mode="contrast"] .admin-stat-card {
    backdrop-filter: none !important;
    box-shadow: none !important;
    border: 2px solid #000000 !important;
}


/* ── the wash ────────────────────────────────────────────────────────────
   Fixed attachment: a gradient that scrolls with a long queue page bands
   visibly where sections meet. */

body {
    background: var(--zen-page-wash, var(--zen-wash)) !important;
    background-attachment: fixed !important;
}






/* ── theme flyout ────────────────────────────────────────────────────────── */

.zs-appearance-scrim {
    position: fixed;
    inset: 0;
    z-index: 1200;
    background: rgba(20, 16, 38, 0.28);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s;
}

.zs-appearance-scrim.is-open { opacity: 1; visibility: visible; }

.zs-appearance-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 1201;
    width: min(23rem, 100%);
    padding: var(--zen-s5);
    overflow-y: auto;
    background: var(--zen-surface-panel, #fff);
    border-left: 1px solid var(--zen-border-subtle);
    box-shadow: -1.4rem 0 3rem rgba(20, 16, 38, 0.18);
    transform: translateX(100%);
    transition: transform 0.24s cubic-bezier(0.2, 0.75, 0.25, 1);
}

.zs-appearance-panel.is-open { transform: none; }

.zs-appearance-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--zen-s3);
    margin-bottom: var(--zen-s4);
}

.zs-appearance-close {
    border: 0;
    background: transparent;
    color: var(--zen-text-muted);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    padding: var(--zen-s1) var(--zen-s2);
    border-radius: calc(0.5rem * var(--zen-radius-scale, 1));
}

.zs-appearance-close:hover { background: var(--zen-surface-accent-soft); color: var(--zen-text); }

.zs-appearance-group { margin-bottom: var(--zen-s5); }

.zs-appearance-label {
    display: block;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--zen-text-soft);
    margin-bottom: var(--zen-s2);
}

.zs-appearance-options { display: flex; flex-wrap: wrap; gap: var(--zen-s2); }

.zs-appearance-option {
    flex: 1 1 auto;
    min-width: 4.5rem;
    padding: var(--zen-s2) var(--zen-s3);
    border: 1px solid var(--zen-border-subtle);
    border-radius: calc(999px * var(--zen-radius-scale, 1));
    background: transparent;
    color: var(--zen-text);
    font: inherit;
    font-size: 0.85rem;
    font-weight: 650;
    cursor: pointer;
}

.zs-appearance-option[aria-pressed="true"] {
    background: var(--zen-text);
    color: var(--zen-surface-panel, #fff);
    border-color: var(--zen-text);
}

.zs-appearance-option:focus-visible { outline: 2px solid var(--zen-text); outline-offset: 2px; }

/* Swatches show the actual wash, so a theme is chosen by looking at it. */
.zs-appearance-swatches { display: grid; grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr)); gap: var(--zen-s2); }

.zs-appearance-swatch {
    padding: 0;
    border: 1px solid var(--zen-border-subtle);
    border-radius: calc(0.7rem * var(--zen-radius-scale, 1));
    overflow: hidden;
    cursor: pointer;
    background: transparent;
    text-align: left;
}

.zs-appearance-swatch span { display: block; height: 2.6rem; }
.zs-appearance-swatch small { display: block; padding: var(--zen-s2); font-size: 0.76rem; font-weight: 650; color: var(--zen-text); }
.zs-appearance-swatch[aria-pressed="true"] { outline: 2px solid var(--zen-text); outline-offset: 1px; }

.zs-appearance-slider {
    display: flex;
    align-items: center;
    gap: var(--zen-s3);
}

.zs-appearance-slider input[type="range"] {
    flex: 1;
    min-width: 0;
    /* The slider is the control here, so it takes the tap floor like every other one. */
    min-height: var(--zen-tap);
    accent-color: var(--zen-accent);
    cursor: pointer;
}

.zs-appearance-slider output {
    flex: none;
    min-width: 3.5rem;
    font-size: 0.82rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--zen-text-strong);
    text-align: right;
}

/* A slider is easy to move and hard to move back to exactly where it was. */
.zs-appearance-reset {
    flex: none;
    min-height: var(--zen-tap);
    padding-inline: 0.7rem;
    border: 1px solid var(--zen-border-subtle);
    border-radius: var(--zen-radius-sm, 0.3rem);
    background: var(--zen-surface-input);
    color: var(--zen-text-muted);
    font-size: 0.76rem;
    font-weight: 700;
    cursor: pointer;
}

.zs-appearance-reset:hover { border-color: var(--zen-accent); color: var(--zen-accent-ink-strong); }

.zs-appearance-note { font-size: 0.78rem; color: var(--zen-text-muted); margin-top: var(--zen-s2); }

/* Round chips of the hue itself, because a colour is chosen by looking at it
   rather than by reading its name. */
.zs-appearance-hues { display: flex; flex-wrap: wrap; gap: var(--zen-s2); align-items: center; }

.zs-appearance-hue {
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 2px solid transparent;
    border-radius: calc(999px * var(--zen-radius-scale, 1));
    cursor: pointer;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
}

.zs-appearance-hue[aria-pressed="true"] {
    border-color: var(--zen-text);
}

.zs-appearance-hue:focus-visible { outline: 2px solid var(--zen-text); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
    .zs-appearance-panel, .zs-appearance-scrim { transition: none; }
}

/* ============================================================================
   Corner radius and accent colour.

   app.css carries 237 border-radius declarations across 41 distinct values, and
   pins .btn-primary to a berry/violet gradient regardless of theme. Both are
   collapsed here: three radius steps, and an accent pair derived from --zen-h.
   ============================================================================ */

:root {
    /* Tighter than the 1rem–1.8rem app.css uses today, and consistent. */
    /* The floor under every control. The Portal is worked standing up, on a tablet at the front
       desk, by people mid-service with one hand free — 48px is the smallest thing a thumb hits
       reliably, and the board, the turns list and the reception shell had each arrived at exactly
       this number under three different names. */
    --zen-tap: max(48px, 3.2rem);
    --zen-tap-lg: max(56px, 3.75rem);  /* close, confirm, check out: the ones hit mid-rush */
    /* The older name for the same floor. Seventeen rules size a control as max(<its own height>,
       var(--zen-touch, 0px)) and nothing ever declared it, so every one of them fell to its own
       height — 2.2rem on a chip, 2.9rem on an input — below the floor the rule was written to
       respect. Aliased rather than renamed, so the rules keep their intent and reach it. */
    --zen-touch: var(--zen-tap);

    /* The height a button takes inside a segmented group. A group is a bordered, padded box around
       its buttons, so sizing the button itself to the tap floor makes the group 2px of border and
       0.4rem of padding taller than every plain control beside it — 53.2px against 51.2px, which
       reads as one control in the row sitting slightly proud of the others. The button takes the
       floor minus the wrapper it lives in, and the group lands on the floor. */
    --zen-tap-inset: calc(var(--zen-tap) - 0.4rem - 2px);

    /* One dial, three corners, and they stay in that order: a control's corner is smaller than the
       panel it sits in, which is smaller than the sheet that holds it. A person choosing them
       separately could take that apart without meaning to. The scale runs 0 (square) to 2.5, and
       1 is what ships.

       Inputs used to carry their own 0.85rem base while buttons took the small token at 0.3rem, so
       the dial pulled them apart — at the far end the inputs were pills beside barely-rounded
       buttons. They share the base now, and the panels moved up to stay above it. */
    --zen-radius-sm: calc(0.85rem * var(--zen-radius-scale, 1));  /* inputs, buttons, chips */
    --zen-radius-md: calc(1.05rem * var(--zen-radius-scale, 1));  /* cards, panels, tables */
    --zen-radius-lg: calc(1.35rem * var(--zen-radius-scale, 1));  /* modals, drawers, page heroes */

    --zen-accent: hsl(var(--zen-h) 68% 54%);
    --zen-accent-2: hsl(calc(var(--zen-h) + 34) 62% 46%);
    --zen-accent-ring: hsl(var(--zen-h) 68% 54% / 0.22);
    --zen-accent-edge: hsl(var(--zen-h) 50% 46% / 0.2);

    /* Buttons are outlined rather than filled, so the accent has to carry text on the page
       background instead of white on itself. --zen-accent at 54% lightness is a fill colour and
       reaches only ~2:1 as text.

       30% is not a taste call: button labels are ~13px, so they need 4.5:1, and lightness buys a
       different amount of contrast per hue. Measured across all eleven, teal is the worst case —
       36% gives it 3.57:1 and fails, 30% gives 4.94:1. Lower this and teal goes under. */
    --zen-accent-ink: hsl(var(--zen-h) 64% 30%);
    --zen-accent-ink-strong: hsl(var(--zen-h) 68% 24%);

    /* The mark drawn *on* the accent, which is not the same thing as ink that reads on the page.
       In the dark theme the accent is the light colour and accent-ink-strong is lighter still, so
       a tick painted with it vanished. One dark, hue-tinted mark clears 3:1 against the accent at
       every hue in both themes; white only manages it for the pinks and violets. */
    --zen-on-accent-mark: hsl(var(--zen-h) 75% 14%);
}

/* html.dark as well as the two modes that set it: the boot script in App.razor adds the class
   from the stored preference before this mode attribute exists, and a dark ground with the light
   theme's 24–30% ink is unreadable for that frame — and for anything else that only sets the
   class. */
html.dark, [data-mode="dim"], [data-mode="midnight"] {
    --zen-accent: hsl(var(--zen-h) 62% 66%);
    --zen-accent-2: hsl(calc(var(--zen-h) + 34) 56% 58%);
    --zen-accent-ring: hsl(var(--zen-h) 62% 66% / 0.26);
    --zen-accent-edge: hsl(var(--zen-h) 50% 70% / 0.3);
    --zen-accent-ink: hsl(var(--zen-h) 70% 76%);
    --zen-accent-ink-strong: hsl(var(--zen-h) 78% 86%);
    --zen-on-accent-mark: hsl(var(--zen-h) 75% 14%);
}

/* Flat, hard-edged, no gradient — a high-contrast button must not be a wash. */
[data-mode="contrast"] {
    --zen-accent: #000000;
    --zen-accent-2: #000000;
    --zen-accent-ring: transparent;
    --zen-accent-edge: #000000;
    --zen-accent-ink: #000000;
    --zen-accent-ink-strong: #000000;
    /* Contrast mode's accent is black, so the mark on it is white. */
    --zen-on-accent-mark: #ffffff;
    /* Contrast mode squares things off on purpose, and still follows the dial: somebody who has
       chosen square corners gets square corners here too. */
    --zen-radius-sm: calc(0.2rem * var(--zen-radius-scale, 1));
    --zen-radius-md: calc(0.25rem * var(--zen-radius-scale, 1));
    --zen-radius-lg: calc(0.3rem * var(--zen-radius-scale, 1));
}

/* ============================================================================
   Grounds.

   The theme used to stop at accents: chips, links, borders and the header ribbon
   took the hue, but the page stayed #f1f4f8, panels stayed white, and the rail
   and top bar were pinned to #ffffff. Picking Teal therefore recoloured a border
   and left every surface a cold neutral grey — the hue read as trim on someone
   else's product.

   These are the surfaces themselves. Every value derives from --zen-h, so the
   colour reaches the page, the panels, the rail, the top bar, the inputs and the
   text, and a theme is the whole screen rather than its edges.

   The numbers are measured, not chosen by eye: lightness buys a different amount
   of contrast per hue, so each pairing below was checked across all eleven
   swatches and set by its worst case. Every text pairing clears 4.5:1 — the
   tightest is text-soft on the page ground at 5.02:1, on lime.

   Declared with the same weight as the block that follows and after the palette
   in components.css, which is what lets these win without !important.
   ============================================================================ */

:root {
    --zen-surface-page: hsl(var(--zen-h) 44% 96.8%);
    --zen-surface-panel: hsl(var(--zen-h) 60% 99.2%);
    --zen-surface-subtle: hsl(var(--zen-h) 38% 95.4%);
    --zen-surface-input: hsl(var(--zen-h) 50% 99%);
    --zen-surface-elevated: hsl(var(--zen-h) 66% 99.6%);

    /* A screen inside the page. The POS tip preview and the Zpoints preview both show what a
       customer reads off a device, so their ground inverts instead of following the panel. It
       carries the hue like every other surface, which is what it was not doing while it was a
       literal near-black with no token behind it. */
    --zen-surface-strong: hsl(var(--zen-h) 24% 12%);
    --zen-on-strong: hsl(var(--zen-h) 30% 97%);
    --zen-on-strong-muted: hsl(var(--zen-h) 18% 76%);
    --zen-on-strong-fill: hsl(var(--zen-h) 30% 100% / 12%);
    --zen-on-strong-edge: transparent;

    /* Near-black carrying the hue rather than a neutral grey: at the rose default this is the
       sign-in page's own ink, and on any other hue the text still belongs to the same family as
       the surface behind it. */
    --zen-text-strong: hsl(var(--zen-h) 30% 11%);
    --zen-text: hsl(var(--zen-h) 26% 16%);
    --zen-text-muted: hsl(var(--zen-h) 16% 37%);
    --zen-text-soft: hsl(var(--zen-h) 13% 39%);

    --zen-shadow-panel: 0 4px 16px hsl(var(--zen-h) 40% 20% / 0.07);

    /* The rail and the top bar are surfaces too. Pinned to #ffffff they were the two largest
       things on screen that ignored the theme entirely. */
    --zs-sidebar-bg: hsl(var(--zen-h) 52% 98.6%);
    --zs-sidebar-active: hsl(var(--zen-h) 62% 50% / 0.1);
    --zs-sidebar-hover: hsl(var(--zen-h) 50% 46% / 0.06);
    --zs-sidebar-text: hsl(var(--zen-h) 18% 30%);
    --zs-sidebar-label: hsl(var(--zen-h) 14% 46%);
    --zs-topbar-bg: hsl(var(--zen-h) 56% 99.2%);
    --zs-content-bg: var(--zen-surface-page);
    --zs-primary: hsl(var(--zen-h) 68% 54%);

    /* Three accents, one pick. --zen-accent-2 and -3 are what a chart, a chip row or a two-state
       control reach for instead of inventing a colour that belongs to no theme. Each has an ink
       variant because the fill colours themselves reach only ~2:1 as text. */
    --zen-accent-2: hsl(var(--zen-h2) 78% 62%);
    --zen-accent-2-ink: hsl(var(--zen-h2) 62% 30%);
    --zen-accent-3: hsl(var(--zen-h3) 72% 42%);
    --zen-accent-3-ink: hsl(var(--zen-h3) 66% 27%);
    --zen-surface-accent-2-soft: hsl(var(--zen-h2) 70% 52% / 0.1);
    --zen-surface-accent-3-soft: hsl(var(--zen-h3) 62% 44% / 0.1);
}

html.dark, [data-mode="dim"], [data-mode="midnight"] {
    /* A real dark, not a dimmed version of the light theme. The chrome — page and rail — is the
       darkest thing on screen and the panels sit above it, which is the arrangement Flowbite's
       dark dashboard uses (#111827 ground, #1f2937 cards) and the reason its cards read as cards.

       Saturation is deliberately far lower here than in the light theme. A dark ground shows hue
       much more strongly than a light one at the same saturation: the first version of this ran
       at 20–24% and the rose theme came out a muddy maroon rather than a dark. At 13–16% the hue
       is a lean on a near-black, which is what keeps teal and rose distinguishable without either
       looking tinted. */
    --zen-surface-page: hsl(var(--zen-h) 16% 7%);
    --zen-surface-panel: hsl(var(--zen-h) 13% 11.5%);
    --zen-surface-subtle: hsl(var(--zen-h) 11% 16%);
    --zen-surface-input: hsl(var(--zen-h) 12% 14%);
    --zen-surface-elevated: hsl(var(--zen-h) 13% 13.5%);

    /* Already dark, so the screen goes deeper than the page rather than lighter; its border is
       what separates it from the card it sits on. */
    --zen-surface-strong: hsl(var(--zen-h) 18% 4%);
    --zen-on-strong: hsl(var(--zen-h) 26% 95%);
    --zen-on-strong-muted: hsl(var(--zen-h) 14% 72%);
    --zen-on-strong-fill: hsl(var(--zen-h) 30% 100% / 14%);
    --zen-on-strong-edge: transparent;

    --zen-text-strong: hsl(var(--zen-h) 30% 97%);
    --zen-text: hsl(var(--zen-h) 22% 92%);
    --zen-text-muted: hsl(var(--zen-h) 14% 74%);
    --zen-text-soft: hsl(var(--zen-h) 12% 64%);

    --zen-shadow-panel: 0 4px 18px hsl(var(--zen-h) 60% 3% / 0.5);

    /* Rail and top bar belong to the ground, a hair above the page so the edge is visible
       without a border. */
    --zs-sidebar-bg: hsl(var(--zen-h) 16% 8.5%);
    --zs-sidebar-active: hsl(var(--zen-h) 60% 72% / 0.16);
    --zs-sidebar-hover: hsl(var(--zen-h) 50% 80% / 0.07);
    --zs-sidebar-text: hsl(var(--zen-h) 16% 82%);
    --zs-sidebar-label: hsl(var(--zen-h) 12% 60%);
    --zs-topbar-bg: hsl(var(--zen-h) 16% 8.5%);
    --zs-primary: hsl(var(--zen-h) 62% 66%);

    --zen-accent-2: hsl(var(--zen-h2) 70% 68%);
    --zen-accent-2-ink: hsl(var(--zen-h2) 66% 74%);
    --zen-accent-3: hsl(var(--zen-h3) 60% 58%);
    --zen-accent-3-ink: hsl(var(--zen-h3) 62% 72%);
    --zen-card-surface: hsl(var(--zen-h) 13% 13.5%);

    /* Panels and cards carry a faint corner light so a dark surface still reads as a raised
       object rather than a flat hole. These were pinned to a fixed periwinkle and a fixed
       #111824 base, which put a blue sheen on every card whatever the theme — the one place a
       teal or amber Portal still looked blue. Both ends now come from the hue. */
    --zen-dark-panel-gradient:
        radial-gradient(circle at top right, hsl(var(--zen-h) 60% 76% / 0.07), transparent 30%),
        linear-gradient(180deg, var(--zen-surface-panel), hsl(var(--zen-h) 15% 9%));
    --zen-dark-card-gradient:
        radial-gradient(circle at 92% 12%, hsl(var(--zen-h) 60% 78% / 0.06), transparent 28%),
        linear-gradient(160deg, var(--zen-surface-elevated), hsl(var(--zen-h) 14% 10%));
    --zen-dark-card-gradient-active:
        radial-gradient(circle at 92% 12%, hsl(var(--zen-h) 66% 78% / 0.1), transparent 28%),
        radial-gradient(circle at 0% 100%, hsl(var(--zen-h3) 60% 66% / 0.07), transparent 38%),
        linear-gradient(160deg, hsl(var(--zen-h) 15% 16%), hsl(var(--zen-h) 14% 10%));
    --zen-surface-accent-2-soft: hsl(var(--zen-h2) 62% 70% / 0.15);
    --zen-surface-accent-3-soft: hsl(var(--zen-h3) 58% 64% / 0.15);
}

/* Midnight is the same theme with the light turned further down, not a third palette.

   html.dark is in the selector list above, and choosing midnight also sets that class, so at
   (0,1,0) this block lost every one of these to it — midnight rendered as an exact copy of dim.
   Qualifying with the class it already carries takes it to (0,2,1), which wins. */
html.dark[data-mode="midnight"],
[data-mode="midnight"] {
    --zen-surface-page: hsl(var(--zen-h) 20% 3%);
    --zen-surface-panel: hsl(var(--zen-h) 17% 7%);
    --zen-surface-subtle: hsl(var(--zen-h) 14% 11.5%);
    --zen-surface-input: hsl(var(--zen-h) 15% 9.5%);
    --zen-surface-elevated: hsl(var(--zen-h) 16% 9%);
    --zen-surface-strong: hsl(var(--zen-h) 22% 2%);
    --zs-sidebar-bg: hsl(var(--zen-h) 20% 4%);
    --zs-topbar-bg: hsl(var(--zen-h) 20% 4%);
}

/* Contrast is an accessibility mode, not a colour scheme: the hue must not reach any surface. */
[data-mode="contrast"] {
    --zen-surface-page: #ffffff;
    --zen-surface-panel: #ffffff;
    --zen-surface-subtle: #ffffff;
    --zen-surface-input: #ffffff;
    --zen-surface-elevated: #ffffff;

    /* The one surface that stays inverted in contrast mode, because it is a device screen and
       not a panel. Flat black, white ink, and the chips get a hard edge instead of a wash. */
    --zen-surface-strong: #000000;
    --zen-on-strong: #ffffff;
    --zen-on-strong-muted: #ffffff;
    --zen-on-strong-fill: transparent;
    --zen-on-strong-edge: #ffffff;
    --zen-text-strong: #000000;
    --zen-text: #000000;
    --zen-text-muted: #1a1a1a;
    --zen-text-soft: #1a1a1a;
    --zen-shadow-panel: none;
    --zs-sidebar-bg: #ffffff;
    --zs-sidebar-active: #ffe680;
    --zs-sidebar-hover: #f0f0f0;
    --zs-sidebar-text: #000000;
    --zs-sidebar-label: #000000;
    --zs-topbar-bg: #ffffff;
    --zs-primary: #000000;
    --zen-accent-2: #000000;
    --zen-accent-2-ink: #000000;
    --zen-accent-3: #000000;
    --zen-accent-3-ink: #000000;
    --zen-surface-accent-2-soft: #ffe680;
    --zen-surface-accent-3-soft: #ffe680;
}

/* ── components inherit the page hue ─────────────────────────────────────
   app.css routes most of its colour identity through this handful of tokens —
   chips, status pills, table rules, panel borders, focus rings and links all read
   them. Repointing them at --zen-h is what makes a page read as one colour family
   rather than a coloured header over neutral content, and it costs one block
   instead of touching every component.

   Left alone deliberately: success, warning and error stay their own colours.
   Those carry meaning, and tinting them to match a page would make a failed
   payout look like decoration. */

:root {
    /* 30%, not 44%. These carry text — chip labels on the accent-soft wash, and links on a
       white panel — and a given lightness buys very different contrast per hue. Measured
       across all eleven: teal chips were 2.51:1 and amber links 2.63:1 at the old values,
       both far under the 4.5 these sizes need. At 30% the worst cases are 4.97 and 4.80.
       Raising either number puts teal and amber back under. */
    --zen-violet: hsl(var(--zen-h) 56% 30%);
    --zen-berry: hsl(calc(var(--zen-h) + 34) 62% 36%);
    --zen-surface-accent-soft: hsl(var(--zen-h) 62% 50% / 0.09);
    --zen-border-subtle: hsl(var(--zen-h) 48% 44% / 0.16);
    --zen-border-faint: hsl(var(--zen-h) 48% 44% / 0.09);
    --zen-focus-ring: hsl(var(--zen-h) 62% 50% / 0.16);
    --zen-link: hsl(calc(var(--zen-h) + 10) 62% 30%);
    --zen-link-hover: hsl(calc(var(--zen-h) + 10) 66% 22%);
}

html.dark, [data-mode="dim"], [data-mode="midnight"] {
    /* Same relationship inverted: on a dark panel the card sits slightly above it, and the edge
       is a light hairline rather than a dark one. */
    --zen-card-surface: hsl(var(--zen-h) 18% 17%);
    --zen-card-edge: hsl(var(--zen-h) 30% 72% / 0.28);
    --zen-card-edge-strong: hsl(var(--zen-h) 45% 78% / 0.5);
    --zen-violet: hsl(var(--zen-h) 62% 78%);
    --zen-berry: hsl(calc(var(--zen-h) + 34) 56% 72%);
    --zen-surface-accent-soft: hsl(var(--zen-h) 60% 72% / 0.14);
    --zen-border-subtle: hsl(var(--zen-h) 50% 76% / 0.22);
    --zen-border-faint: hsl(var(--zen-h) 40% 84% / 0.1);
    --zen-focus-ring: hsl(var(--zen-h) 60% 74% / 0.18);
    --zen-link: hsl(calc(var(--zen-h) + 10) 72% 76%);
    --zen-link-hover: hsl(calc(var(--zen-h) + 10) 78% 86%);
}

/* Contrast last: hue must not leak back in through any of the above. */
[data-mode="contrast"] {
    --zen-violet: #000000;
    --zen-berry: #000000;
    --zen-surface-accent-soft: #ffe680;
    --zen-border-subtle: #000000;
    --zen-border-faint: #000000;
    --zen-focus-ring: #000000;
    --zen-link: #0000cc;
    --zen-link-hover: #000088;
}















/* The radius scale collapses every .btn to the small step, which squared off this
   circular icon button. Pill shape here is intent, not drift. Four classes because
   the radius rule's :not() chain counts toward its specificity and it sits later in
   this file, so anything lighter loses. */
.workspace-gate__card .btn.workspace-gate__action.workspace-gate__action-icon {
    border-radius: calc(999px * var(--zen-radius-scale, 1)) !important;
}

[data-mode="contrast"] .workspace-gate__card .btn-primary {
    background: #ffffff !important;
    border: 2px solid #000000 !important;
    color: #000000 !important;
    box-shadow: none !important;
}











/* ============================================================================
   Section enclosure.

   A section used to be a header and a grid of cards both floating on the page
   wash, with nothing saying they belonged together and only whitespace marking
   where one section ended. Each section is now its own panel, so the boundary is
   an edge and the header sits inside the box it introduces.

   Applied to the section wrapper that already exists in every page's markup.
   :not(.admin-panel) skips sections that are already panels themselves.

   Not restricted to direct children of admin-view-stack: Pass, Finance and the
   other tabbed pages nest their sections inside tab content, and a direct-child
   selector missed every one of them.

   Sections that ARE a grid are excluded from both rules. Their direct children are
   the cards — Help renders each article as an admin-panel in admin-help-grid — so
   enclosing them boxes a row of boxes, and dissolving them erases the cards
   entirely. That is exactly what happened to the help articles.

   Panels nested inside an enclosed section lose their own outline rather than
   being skipped. Finance and Pass put a header beside a panel-wrapped table,
   which is the floating-header problem itself — skipping those sections would
   leave the exact case this is meant to fix. Their padding and background stay,
   so several sibling panels still read as separate blocks inside the section.
   ============================================================================ */









/* ── page hero ribbons ───────────────────────────────────────────────────
   The login shell's silhouette, applied inside the header. Same construction as
   AuthSplitShell: a blurred 72px band under two gradient ribbons and two white
   highlights, all round-capped. The SVG is injected by portal-appearance.js
   rather than being a background image, because a data: URI cannot read the CSS
   variables that make it follow the page hue.

   app.css gives every hero a fixed pair of hexes per page; the ribbons and the
   hue-derived background below replace that. */







.zs-hero-ribbons {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
    border-radius: inherit;
    opacity: var(--zen-rib-opacity);
}

.zs-hero-ribbons svg { width: 100%; height: 100%; display: block; }

.zs-rib-a-1 { stop-color: var(--zen-rib-1); stop-opacity: 0.72; }
.zs-rib-a-2 { stop-color: var(--zen-rib-2); stop-opacity: 0.56; }
.zs-rib-a-3 { stop-color: var(--zen-rib-3); stop-opacity: 0.48; }
.zs-rib-b-1 { stop-color: var(--zen-rib-2); stop-opacity: 0.38; }
.zs-rib-b-2 { stop-color: var(--zen-rib-1); stop-opacity: 0.42; }
.zs-rib-b-3 { stop-color: var(--zen-rib-3); stop-opacity: 0.24; }
.zs-rib-c-1 { stop-color: var(--zen-rib-glow); stop-opacity: 0.68; }
.zs-rib-c-2 { stop-color: var(--zen-rib-glow); stop-opacity: 0.42; }
.zs-rib-c-3 { stop-color: var(--zen-rib-glow); stop-opacity: 0.58; }

.zs-rib-highlight {
    stroke: var(--zen-rib-highlight);
    stroke-opacity: var(--zen-rib-highlight-opacity);
}

@media (prefers-reduced-motion: no-preference) {
    .zs-hero-ribbons { transition: opacity 0.25s ease; }
}





/* Buttons and chips keep their pill shape where that is the design; everything
   else that was 1rem+ comes down to the small step. */
.btn:not(.btn-link):not([class*="rounded-pill"]) {
    border-radius: var(--zen-radius-sm) !important;
}



/* ============================================================================
   Card spacing.

   app.css spaces cards through 155 separate gap rules ranging 0.1rem to 2rem, so
   the distance between two cards depends on which grid happens to hold them. One
   token, capped at 5px, applied to the containers that hold cards — grids, decks
   and card lists.

   Deliberately NOT applied to *-row, *-topline, *-body, *-meta and friends: those
   space content inside a card, and collapsing them to 5px would destroy the
   layout rather than tidy it.
   ============================================================================ */

:root {
    /* The step between cards inside one block. It was 5px — off the scale, and below the width at
       which a gap reads as a gap rather than as a printing fault. --zen-s2 is the tightest step the
       scale offers and still separates two cards. */
    --zen-card-gap: var(--zen-s2);

    /* A card's own surface and edge, so it reads as a plane on the panel behind it rather than
       relying on a shadow. The edge sits near 2.5:1 against a white panel — a boundary needs
       roughly 3:1 to register, and the old 0.06 alpha measured 1.12:1, which is why the Home
       board looked like text floating on a sheet. */
    --zen-card-surface: hsl(var(--zen-h) 24% 99%);
    /* 58% lightness measures 2.62:1 against a white panel. 74% measured 1.74, which still read as
       a suggestion rather than an edge; the values were checked rather than guessed. */
    --zen-card-edge: hsl(var(--zen-h) 18% 58%);
    --zen-card-edge-strong: hsl(var(--zen-h) 34% 46%);

    /* The step between a page's own blocks — the header and the sections under it. Cards inside
       one block stay on --zen-card-gap, so the two distances differ and the hierarchy reads: a
       header is further from the content than the cards are from each other. Both were pinned to
       the same 5px, which is what made a page header look like the top of the first card. */
    --zen-page-gap: var(--zen-s4);
}







html.dark .admin-section-header[data-wave]::after { opacity: 0.3; }

/* Contrast mode opts out of decoration entirely, the same way it opts out of the wash and the
   blur: a faint gradient behind a label is precisely what it exists to remove. */
[data-mode="contrast"] .admin-section-header[data-wave]::after { display: none; }

