/* =====================================================================
 *  theme.css — design tokens
 *  Material 3 colour roles + glassmorphism surfaces, dual theme.
 *
 *  Everything downstream reads from these variables. To re-skin the app
 *  for another unit, change this file and nothing else.
 * ===================================================================== */

/* --- Local fonts. No CDN, no Google Fonts request. ------------------ */
@font-face {
    font-family: 'Heebo';
    src: url('../vendor/fonts/heebo-400.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Heebo';
    src: url('../vendor/fonts/heebo-600.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Heebo';
    src: url('../vendor/fonts/heebo-700.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

:root {
    /* --- Brand ------------------------------------------------------ */
    --md-primary:           #2563eb;
    --md-primary-hover:     #1d4ed8;
    --md-on-primary:        #ffffff;
    --md-primary-container: #dbeafe;
    --md-secondary:         #0d9488;
    --md-tertiary:          #7c3aed;

    /* --- Semantic --------------------------------------------------- */
    --md-success: #16a34a;
    --md-warning: #d97706;
    --md-danger:  #dc2626;
    --md-info:    #0284c7;

    /* --- Shift fill states (the calendar's visual language) ----------
       Ordered by URGENCY, not by how full the shift is. A shift with
       nobody on it is the most urgent thing in the app, so it must not
       be painted grey — grey reads as "inactive/unavailable" and inverts
       the hierarchy against what the squad actually needs to see. */
    --state-full:      var(--md-success);   /* מלא — nothing needed */
    --state-open:      var(--md-info);      /* partly staffed */
    --state-needs-one: var(--md-warning);   /* one short */
    --state-empty:     #dc2626;             /* nobody at all — most urgent */
    --state-mine:      var(--md-tertiary);  /* you are on it */

    /* Squad-wide ("כל הצוותים") shift — a THEME colour, not a DB one. These
       shifts have no team row to carry a colour, and the old neutral grey
       tint was invisible against the cell background: a day held by the
       whole squad looked like a day with nothing on it. Pink collides with
       no team colour in use (blue/green/orange) and no state colour. */
    --team-all:        #d63384;

    /* HOLIDAYS — a colour family of their own, and deliberately not one of
       the state colours above. The dots along the bottom of a day cell and
       the tint behind it already say "staffing" and "which team"; a holiday
       painted in success-green or warning-amber would be read as a sixth
       staffing state by anyone glancing at the month. Gold, flag-blue and
       slate belong to no other signal in the app. */
    --holiday-festive:  #b45309;   /* חג ומועד */
    --holiday-national: #1d4ed8;   /* יום ממלכתי */
    --holiday-mourning: #64748b;   /* צום ויום זיכרון — שקט בכוונה */

    /* Chip foregrounds. The state colours above are tuned for 6px dots and
       4px stripes (3:1 non-text threshold); at 12px/600 chip text they need
       4.5:1, which the raw hues do not reach on a 16% tint. These are the
       darkened/lightened equivalents used for chip labels only. */
    --chip-full-fg:      #0f5132;
    --chip-open-fg:      #013a58;
    --chip-needs-one-fg: #7c2d12;
    --chip-empty-fg:     #7f1d1d;
    --chip-mine-fg:      #4c1d95;

    /* Navigation foregrounds, separate from the brand colours they derive
       from — and they have to be, because both sit on a TINTED background.
       --md-primary on an 18% --md-primary wash measured 4.01:1 in this
       theme and 4.42:1 in the dark one: both under 4.5, and this is the
       "which page am I on" indicator, so it is exactly the text that must
       not be the hardest to read. Darkened here, lightened for dark. */
    --nav-active-fg:     #1d4ed8;
    --nav-danger-fg:     #b91c1c;

    /* --- Light surfaces --------------------------------------------- */
    --surface-0:  #f6f8fc;
    --surface-1:  #ffffff;
    --surface-2:  #f1f5f9;
    --surface-3:  #cbd5e1;   /* darkened: the progress-ring track was
                                invisible at 1.23:1 against surface-1 */
    --on-surface: #0f172a;
    /* #64748b measured 4.48:1 on surface-0 — a hair under the 4.5 floor,
       and this token carries shift times and slot counts. */
    --on-surface-muted: #526077;
    --outline:    rgba(15, 23, 42, 0.18);

    /* --- Glass ------------------------------------------------------
       0.92, not 0.72: body text sits on these surfaces and the animated
       aurora behind them made real contrast unpredictable outdoors. */
    --glass-bg:     rgba(255, 255, 255, 0.92);
    --glass-border: rgba(255, 255, 255, 0.85);
    --glass-blur:   18px;
    --glass-shadow: 0 8px 32px rgba(15, 23, 42, 0.08);

    /* --- Shape (M3 rounding scale) ---------------------------------- */
    --radius-xs: 8px;
    --radius-sm: 12px;
    --radius-md: 16px;
    --radius-lg: 24px;
    --radius-xl: 32px;
    --radius-full: 999px;

    /* --- Elevation -------------------------------------------------- */
    --elev-1: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.10);
    --elev-2: 0 4px 8px rgba(15, 23, 42, 0.06), 0 2px 4px rgba(15, 23, 42, 0.08);
    --elev-3: 0 10px 20px rgba(15, 23, 42, 0.08), 0 3px 6px rgba(15, 23, 42, 0.06);

    /* --- Motion. M3 emphasized easing; short enough to feel instant.  */
    --ease-standard:   cubic-bezier(0.2, 0, 0, 1);
    --ease-emphasized: cubic-bezier(0.05, 0.7, 0.1, 1);
    --ease-spring:     cubic-bezier(0.34, 1.56, 0.64, 1);
    --dur-fast:   120ms;
    --dur-normal: 220ms;
    --dur-slow:   380ms;

    --font-ui: 'Heebo', 'Segoe UI', system-ui, -apple-system, 'Arial Hebrew', sans-serif;

    /* Bootstrap bridge — keeps BS components on our palette. */
    --bs-primary: var(--md-primary);
    --bs-body-font-family: var(--font-ui);
    --bs-border-radius: var(--radius-sm);
}

/* --- Dark theme ----------------------------------------------------- */
[data-bs-theme="dark"] {
    --md-primary:           #60a5fa;
    --md-primary-hover:     #3b82f6;
    --md-on-primary:        #0b1220;
    --md-primary-container: #1e3a5f;

    --surface-0:  #0b1220;
    --surface-1:  #131c2e;
    --surface-2:  #1b2740;
    --surface-3:  #33456e;   /* lightened so the ring track is visible */
    --on-surface: #e8edf7;
    --on-surface-muted: #a8b6cc;
    --outline:    rgba(232, 237, 247, 0.20);

    --glass-bg:     rgba(19, 28, 46, 0.92);
    --glass-border: rgba(255, 255, 255, 0.10);
    --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.42);

    --elev-1: 0 1px 3px rgba(0, 0, 0, 0.40);
    --elev-2: 0 4px 10px rgba(0, 0, 0, 0.45);
    --elev-3: 0 12px 28px rgba(0, 0, 0, 0.50);

    --state-empty: #f87171;
    --team-all:    #f472b6;   /* lightened for dark surfaces */

    /* Holiday stripes, lightened the same way — the light-theme gold and
       navy both disappear against a dark cell. */
    --holiday-festive:  #fbbf24;
    --holiday-national: #93c5fd;
    --holiday-mourning: #94a3b8;

    /* Dark theme needs LIGHT chip text on the same tinted backgrounds. */
    --chip-full-fg:      #6ee7a8;
    --chip-open-fg:      #7dd3fc;
    --chip-needs-one-fg: #fcd34d;
    --chip-empty-fg:     #fca5a5;
    --chip-mine-fg:      #c4b5fd;

    /* --md-danger (#dc2626) was never overridden here, so the logout link
       kept the light theme's red on a near-black sidebar: 3.19:1. It is
       fine as a BACKGROUND (the toast border, the SOS button) which is why
       it went unnoticed — only as text does it fail. */
    --nav-active-fg:     #93c5fd;
    --nav-danger-fg:     #f87171;
}

/* --- The help centre (v1.36) ------------------------------------------
   Same tokens, same cards. The print block is what turns the page into
   a clean booklet: everything opens, chrome disappears, and no article
   is ever cut across a page. */
.help-beacon {
    color: var(--on-surface-muted);
    font-size: .85em;
    margin-inline-start: .3rem;
    text-decoration: none;
    opacity: .7;
}
.help-beacon:hover, .help-beacon:focus-visible { opacity: 1; color: var(--md-primary); }
.help-search { display: flex; align-items: center; gap: .5rem; padding-inline: .9rem; color: var(--on-surface-muted); }
.help-search input { color: var(--on-surface); box-shadow: none; }
.help-pills { display: flex; flex-wrap: wrap; gap: .45rem; }
.help-topic { padding: 0; overflow: hidden; }
.help-topic > summary {
    list-style: none; cursor: pointer;
    padding: .8rem 1rem; font-weight: 700;
    display: flex; align-items: center; gap: .5rem;
}
.help-topic > summary::-webkit-details-marker { display: none; }
.help-topic > summary::after {
    content: "‹"; margin-inline-start: auto; color: var(--on-surface-muted);
    transition: transform .15s;
}
.help-topic[open] > summary::after { transform: rotate(-90deg); }
.help-body { padding: 0 1rem .9rem; }
.help-step, .help-plain { display: flex; gap: .6rem; align-items: baseline; margin: 0 0 .45rem; color: var(--on-surface-muted); }
.help-step .help-num {
    flex-shrink: 0; width: 1.35rem; height: 1.35rem; border-radius: 50%;
    background: var(--md-primary); color: var(--md-on-primary);
    font-size: .74rem; font-weight: 800;
    display: inline-flex; align-items: center; justify-content: center;
    transform: translateY(.15rem);
}
.help-tip {
    background: color-mix(in srgb, var(--md-primary) 10%, transparent);
    border-radius: var(--radius-xs); padding: .5rem .7rem;
    font-size: .88rem; color: var(--on-surface); margin: .5rem 0 0;
}
.help-tour { margin-top: .6rem; }
.help-related { font-size: .84rem; color: var(--on-surface-muted); margin: .6rem 0 0; }
.help-related a { margin-inline-start: .4rem; }
.help-feedback {
    display: flex; align-items: center; gap: .5rem;
    border-top: 1px solid var(--outline); margin-top: .7rem; padding-top: .6rem;
    font-size: .84rem; color: var(--on-surface-muted);
}
.help-term { display: flex; gap: .6rem; margin-bottom: .35rem; }
.help-term dt { flex-shrink: 0; min-width: 7.5rem; font-weight: 700; }
.help-term dd { margin: 0; color: var(--on-surface-muted); }

/* The booklet front matter exists only on paper. */
.help-cover, .help-toc { display: none; }

@media print {
    /* ==== The booklet (v1.36.2) =======================================
       A manual, not a printed web page: cover, table of contents, one
       chapter per page, full page width, forced light ink over any
       screen theme (including operational night mode). */
    @page { margin: 14mm 12mm; }

    .app-bar, .app-sidebar, .news-ticker, .help-search, .help-pills,
    .help-feedback, .help-tour, .help-beacon, #helpEmpty, .help-screen-head,
    #helpPrint, .toast-stack, .bottom-bar, .skip-link { display: none !important; }

    /* Ink, regardless of the theme the screen was in. */
    body { background: #fff !important; color: #111 !important; font-size: 10.5pt; }
    #mainContent { max-width: none !important; padding: 0 !important; }
    #mainContent, #mainContent * {
        color: #111 !important;
        background: transparent !important;
        box-shadow: none !important;
        text-shadow: none !important;
    }

    /* Cover page. */
    .help-cover {
        display: flex !important; flex-direction: column;
        align-items: center; justify-content: center; text-align: center;
        min-height: 88vh; break-after: page; page-break-after: always;
    }
    .help-cover .hc-logos {
        display: flex; align-items: center; gap: 10mm; margin-bottom: 9mm;
    }
    .help-cover .hc-logos img { height: 17mm; width: auto; }
    .help-cover .hc-logos .hc-logo-main { height: 26mm; }
    .help-cover .hc-org   { font-size: 14pt; letter-spacing: .06em; margin: 0 0 .4rem; }
    .help-cover .hc-title { font-size: 34pt; font-weight: 800; margin: 0 0 .8rem; }
    .help-cover .hc-sub   { font-size: 12pt; max-width: 34rem; margin: 0 0 2.2rem; }
    .help-cover .hc-meta  { font-size: 10.5pt; border-top: 1pt solid #777; padding-top: .8rem; }

    /* Table of contents — an index: chapters and articles numbered with
       the same counters the body uses, so "3.2" in the TOC is findable
       by eye under the chapter that opens with "3." on its own page.
       (Page-number cross references — target-counter() — are not yet
       painted by Chromium, so the numbering IS the index.) */
    .help-toc { display: block !important; break-after: page; page-break-after: always; counter-reset: chap; }
    .help-toc h2 { font-size: 16pt; border-bottom: 2pt solid #333; padding-bottom: 3pt; margin: 0 0 .8rem; }
    .help-toc .toc-sec { break-inside: avoid; margin-bottom: .7rem; counter-increment: chap; counter-reset: art; }
    .help-toc h3 { font-size: 12pt; margin: 0 0 .25rem; }
    .help-toc h3::before { content: counter(chap) ".  "; }
    .help-toc ul { margin: 0; padding-inline-start: 1.2rem; columns: 2; column-gap: 8mm; list-style: none; }
    .help-toc li { font-size: 9.5pt; margin-bottom: .12rem; break-inside: avoid; counter-increment: art; }
    .help-toc li::before { content: counter(chap) "." counter(art) "  "; color: #666; font-variant-numeric: tabular-nums; }

    /* One chapter per page, articles kept whole and compact — numbered
       to match the table of contents. */
    #mainContent { counter-reset: bchap; }
    .help-section { break-before: page; page-break-before: always; counter-increment: bchap; counter-reset: bart; }
    .help-section h2 {
        font-size: 15pt; border-bottom: 2pt solid #333;
        padding-bottom: 3pt; margin-bottom: .6rem; break-after: avoid;
    }
    .help-section h2::before { content: counter(bchap) ".  "; }
    .help-section h2 .bi, .help-toc h3 .bi { display: none; }
    .help-topic summary::before {
        counter-increment: bart;
        content: counter(bchap) "." counter(bart) "  ";
        color: #555; font-variant-numeric: tabular-nums;
    }
    .help-topic, .help-term, .help-glossary { break-inside: avoid; page-break-inside: avoid; }
    .help-topic {
        border: .75pt solid #999 !important; border-radius: 3pt;
        padding: 5pt 8pt; margin-bottom: 6pt;
    }
    .help-topic summary { font-weight: 700; font-size: 11pt; list-style: none; }
    .help-topic summary::-webkit-details-marker,
    .help-topic summary::marker { display: none; content: ''; }
    .help-step, .help-plain { margin: 2pt 0; font-size: 10pt; }
    .help-tip { border: .75pt dashed #999 !important; padding: 3pt 6pt; font-size: 9.5pt; }
    .help-related { font-size: 9pt; }
    /* The glossary is the closing chapter — numbered like the others so
       the body agrees with the table of contents. */
    .help-glossary { break-before: page; page-break-before: always; counter-increment: bchap; }
    .help-glossary h2 { font-size: 15pt; border-bottom: 2pt solid #333; padding-bottom: 3pt; }
    .help-glossary h2::before { content: counter(bchap) ".  "; }

    /* A wide sheet gets two columns — the reason to print landscape. */
    @media (orientation: landscape) {
        .help-section, .help-toc ul { column-count: 2; column-gap: 10mm; }
        .help-section h2 { column-span: all; }
        .help-toc ul { columns: 3; }
    }
}

/* --- Personal dashboard layout (v1.35) --------------------------------
   The <details> popover for choosing cards, and the rule that makes a
   user's "hide" beat a card's own "I have content, show me". */
.dash-custom { position: relative; display: inline-block; }
.dash-custom > summary { list-style: none; display: inline-flex; align-items: center; }
.dash-custom > summary::-webkit-details-marker { display: none; }
.dash-custom-menu {
    position: absolute; z-index: 40; margin-top: .4rem;
    min-width: 15rem; inset-inline-start: 0;
}
[data-user-hidden] { display: none !important; }

/* --- The command palette (v1.35) --------------------------------------
   Ctrl+K / the app-bar search button. A centered dialog over a scrim;
   everything colored through the tokens so night-ops and both themes
   come free. */
.cmdk-overlay {
    position: fixed; inset: 0; z-index: 1080;
    background: rgba(5, 8, 15, 0.55);
    backdrop-filter: blur(3px);
    display: flex; align-items: flex-start; justify-content: center;
    padding: 10vh 1rem 1rem;
}
.cmdk {
    width: 100%; max-width: 34rem;
    background: var(--surface-1);
    border: 1px solid var(--outline);
    border-radius: var(--radius-md);
    box-shadow: var(--elev-3);
    overflow: hidden;
    display: flex; flex-direction: column;
    max-height: 70vh;
}
.cmdk-inputwrap {
    display: flex; align-items: center; gap: .6rem;
    padding: .8rem 1rem;
    border-bottom: 1px solid var(--outline);
    color: var(--on-surface-muted);
}
.cmdk-input {
    flex: 1; border: 0; outline: 0; background: transparent;
    color: var(--on-surface); font-size: 1rem; font-family: var(--font-ui);
}
.cmdk-hint {
    font-size: .68rem; color: var(--on-surface-muted);
    border: 1px solid var(--outline); border-radius: 6px; padding: .1rem .4rem;
}
.cmdk-list { overflow-y: auto; padding: .4rem; }
.cmdk-ghead {
    font-size: .68rem; letter-spacing: .06em; text-transform: uppercase;
    color: var(--on-surface-muted); margin: .55rem .6rem .25rem;
}
.cmdk-item {
    display: flex; align-items: center; gap: .6rem;
    padding: .5rem .6rem; border-radius: var(--radius-xs);
    color: var(--on-surface); text-decoration: none; font-size: .92rem;
}
.cmdk-item.active, .cmdk-item:hover {
    background: var(--md-primary-container);
}
.cmdk-ic  { color: var(--on-surface-muted); flex-shrink: 0; }
.cmdk-title { flex-shrink: 0; }
.cmdk-sub {
    color: var(--on-surface-muted); font-size: .78rem;
    margin-inline-start: auto; text-align: start;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cmdk-empty { color: var(--on-surface-muted); text-align: center; padding: 1.2rem; margin: 0; }

/* --- The running news strip (v1.32.1) --------------------------------
   A thin marquee of active notices under the app bar. The whole strip
   is one link to the board. Direction-aware: the sentence's BEGINNING
   enters first in both scripts — from the left edge moving right in
   RTL, mirrored in LTR. Hover/focus pauses; ticker.js swaps the whole
   thing for a static line under prefers-reduced-motion. */
.news-ticker {
    overflow: hidden;
    background: var(--surface-2);
    border-bottom: 1px solid var(--outline);
    line-height: 1;
}
.news-ticker-track {
    display: inline-block;
    white-space: nowrap;
    padding-block: .45rem;
    padding-inline-start: 100%;
    font-size: .85rem;
    color: var(--on-surface);
    text-decoration: none;
    will-change: transform;
    animation: newsTickerRtl 30s linear infinite;
}
html[dir="ltr"] .news-ticker-track { animation-name: newsTickerLtr; }
.news-ticker:hover .news-ticker-track,
.news-ticker:focus-within .news-ticker-track { animation-play-state: paused; }
.news-ticker-item { margin-inline: .35rem; }
.news-ticker-sep  { color: var(--on-surface-muted); margin-inline: .55rem; }
.news-ticker-more {
    color: var(--on-surface-muted);
    font-size: .78rem;
}
@keyframes newsTickerRtl {
    from { transform: translateX(0); }
    to   { transform: translateX(100%); }
}
@keyframes newsTickerLtr {
    from { transform: translateX(0); }
    to   { transform: translateX(-100%); }
}
@media (prefers-reduced-motion: reduce) {
    .news-ticker-track { animation: none; padding-inline-start: .75rem; }
}

/* --- Direction-aware chevrons ----------------------------------------
   Arrows that mean prev/next are LOGICAL: authored for the RTL layout
   (prev points right), and mirrored wholesale when the UI runs LTR.
   scaleX(-1) rather than swapping icon classes: one CSS rule covers
   every such arrow, present and future, in every language at once. */
html[dir="ltr"] .dir-chevron {
    display: inline-block;
    transform: scaleX(-1);
}

/* --- Night-ops mode (v1.31) ------------------------------------------
   A red-on-near-black interface for night patrol. Red light is the one
   part of the spectrum the eye's rods barely respond to, so a red screen
   can be read without resetting the ~30 minutes of dark adaptation a
   lookout depends on. That physiology is the whole spec: NO blues, NO
   whites, brightness kept low, and every accent collapsed onto the same
   red band — a colour-coded status chip is worthless to night vision
   anyway.

   Attribute selector on :root, set by head.php before first paint (the
   flash IS the failure mode) and synced to the account like font_scale.
   It deliberately layers on top of whichever bs-theme is active: the
   variables below override both palettes wholesale. */
:root[data-night-ops="1"] {
    --md-primary:           #f87171;
    --md-primary-hover:     #ef4444;
    --md-on-primary:        #1a0505;
    --md-primary-container: #3f0d0d;

    --surface-0:  #0a0303;
    --surface-1:  #140606;
    --surface-2:  #1e0909;
    --surface-3:  #351111;
    /* Brightened after field feedback: the first values sat right at the
       WCAG floor and read as "invisible text" on a dimmed phone — and a
       night-patrol phone is ALWAYS dimmed. Still inside the red band. */
    --on-surface: #fbc7c0;
    --on-surface-muted: #d9a49c;
    --outline:    rgba(245, 176, 168, 0.28);

    --glass-bg:     rgba(20, 6, 6, 0.94);
    --glass-border: rgba(245, 176, 168, 0.12);
    --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);

    /* One band of red for every state: night vision cannot tell the
       colours apart, so pretending otherwise just adds glare. Fill
       level still reads through text and shape. */
    --state-empty: #f87171;
    --team-all:    #e08a83;

    /* Holidays join the single red band for the same reason the states do:
       under night vision the hues are indistinguishable, so three of them
       would be three kinds of glare and no extra information. The stripe
       still says "something today"; the name in the list says what. */
    --holiday-festive:  #f5b0a8;
    --holiday-national: #f5b0a8;
    --holiday-mourning: #f5b0a8;

    --chip-full-fg:      #f5b0a8;
    --chip-open-fg:      #f5b0a8;
    --chip-needs-one-fg: #f5b0a8;
    --chip-empty-fg:     #f87171;
    --chip-mine-fg:      #f5b0a8;
    --nav-active-fg:     #f5b0a8;
    --nav-danger-fg:     #f87171;

    --bs-primary: var(--md-primary);
}

/* Photos, icons and any stray full-colour element get pulled down to the
   same band. Dimming beats recolouring: it needs no per-component work
   and cannot miss one. */
:root[data-night-ops="1"] img,
:root[data-night-ops="1"] .team-dot,
:root[data-night-ops="1"] .badge {
    filter: sepia(1) saturate(3) hue-rotate(-35deg) brightness(0.72);
}

/* ---------------------------------------------------------------------
 * Accessibility: honour the OS "reduce motion" setting. Vestibular
 * disorders are not an edge case, and this app runs on phones at night.
 * ------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* =====================================================================
 *  v1.4 — USER-CONTROLLED TEXT SIZE (Phase D)
 *
 *  One number, read by exactly one rule (html { font-size } in app.css).
 *  Everything else in this app is already expressed in rem, so scaling the
 *  root scales the whole layout — spacing, tap targets, the calendar cells
 *  and the bottom bar all move together. That is the entire point: an
 *  override that only touched body text would enlarge the words inside
 *  boxes that stayed the same size, which is how "large text" modes end up
 *  clipping their own labels.
 *
 *  THE MULTIPLIERS WERE MEASURED, not picked. 1.15 and 1.30 were checked
 *  at 320px, 360px and 1280px against the three densest things in the app
 *  — the seven-column calendar grid (square cells with a 44px floor), the
 *  shift card, and the six-slot bottom navigation, the last of these with
 *  the Russian labels, which are the longest the app ships. At 1.30 every
 *  one of them still fits with room to spare and the page has no
 *  horizontal overflow at any of the three widths.
 *
 *  THREE STEPS, NOT FOUR. A fourth would have to be around 1.5, and at
 *  that point the bottom bar's labels need a third line on a narrow phone.
 *  A step that is offered but crowds the screen is not a setting, it is a
 *  trap — and 1.30 on top of whatever the reader has already set in their
 *  browser or in iOS is a large size already, because these multiply.
 *
 *  These tokens live BELOW the dark-theme block on purpose: they are not
 *  colours, they do not differ per theme, and tests/accessibility.php reads
 *  this file by splitting it on the dark selector. Nothing here shadows a
 *  colour token in either half of that split.
 * ------------------------------------------------------------------- */
:root {
    /* No data-font-scale attribute at all — the untouched default. */
    --font-scale: 1;
}

:root[data-font-scale="normal"] { --font-scale: 1; }
:root[data-font-scale="large"]  { --font-scale: 1.15; }
:root[data-font-scale="xlarge"] { --font-scale: 1.3; }
