/* ==========================================================================
   BetterLoyaltyRewards — design system  ("Dark Metal")

   Founder decision 2026-10-22: the web adopts the Dark Metal system that was
   locked for mobile on 2026-10-21, for brand continuity. This REPLACES
   "Periwinkle Calm", which the founder rejected. It also reverses doc 13's
   earlier "parity between web and mobile is explicitly NOT a goal" — the two
   surfaces are now meant to look like one product.

   Full rationale, brand sourcing and the material rules: 09_visual_theme.md
   Part 2. The three rules that matter most, because breaking them has already
   cost us twice:

     1. BANDING NEEDS AREA. Full banded metal (--m, --brand-metal) only on
        surfaces with NO text on top. Anything a label sits on uses the
        lighter `face` variant. A banded gradient is light in places and dark
        in others, so ink that looks fine on a swatch lands on a dark band
        somewhere else.
     2. GOLD MEANS EXACTLY ONE THING: a reward is waiting. Never decorative,
        never emphasis. Because it has one meaning it never needs a legend.
     3. NEVER !important TO WIN SPECIFICITY. Double the class (.metal.metal).
        A component class that declares its own background outranks a utility
        at equal specificity and silently produces invisible UI.
   ========================================================================== */

:root {
    color-scheme: dark;

    /* --- Brand: brushed satin, sampled from the 1440 Core logo ------------
       Deliberately quieter than the reward metal so gold carries hierarchy. */
    --brand-deep:   #186CB4;
    --brand-mid:    #24A8D8;
    --brand-bright: #3DC8F5;

    /* Full banded — surfaces with no text on them. */
    --brand-metal: linear-gradient(135deg,
        #185C92 0%, #1F76B2 16%, #3FA6D4 32%, #2589C0 46%,
        #1C6DA4 58%, #3C9ECB 74%, #227FB4 88%, #17578B 100%);
    /* Face — every stop stays light, so dark ink is readable anywhere on it. */
    --brand-face: linear-gradient(135deg,
        #9FDCF6 0%, #D6F2FD 28%, #7FCBEE 56%, #C2EAFB 80%, #74C2E8 100%);
    --m-ink-brand: #04141F;

    /* --- Reward: gold. The ONLY warm colour in the system. ---------------- */
    --m: linear-gradient(135deg,
        #6E4C14 0%, #C79A3C 12%, #FFF3CB 26%, #E3C073 38%,
        #A87B22 52%, #FFEFB8 66%, #D2A443 80%, #7A5618 100%);
    --m-face: linear-gradient(135deg,
        #F2D79A 0%, #FFF3CB 26%, #E8C878 54%, #FFEFC0 78%, #E2BE72 100%);
    --m-core: #E8C878;
    --m-glow: rgba(226, 190, 110, .5);
    --m-tint: rgba(226, 190, 110, .13);
    --m-ink:  #2A1C04;

    /* Brand wordmark gold — the "Rewards" in the logotype. Pitched to the ring's
       lit node (#ffe6ad → #f0b43c → #b9801f): a bright highlight into deep bronze.
       Shared by AuthLayout and the nav wordmark so the two never drift. */
    --wordmark-gold: linear-gradient(135deg, #FFE6AD 0%, #F0B43C 36%, #B9801F 58%, #F3C255 82%, #B9801F 100%);

    /* --- Ground ----------------------------------------------------------- */
    --bg:      #05070B;
    --surf-a:  #0D121A;
    --surf-b:  #070B11;
    --line:    rgba(255, 255, 255, .065);
    --line-hi: rgba(255, 255, 255, .14);
    --inner-hi: inset 0 1px 0 rgba(255, 255, 255, .075);

    /* Ink. --dim is decorative only; it does not meet AA for body copy. */
    --text:  #EDF2F8;
    --muted: #8493A6;
    --dim:   #5A6577;

    /* --- Semantic --------------------------------------------------------- */
    --reward-success:      #43B98A;
    --reward-success-soft: rgba(67, 185, 138, .14);
    --success-text:        #6FD6AC;
    --warning:             #E0A33C;
    --warning-soft:        rgba(224, 163, 60, .14);
    --danger:              #F08A82;
    --danger-soft:         rgba(232, 115, 107, .15);
    --link:                var(--brand-bright);

    /* --- Named tokens the rest of the sheet and the shop override use ------
       Kept at their Periwinkle names on purpose: every downstream rule and the
       per-shop white-label block in MainLayout already point at these, so the
       system swaps by changing values, not by rewriting every call site. */
    --brand-primary:       var(--brand-mid);
    --brand-primary-hover: var(--brand-bright);
    --brand-primary-soft:  rgba(36, 168, 216, .15);
    --accent-secondary:    var(--brand-deep);

    --bg-base:        var(--bg);
    --surface:        var(--surf-a);
    --surface-subtle: var(--surf-b);
    --border:         var(--line);
    --text-muted:     var(--muted);
    --text-body:      var(--text);

    /* Shape — slightly softer than Periwinkle, per the mobile system. */
    --radius-xs:  8px;
    --radius-sm:  12px;
    --radius-md:  18px;
    --radius-lg:  26px;
    --radius-xl:  32px;
    --radius-pill: 999px;

    /* Depth. Shadows are tinted with the surface hue, never neutral black. */
    --shadow-xs: 0 1px 2px rgba(0, 0, 0, .45);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .5), 0 2px 8px rgba(0, 0, 0, .35);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, .5), 0 10px 24px rgba(0, 0, 0, .45);
    --shadow-lg: 0 6px 14px rgba(0, 0, 0, .55), 0 18px 40px rgba(0, 0, 0, .5);
    --shadow-primary: 0 6px 18px rgba(36, 168, 216, .28);
    --shadow-reward:  0 6px 18px var(--m-glow);

    /* Motion */
    --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
    --dur-fast: 120ms;
    --dur-base: 200ms;

    /* Type */
    --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans",
                 sans-serif, "Apple Color Emoji", "Segoe UI Emoji";

    /* --- Back-compat aliases (older markup references these) --- */
    --brand: var(--brand-primary);
    --brand-strong: var(--brand-primary-hover);
    --brand-violet: var(--accent-secondary);
    --brand-weak: var(--brand-primary-soft);
    --accent: var(--reward-success);
    --accent-strong: var(--success-text);
    --accent-weak: var(--reward-success-soft);
    --surface-2: var(--surface-subtle);
    --radius: var(--radius-md);
    --ring: rgba(61, 200, 245, .35);

    /* Re-point Bootstrap tokens */
    --bs-body-bg: var(--bg-base);
    --bs-body-color: var(--text-body);
    --bs-emphasis-color: var(--text);
    --bs-secondary-color: var(--muted);
    --bs-primary: var(--brand-primary);
    --bs-primary-rgb: 36, 168, 216;
    --bs-success: var(--reward-success);
    --bs-success-rgb: 67, 185, 138;
    /* Bootstrap's own danger/warning reds are tuned for a white page and
       measure about 4.15:1 on this ground - under AA for body text, which is
       exactly the size the ledger deltas render at. Re-pointed, not left to
       inherit. */
    --bs-danger: var(--danger);
    --bs-danger-rgb: 240, 138, 130;
    --bs-warning: var(--warning);
    --bs-warning-rgb: 224, 163, 60;
    --bs-body-bg-rgb: 5, 7, 11;
    --bs-border-color: var(--border);
    --bs-border-radius: var(--radius-sm);
    --bs-border-radius-lg: var(--radius-md);
    --bs-link-color: var(--link);
    --bs-link-hover-color: var(--brand-bright);
}

html, body {
    font-family: var(--font-sans);
    background-color: var(--bg-base);
    color: var(--text-body);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* ---- Typography -------------------------------------------------------- */
h1, .h1 { font-weight: 700; letter-spacing: -0.015em; font-size: clamp(1.6rem, 1.2rem + 1.4vw, 1.9rem); line-height: 1.2; color: var(--text); }
h2, .h2 { font-weight: 600; letter-spacing: -0.01em; line-height: 1.25; color: var(--text); }
h3, .h3, h4, h5, h6 { font-weight: 600; letter-spacing: -0.005em; line-height: 1.35; color: var(--text); }
body { line-height: 1.55; }
.text-muted { color: var(--text-muted) !important; }
/* Explicit, because the ledger renders +/- deltas at 14px where AA needs 4.5:1
   and Bootstrap's defaults do not reach it on this ground. */
.text-danger  { color: var(--danger) !important; }
.text-success { color: var(--success-text) !important; }
.text-warning { color: #F0C987 !important; }
/* Bootstrap's *-emphasis colours are deliberately DARKENED for a white page.
   On this ground text-warning-emphasis measured 2.35:1 - it is used for the
   per-service stamp warning, which is the one sentence on that screen a shop
   owner most needs to read. */
.text-warning-emphasis { color: #F0C987 !important; }
.text-success-emphasis { color: var(--success-text) !important; }
.text-danger-emphasis  { color: var(--danger) !important; }
.text-primary-emphasis { color: var(--brand-bright) !important; }
.text-secondary-emphasis { color: var(--muted) !important; }
.text-body { color: var(--text-body) !important; }
.display-6 { font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }

a, .btn-link { color: var(--link); text-decoration: none; }
a:hover, .btn-link:hover { color: var(--brand-bright); text-decoration: underline; }

/* ---- Buttons -----------------------------------------------------------
   Face variants with dark ink, never a saturated fill with white text:
   white on the brand cyan measures about 2.8:1 and fails outright. Raised
   with an inner top highlight and an inner bottom shadow. */
.btn {
    border-radius: var(--radius-sm);
    font-weight: 600;
    padding: 0.55rem 1.1rem;
    border: 1px solid transparent;
    transition: filter var(--dur-fast) var(--ease-soft),
                box-shadow var(--dur-base) var(--ease-soft),
                transform var(--dur-fast) var(--ease-soft);
}
.btn:active { transform: translateY(1px); }
.btn-lg { padding: 0.72rem 1.4rem; font-size: 1.02rem; border-radius: var(--radius-md); }
.btn-sm { padding: 0.35rem 0.7rem; border-radius: var(--radius-xs); }

/* Themed through Bootstrap's own --bs-btn-* variables rather than by setting
   `color` directly. Setting it directly looked right and then failed on the
   DISABLED state: `.btn:disabled` is specificity (0,2,0) and outranks
   `.btn-primary` (0,1,0), so a disabled primary button kept Bootstrap's white
   ink on our light face and measured 1.17:1. The variables feed every state at
   once - and this avoids both !important and a specificity war, per the
   09_visual_theme.md gotcha. */
.btn-primary {
    --bs-btn-color: var(--m-ink-brand);
    --bs-btn-hover-color: var(--m-ink-brand);
    --bs-btn-active-color: var(--m-ink-brand);
    --bs-btn-disabled-color: var(--m-ink-brand);
    --bs-btn-disabled-bg: transparent;
    --bs-btn-disabled-border-color: rgba(255, 255, 255, .18);
    background: var(--brand-face);
    border-color: rgba(255, 255, 255, .18);
    box-shadow: var(--inner-hi), inset 0 -1px 0 rgba(0, 0, 0, .18), var(--shadow-primary);
    text-shadow: 0 1px 0 rgba(255, 255, 255, .42);
}
.btn-primary:hover, .btn-primary:focus-visible { filter: brightness(1.07); }

/* Gold button = "there is a reward to hand over". Nothing else. */
.btn-reward, .btn-warning {
    --bs-btn-color: var(--m-ink);
    --bs-btn-hover-color: var(--m-ink);
    --bs-btn-active-color: var(--m-ink);
    --bs-btn-disabled-color: var(--m-ink);
    --bs-btn-disabled-bg: transparent;
    background: var(--m-face);
    border-color: rgba(255, 255, 255, .22);
    box-shadow: var(--inner-hi), inset 0 -1px 0 rgba(0, 0, 0, .2), var(--shadow-reward);
    text-shadow: 0 1px 0 rgba(255, 255, 255, .42);
}
.btn-reward:hover, .btn-warning:hover { filter: brightness(1.06); }

.btn-success {
    --bs-btn-color: #04150F; --bs-btn-hover-color: #04150F;
    --bs-btn-active-color: #04150F; --bs-btn-disabled-color: #04150F;
    background: var(--reward-success); border-color: var(--reward-success); font-weight: 700;
}
.btn-success:hover { filter: brightness(1.08); }
.btn-danger {
    --bs-btn-color: #1B0806; --bs-btn-hover-color: #1B0806;
    --bs-btn-active-color: #1B0806; --bs-btn-disabled-color: #1B0806;
    background: var(--danger); border-color: var(--danger); font-weight: 700;
}

.btn-light { background: var(--surf-a); border-color: var(--line-hi); color: var(--text); }
.btn-light:hover { background: var(--surf-b); color: var(--text); }

.btn-outline-primary { color: var(--brand-bright); border-color: rgba(61, 200, 245, .5); background: transparent; }
.btn-outline-primary:hover { background: rgba(36, 168, 216, .16); border-color: var(--brand-bright); color: var(--brand-bright); }
.btn-outline-light { color: var(--text); border-color: var(--line-hi); background: transparent; }
.btn-outline-light:hover { background: rgba(255, 255, 255, .1); border-color: var(--text); color: var(--text); }
.btn-outline-success { color: var(--success-text); border-color: rgba(67, 185, 138, .5); background: transparent; }
.btn-outline-success:hover { background: var(--reward-success-soft); border-color: var(--success-text); color: var(--success-text); }
.btn-outline-secondary { color: var(--muted); border-color: var(--line-hi); background: transparent; }
.btn-outline-secondary:hover { background: rgba(255, 255, 255, .06); border-color: var(--text); color: var(--text); }
.btn-outline-danger { color: var(--danger); border-color: rgba(232, 115, 107, .5); background: transparent; }
.btn-outline-danger:hover { background: var(--danger-soft); color: var(--danger); }

.btn:disabled, .btn.disabled { opacity: .45; box-shadow: none; }

/* ---- Cards -------------------------------------------------------------
   Gradient surface, lighter top to darker bottom, with a 1px inner top
   highlight. Flat fills read as paper; these read as a lit object. */
.card {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: linear-gradient(180deg, var(--surf-a) 0%, var(--surf-b) 100%);
    color: var(--text);
    box-shadow: var(--inner-hi), var(--shadow-sm);
}
.card-body { padding: 1.5rem; }
.card-title { font-weight: 600; color: var(--text); }
.shadow-sm { box-shadow: var(--inner-hi), var(--shadow-sm) !important; }

/* ---- Forms ------------------------------------------------------------- */
.form-control, .form-select {
    border-radius: var(--radius-sm);
    border: 1px solid var(--line-hi);
    padding: 0.55rem 0.8rem;
    background-color: rgba(0, 0, 0, .3);
    color: var(--text);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, .55);
    transition: border-color var(--dur-fast) var(--ease-soft), box-shadow var(--dur-fast) var(--ease-soft);
}
.form-control-lg { padding: 0.7rem 0.9rem; font-size: 1.05rem; }
.form-control-sm, .form-select-sm { border-radius: var(--radius-xs); }
.form-control:focus, .form-select:focus {
    background-color: rgba(0, 0, 0, .3);
    border-color: var(--brand-bright);
    color: var(--text);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, .55), 0 0 0 4px var(--ring);
}
.form-control::placeholder { color: var(--dim); }

/* Disabled / read-only fields. Bootstrap's own state paints them light gray (#e9ecef) — a white bar on
   the dark ground (Robert, 2026-11-07: the locked email on Check-In). Here a locked field reads as
   "recessed and quiet": flatter, dimmer text, no inset well, not-allowed cursor. */
.form-control:disabled, .form-control[readonly], .form-select:disabled {
    background-color: rgba(255, 255, 255, .03);
    border-color: var(--line);
    color: var(--muted);
    box-shadow: none;
    cursor: not-allowed;
    opacity: 1;
}

/* Browser autofill would otherwise flood the field pale yellow/blue. Paint over it with the field's
   own dark fill (the inset-shadow trick is the only thing autofill honours). */
.form-control:-webkit-autofill,
.form-control:-webkit-autofill:hover,
.form-control:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--text);
    caret-color: var(--text);
    -webkit-box-shadow: 0 0 0 1000px #0a0f15 inset;
    transition: background-color 9999s ease-out 0s;
}

/* Dropdown option lists render on the OS's light popup in some browsers; keep them dark. */
.form-select option { background-color: #0b1118; color: var(--text); }
.form-label { font-weight: 500; font-size: 0.9rem; color: var(--text); margin-bottom: 0.35rem; }
.form-text { color: var(--muted); }
.form-check-input { background-color: rgba(0, 0, 0, .35); border-color: var(--line-hi); }
.form-check-input:checked { background-color: var(--brand-mid); border-color: var(--brand-mid); }
.form-check-label { color: var(--text); }
.input-group-text { background: var(--surf-b); border-color: var(--line-hi); color: var(--muted); border-radius: var(--radius-sm); }

/* ---- Alerts (tinted, dark) --------------------------------------------- */
.alert { border-radius: var(--radius-sm); border: 1px solid transparent; padding: 0.85rem 1.05rem; }
.alert-success { background: var(--reward-success-soft); color: var(--success-text); border-color: rgba(67, 185, 138, .35); }
.alert-danger  { background: var(--danger-soft); color: #F3A49E; border-color: rgba(232, 115, 107, .35); }
.alert-info    { background: var(--brand-primary-soft); color: #9BDDF5; border-color: rgba(36, 168, 216, .35); }
.alert-warning { background: var(--warning-soft); color: #F0C987; border-color: rgba(224, 163, 60, .35); }

/* "A reward is waiting" — the one place gold is allowed on the web, matching
   the mobile system's single meaning. Face variant: it carries text. */
.alert-reward {
    background: var(--m-face);
    color: var(--m-ink);
    border-color: rgba(255, 255, 255, .25);
    box-shadow: var(--inner-hi), var(--shadow-reward);
    text-shadow: 0 1px 0 rgba(255, 255, 255, .42);
    font-weight: 600;
}
.alert-reward a, .alert-reward .alert-link { color: #5A3B06; text-decoration: underline; }

/* ---- Badges ------------------------------------------------------------ */
.badge { font-weight: 700; border-radius: var(--radius-pill); padding: 0.38em 0.72em; }
.text-bg-success   { background-color: var(--reward-success) !important; color: #04150F !important; }
.text-bg-primary   { background-color: var(--brand-mid) !important; color: var(--m-ink-brand) !important; }
.text-bg-warning   { background-color: var(--warning) !important; color: #1F1402 !important; }
.text-bg-danger    { background-color: var(--danger) !important; color: #1B0806 !important; }
.text-bg-secondary { background-color: rgba(255, 255, 255, .1) !important; color: var(--text) !important; }

/* ---- Tables ------------------------------------------------------------ */
.table { --bs-table-bg: transparent; --bs-table-color: var(--text); margin-bottom: 0; color: var(--text); }
.table > thead th {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--muted); font-weight: 700; border-bottom: 1px solid var(--line-hi);
}
.table > :not(caption) > * > * { padding: 0.65rem 0.5rem; background-color: transparent; color: var(--text); }
.table td { border-color: var(--line); vertical-align: middle; }
.table-hover > tbody > tr:hover > * { background-color: rgba(255, 255, 255, .04); color: var(--text); }
.list-group-item { background: transparent; border-color: var(--line); color: var(--text); }

/* ==========================================================================
   App-specific components
   ========================================================================== */

/* Stamp card — recessed holes, so a filled stamp reads as something
   physically dropped in rather than a coloured circle. */
.stamp-grid { display: flex; flex-wrap: wrap; gap: 0.55rem; }
.stamp-dot {
    width: 2.85rem; height: 2.85rem; border-radius: var(--radius-pill);
    display: grid; place-items: center; font-size: 1.15rem; font-weight: 700;
    border: 1px solid rgba(255, 255, 255, .08);
    color: var(--dim);
    background: var(--surf-b);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, .95);
    transition: transform var(--dur-base) var(--ease-soft);
}
/* Filled = earned. FACE not full band: a glyph sits on it, and banded metal
   would drop that glyph onto a dark stop. */
.stamp-dot.is-filled {
    border: 1px solid rgba(255, 255, 255, .25);
    color: var(--m-ink);
    background: var(--m-face);
    box-shadow: var(--inner-hi), 0 2px 8px var(--m-glow);
    text-shadow: 0 1px 0 rgba(255, 255, 255, .42);
}

/* Member code chip */
.member-code {
    font-family: ui-monospace, "SF Mono", "Segoe UI Mono", "Roboto Mono", monospace;
    font-size: 2rem; font-weight: 700; letter-spacing: 0.28rem;
    color: var(--text); font-variant-numeric: tabular-nums;
}

/* Hero surface for the shop/reward summary. Brushed-satin brand metal; no
   text sits directly on the banding because the inner content carries its
   own face treatment. */
/* Deep brand gradient, NOT --brand-metal. The first attempt used full banded
   metal here and the eyebrow, tagline and muted copy measured 2.2-2.8:1 on its
   light stops - the "banding needs area" rule catching me out on my own sheet.
   Every stop here stays dark, so white ink is readable across the whole
   surface, and the brand hue still carries the material language. */
.reward-hero {
    background: linear-gradient(135deg,
        #0E3A5E 0%, #155A8C 22%, #0F4572 44%, #1A6DA6 66%, #113F67 86%, #0B2B45 100%);
    color: #FFFFFF;
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: var(--radius-lg);
    box-shadow: var(--inner-hi), var(--shadow-md);
}
/* Every descendant, not just the ones that happened to be checked: muted and
   eyebrow tokens are tuned for the app ground, not for this surface. */
.reward-hero .text-muted,
.reward-hero .member-code,
.reward-hero .eyebrow,
.reward-hero p,
.reward-hero small { color: rgba(255, 255, 255, 0.92) !important; }
.reward-hero h1, .reward-hero h2, .reward-hero h3,
.reward-hero h4, .reward-hero h5 { color: #FFFFFF; text-shadow: 0 1px 2px rgba(0, 0, 0, .55); }

/* Special / promotion cards */
.special-card { position: relative; overflow: hidden; height: 100%; }
.special-card .special-highlight {
    display: inline-block; font-weight: 700; font-size: 0.8rem; letter-spacing: 0.01em;
    color: var(--success-text); background: var(--reward-success-soft);
    padding: 0.25rem 0.6rem; border-radius: var(--radius-pill);
}
.special-card.is-expired { opacity: 0.55; }

/* Section eyebrow label */
.eyebrow { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 700; color: var(--muted); }

/* ==========================================================================
   Responsive — phone and tablet
   ========================================================================== */

/* Tablet: the sidebar is present but content needs room to breathe. */
@media (min-width: 768px) and (max-width: 1199.98px) {
    .card-body { padding: 1.15rem; }
    .member-code { font-size: 1.6rem; letter-spacing: 0.2rem; }
    .stamp-dot { width: 2.5rem; height: 2.5rem; font-size: 1.05rem; }
}

/* Mobile bottom tab bar (shown only < 768px; the sidebar takes over above). */
.bottom-tabs { display: none; }

@media (max-width: 767.98px) {
    /* The off-screen slide-in drawer must never add horizontal scroll. */
    html, body { overflow-x: clip; }

    .card-body { padding: 1.05rem; }
    .member-code { font-size: 1.5rem; letter-spacing: 0.18rem; }

    /* Stamp dots must stay tappable and must not wrap raggedly on a narrow
       phone: a fixed rem size overflows at 320px. */
    .stamp-dot { width: 2.45rem; height: 2.45rem; font-size: 1rem; }
    .stamp-grid { gap: 0.45rem; }

    .bottom-tabs {
        display: flex;
        position: fixed;
        bottom: 0; left: 0; right: 0;
        height: 4rem;
        background: linear-gradient(180deg, var(--surf-a) 0%, var(--surf-b) 100%);
        border-top: 1px solid var(--line-hi);
        box-shadow: 0 -2px 16px rgba(0, 0, 0, .6);
        z-index: 1030;
        /* Clear of the iOS home indicator. */
        padding-bottom: env(safe-area-inset-bottom, 0);
    }
    .bottom-tab {
        flex: 1;
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        gap: 2px;
        font-size: 0.68rem; font-weight: 600;
        color: var(--muted); text-decoration: none;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        min-height: 44px;
    }
    .bottom-tab:hover, .bottom-tab:focus { text-decoration: none; color: var(--text); }
    .bottom-tab.active { color: var(--brand-bright); }
    .bt-ico { font-size: 1.3rem; line-height: 1; }
}

/* ==========================================================================
   Focus, motion, framework leftovers
   ========================================================================== */
:focus-visible {
    outline: 2px solid var(--brand-bright);
    outline-offset: 2px;
    box-shadow: 0 0 0 4px var(--ring);
    border-radius: inherit;
}
:focus:not(:focus-visible) { outline: none; }
.btn:focus, .btn:active:focus, .form-control:focus, .form-check-input:focus { box-shadow: 0 0 0 4px var(--ring); }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

.content { padding-top: 0.5rem; }
h1:focus { outline: none; }
.valid.modified:not([type=checkbox]) { outline: none; }
.invalid { outline: 1px solid var(--danger); border-radius: var(--radius-sm); }
.validation-message { color: #F3A49E; font-size: 0.85rem; }
.darker-border-checkbox.form-check-input { border-color: var(--line-hi); }
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { color: var(--muted); text-align: end; }
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { text-align: start; }

.blazor-error-boundary { background: var(--danger); padding: 1rem; color: #1B0806; border-radius: var(--radius-sm); }
.blazor-error-boundary::after { content: "An error has occurred." }
#blazor-error-ui {
    color-scheme: dark only; background: #2A1F06; color: #F0C987; bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.6); box-sizing: border-box; display: none;
    left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem; position: fixed; width: 100%; z-index: 1000;
}
#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; }
/* The error bar is hidden until something breaks, which is exactly why it gets
   missed: its link and dismiss inherited colours that measure under 2:1 on the
   dark bar. It is only ever read by someone already having a bad time. */
#blazor-error-ui a, #blazor-error-ui .reload { color: #FFE3A3; text-decoration: underline; }
#blazor-error-ui .dismiss { color: #F0C987; }
