/*
    UI THEME TOKENS. One switch, every themed surface: buttons, the bottom-nav
    FAB, and the header.

    ─────────────────────────────────────────────────────────────────────────────
    THE SWITCH IS NOT IN THIS FILE. It is one C# constant:

        SkillMatch.Shared.Client/UiTheme.cs  →  UiTheme.Active

    Set it to Capsule to work on the new theme, Classic to go back. The host
    renders it as <html data-ui="…"> and the cascade below does the rest.

    ─────────────────────────────────────────────────────────────────────────────
    HOW THIS IS LAYERED

    Two layers, and the split is the point:

      THIS FILE   decides what a surface LOOKS like. Values only, no selectors
                  beyond :root. A theme is a complete set of these tokens.
      COMPONENTS  decide what a surface IS — structure, states, specificity, the
                  hover-mirrors-focus rule. buttons.css, AppHeader.razor.css and
                  MainLayout.razor.css read tokens and hardcode nothing a theme is
                  allowed to change, so none of them is edited to switch.

    Adding a theme therefore means adding one block here, not hunting through the
    component layer for literals. Adding a SURFACE to the theme means adding its
    tokens to BOTH blocks — a token defined in only one silently falls back to the
    other theme's value, which is the half-state this structure exists to prevent.

    CLASSIC IS ON BARE :root, ON PURPOSE. It is the fallback when no attribute is
    present at all — a host that forgets to render data-ui gets the shipped look
    rather than an untested half-state. Capsule is the opt-in.

    WHY THE SHADOWS ARE SPELT OUT PER VARIANT. The obvious compression is one
    --btn-shadow-rest that reads a --btn-glow set on each button. It does not
    work: a custom property containing var() is substituted where it is DECLARED,
    so a token defined on :root resolves --btn-glow against :root (undefined),
    not against the button. Declared here per variant, every var() resolves on
    :root where the colour tokens live — including the dark-mode ones, because
    [data-theme="dark"] is on :root too, so these follow the theme for free.
*/

/* ═══════════════════════════════════════════════════════════════════════════
   CLASSIC — the look that shipped. Values are lifted verbatim from the
   pre-2026-09-06 buttons.css so this is the original, not a reconstruction.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
    /* ── RADIUS SCALE — THE DESIGN LANGUAGE ────────────────────────────────
       Radius encodes WHAT A THING HOLDS, not how big it is. Five roles, and
       every rounded surface in the app maps onto one of them:

         --r-pill    holds a short label     buttons, chips, badges, fields, tabs
         --r-tag     an inline tag in a card the "Besok" / "Baru" markers
         --r-thumb   an image or square avatar
         --r-card    a content container     cards, panels
         --r-card-sm a compact notice        banners, inline callouts
         --r-panel   the largest surfaces    header card, sheets
         --r-circle  inherently circular     round avatars, icon buttons, dots

       Pick by asking what the box contains, never by eyeballing a number. That
       is what stops a new component inventing a seventh radius.

       Classic's values are today's literals, drift and all: it has a 14px
       banner next to a 16px card, which is exactly the inconsistency this scale
       exists to retire. Capsule regularises them.

       --r-pill and --r-circle are identical in both themes and are still
       declared in both, so "a theme is a complete token set" stays true and a
       missing token can never silently fall through to the other theme. */
    --r-pill: 999px;
    --r-tag: 6px;
    --r-thumb: 16px;
    --r-card: 16px;
    --r-card-sm: 14px;
    --r-panel: 20px;
    --r-circle: 50%;

    /* Brand surface — a filled brand block that is NOT a button (the Beranda
       notification banner). Classic uses the same 135deg diagonal the buttons
       used to. */
    --brand-fill: linear-gradient(135deg, var(--btn-primary-start), var(--btn-primary-end));
    --brand-shadow: 0 4px 12px var(--btn-primary-shadow);

    /* ── BOTTOM NAV ────────────────────────────────────────────────────────
       Classic is an in-flow bar welded to the screen edge: it reserves its own
       height in the flex column, so nothing scrolls behind it and
       --scroll-pad-bottom is 0.

       --nav-position and --scroll-pad-bottom move together and must stay in
       step. The moment the bar leaves the flow, it stops reserving height and
       the last card would sit underneath it, so the scroll area has to make that
       space back. Changing one without the other is the bug this pairing
       prevents.

       relative, NOT static, and the difference is not cosmetic. The bar's
       surface is an inset:0 ::before (see MainLayout.razor.css), and an
       absolutely positioned pseudo-element resolves inset against its nearest
       POSITIONED ancestor. On a static bar that ancestor is .app-page, so the
       surface stopped being a bar and became an opaque sheet over the entire
       viewport: every page rendered, laid out and stayed hit-testable while
       painting nothing at all, because --nav-fill is the same solid white as
       the page ground. It cost a whole day on Android, which is where classic
       actually renders. A relative bar with no offsets occupies exactly the
       same box a static one did and stays in the flow, and it is also what
       makes the z-index: 9100 below take effect at all (z-index is ignored on
       a static box), which is the layer the rest of the scale already assumes:
       sheets sit at 9200, the camera at 9400, BackToTop at 9050 UNDER the bar.
       Capsule never had the bug because its bar is already absolute. */
    --nav-position: relative;
    --nav-inset: auto;
    --nav-radius: 0;
    --nav-fill: var(--bg-white);
    /* Classic stays opaque: it is a full-bleed bar with a real top border, so it
       already answers the edge that the floating capsule nav had to solve. */
    --nav-backdrop: none;
    --nav-border-top: 1px solid var(--border);
    --nav-edge: 0 -1px 2px rgba(13,33,55,0.04), 0 -2px 4px rgba(13,33,55,0.03), 0 -4px 8px rgba(13,33,55,0.03);
    --nav-height-box: calc(var(--nav-height) + var(--safe-area-bottom-fixed));
    --nav-pad-bottom: var(--safe-area-bottom-fixed);
    --nav-pad-x: max(0px, calc((100% - 848px) / 2));
    /* ZERO, and it has to be. An auto inline margin on a flex item cancels the
       cross-axis stretch, so the in-flow bar stops spanning the window and
       shrink-wraps to its five items: measured at 390px wide it came out 229px,
       centred, with the labels colliding. Only a bar that has left the flow and
       taken a max-width needs auto here. */
    --nav-margin-inline: 0;
    --scroll-pad-bottom: 0px;
    /* How far up anything anchored above the nav must sit: slide-up sheets and
       the BackToTop button. Equals the bar's own height while it is flush to the
       screen edge. */
    --nav-clearance: calc(var(--nav-height) + var(--safe-area-bottom-fixed));
    /* Selected destination: a 3px bar hugging the top edge of the bar. */
    --nav-ind-inset: 0 25% auto 25%;
    --nav-ind-height: 3px;
    --nav-ind-radius: 0 0 3px 3px;
    --nav-ind-bg: var(--blue-primary);
    /* Classic's bar is already sized by its 25% insets. */
    --nav-ind-max: none;
    /* Interaction (hover / focus / press). Separate from the selection indicator
       above because the two themes disagree about whether they are the same
       shape: classic tints the whole slot square while selection is a 3px bar,
       capsule uses one pill for both. */
    --nav-hover-inset: 0;
    --nav-hover-radius: 0;
    --nav-hover-max: none;
    --nav-hover-bg: var(--blue-pale);
    /* Pills sit ON the bar here: classic's surface is opaque and unblurred, so
       there is nothing to sit behind. */
    --nav-state-z: 0;

    /* Clickable card (.card-clickable — the Beranda mitra cards). */
    --card-hover-transform: translateY(-1px);
    --card-hover-shadow: 0 4px 16px rgba(13,33,55,.08), 0 0 0 1.5px var(--blue-pale);
    --card-press-transform: scale(0.98);

    /* Selectable pill (.cat-pill and friends). */
    --pill-hover-shadow: none;
    --pill-active-shadow: 0 2px 8px var(--tab-active-shadow);

    /* ── SELECTABLE ROW, AND ITS ITEMS ──────────────────────────────────────
       Two surfaces that used to be one. The ROW is the container around a set
       of mutually related choices (the Beranda category pills, the Aktivitas
       filter chips); the ITEM is one choice inside it.

       Classic has no row container at all: every item draws its own 1.5px
       border and the row is a bare horizontal scroller. So every --track-*
       token here is a deliberate no-op, and the row's geometry stays exactly
       where it has always been, on the scroller.

       SOME TOKENS ARE MISSING FROM THIS BLOCK ON PURPOSE, which is the one
       place this file bends its own "define in both blocks" rule:

           --track-margin   --track-pad   --track-scroll-pad   --track-gap
           --chip-radius    --tag-radius   --card-radius
           --card-hover-lift               --card-hover-edge
           --field-radius   --field-radius-block   --tonal-fill
           --field-prefix-fill    --field-prefix-radius
           --search-hdr-pad
           --profile-card-top    --profile-avatar-pad-bottom
           --lb-pad         --lb-option-radius
           --cart-radius    --cart-press-transform
           --cell-radius    --cell-on-fill  --cell-on-ink  --cell-on-border-color
           --cell-on-shadow --cell-focus-shadow   --opt-hover-bg
           --calday-width   --calday-radius --calday-today-cue
           --send-fill      --send-shadow-rest   --send-shadow-hover
           --send-shadow-press   --send-transform-hover  --send-transform-press
           --btt-fill       --btt-border-color   --btt-shadow
           --btt-on-fill    --btt-on-border-color --btt-press-transform
           --chat-hdr-radius     --chat-hdr-edge
           --tile-radius    --icon-btn-radius
           --step-rail-margin    --step-rail-fill   --step-rail-radius
           --step-rail-fade      --step-rail-overflow
           --step-dot-radius     --step-dot-fill    --step-dot-shadow
           --step-dot-ring       --step-dot-pending-fill
           --step-dot-pending-edge

       Their classic value is PAGE-SPECIFIC (.cat-scroll is 14px 16px 6px with
       an 8px gap, .filter-scroll is 12px 16px 0 with 6px, and the Wallet and
       ReferralEarnings wraps carry their own margins), so a single shared
       value cannot carry both. Declaring them here would force one page to the
       other's spacing. Instead each component writes its own classic value as
       the var() fallback and only capsule defines the token, so classic
       resolves to the literal that was already there. The failure this file
       normally guards against, a token leaking the other theme's value, cannot
       happen here because the fallback IS the classic value and it lives three
       lines from the property it feeds. */
    --track-fill: transparent;
    --track-edge: none;
    --track-radius: 0;
    /* What .scroll-fade-wrap fades its edges TO. Must match whatever is behind
       the scrolling items, which in classic is the page. */
    --track-fade: var(--bg-page);
    --track-overflow: visible;

    /* Item, unselected. --pill-hover-bg repeats --pill-fill rather than saying
       "transparent": the hover cue must not change the fill in classic, and a
       shorthand that means "leave it alone" does not exist for a background. */
    --pill-fill: var(--bg-white);
    --pill-border-color: var(--border);
    --pill-hover-border-color: var(--blue-primary);
    --pill-hover-bg: var(--bg-white);
    /* Item, selected. */
    --pill-on-fill: var(--tab-active-bg);
    --pill-on-border-color: var(--tab-active-bg);

    /* SELECTABLE CELL: a date, time slot or calendar day. Same idea as a chip,
       but it sits loose on a card rather than inside a trough, so it cannot go
       fully transparent at rest or it would lose all affordance. Classic keeps
       the white fill and grey outline it has always had.

       --cell-on-shadow is NOT declared here, and that is the same page-specific
       reason --chip-radius is not. Most selected cells carry no shadow in
       classic, but three carry a real one (Payment's .pm-opt.selected, Jasaku's
       and Search's selected rows), so a single value here would have to pick a
       side: `none` silently deleted their shadow, which is exactly the drift
       this file exists to prevent. It is capsule-only instead, and every
       consumer states its own classic value as the var() fallback. */
    --cell-fill: var(--bg-white);
    --cell-border-color: var(--border);

    /* FORM FIELD. The box is drawn by a WRAPPER in every case (.input-wrapper,
       .field-wrap, .sec-input-wrap and eleven more); the input inside is
       transparent and carries no theme. Classic is the white box with a grey
       outline that all fourteen of them already draw, so this block is a
       restatement, not a change.

       --field-radius and --field-radius-block are capsule-only for the reason
       the --chip-radius note gives: the classic literal differs per page (10px,
       12px, --radius-sm, --radius-xs), so each component keeps its own as the
       var() fallback and only capsule states one value.

       --field-footer-* is the character-counter strip under a textarea. It is a
       separate pair because it sits INSIDE the field box, so it follows the
       field's fill rather than the page's. */
    --field-fill: var(--bg-white);
    --field-border-color: var(--border);
    --field-focus-fill: var(--bg-white);
    --field-focus-border-color: var(--blue-primary);
    --field-focus-shadow: 0 0 0 3px rgba(21,101,192,0.1);
    --field-footer-fill: var(--bg-page);
    --field-footer-edge: 1px solid var(--border);

    /* OUTLINED CARD. The second kind of card in this app: one that separates
       itself with a drawn grey edge instead of the shadow --shadow-card
       carries. Because it never read --shadow-card, repointing that token
       reached every floating card and none of these, which is how a page like
       Detail ended up with fourteen flat outlined sections under a floating
       header.

       Classic restates what they already draw: the edge on, no shadow. */
    --card-edge-color: var(--border);
    --card-edge-shadow: none;

    /* CHAT. The one screen that does not use AppHeader: it draws its own flat
       white bar welded to the top of the window, with the conversation on a
       white page under it. Classic restates that. --chat-hdr-radius and
       --chat-hdr-edge are capsule-only (each keeps its literal as the fallback),
       the two that are declared here are not, because classic needs real values
       for both halves of the inset-versus-padding pair. */
    --chat-page-fill: var(--bg-white);
    --chat-hdr-inset: 0;
    --chat-hdr-pad-top: max(40px, var(--safe-area-top) + 12px);

    /* BACK-TO-TOP. Every --btt-* token is capsule-only, and this is the third
       reason that pattern gets used: classic's values differ per THEME, not per
       page. The component carries a light rule and a [data-theme="dark"] rule
       with different shadow stacks and a different hairline opacity, so a token
       declared here would hand one of the two the other's value. Each rule keeps
       its own literal as the var() fallback instead, and classic stays exact in
       both themes. */

    /* ── CARD ───────────────────────────────────────────────────────────────
       --shadow-card is a ROLE token in theme.css and the whole app already
       reads it, so a theme repoints it rather than adding a parallel one that
       36 stylesheets would have to be taught about. Classic points it at the
       hairline stack it has always been.

       --card-hover-shadow-base is --card-hover-shadow without its ring, for the
       two cards that deliberately use a stronger ring than --blue-pale (the
       selected service in Detail and Jasaku). They were hand-copied literals
       and so ignored the theme entirely; this lets them follow its elevation
       while keeping the difference that was intended. */
    --shadow-card: var(--shadow-hairline);
    --card-hover-shadow-base: 0 4px 16px rgba(13,33,55,.08);

    /* Availability strip down the left of a mitra card. Classic welds it to the
       card's edge, full height, rounded on the inner side only. */
    --strip-inset: 0 auto 0 0;
    --strip-radius: 0 4px 4px 0;
    /* Extra left inset a card needs once the strip stops being welded to its
       edge. Zero while the strip IS the edge. */
    --strip-gutter: 0px;

    /* The tag inside a card ("Hari Ini" / "Besok"). */
    --tag-fill: var(--blue-pale);
    /* The lighting layer a tinted status badge (Admin and Agent .badge) paints
       over its own colour. Classic badges are flat. */
    --badge-lit: none;

    /* MONOGRAM AVATAR. The generated fill behind a user's initials, and the
       fixed ones behind a system sender. Unlike every other surface here the
       fill string is composed in C# (Services/AvatarFill.cs), written into a
       style attribute at 25 call sites and stored per user and per notification
       row, so the theme cannot restyle it by class. Instead the string reads
       these two tokens with the classic value as its var() fallback, and the
       theme decides only the lighting: which way the two stops run, and whether
       the sheen every filled control wears is laid over them. Classic is the
       flat 135deg diagonal it always had. */
    --avatar-angle: 135deg;
    --avatar-sheen: none;

    /* Geometry */
    --btn-radius-lg: 10px;
    --btn-radius-md: 8px;
    --btn-radius-sm: 6px;
    --btn-radius-icon: 8px;
    --btn-radius-social: 10px;
    --btn-pad-lg: 12px;
    --btn-pad-md: 8px 12px;
    --btn-pad-sm: 6px 10px;

    /* Fills */
    --btn-fill-primary: linear-gradient(135deg, var(--btn-primary-start), var(--btn-primary-end));
    --btn-fill-success: linear-gradient(135deg, var(--btn-success-start), var(--btn-success-end));
    --btn-fill-danger:  linear-gradient(135deg, var(--btn-danger-start), var(--btn-danger-end));

    /* Depth. Classic's press is a shadow no-op: it only cancels the hover lift,
       which is why rest and press are the same value here. */
    --btn-shadow-primary-rest:  0 4px 14px var(--btn-primary-shadow);
    --btn-shadow-primary-hover: 0 6px 22px var(--btn-primary-shadow);
    --btn-shadow-primary-press: 0 4px 14px var(--btn-primary-shadow);
    --btn-shadow-success-rest:  0 4px 14px var(--btn-success-shadow);
    --btn-shadow-success-hover: 0 6px 22px var(--btn-success-shadow);
    --btn-shadow-success-press: 0 4px 14px var(--btn-success-shadow);
    --btn-shadow-danger-rest:   0 4px 14px var(--btn-danger-shadow);
    --btn-shadow-danger-hover:  0 6px 22px var(--btn-danger-shadow);
    --btn-shadow-danger-press:  0 4px 14px var(--btn-danger-shadow);

    /* Motion */
    --btn-transform-hover: translateY(-1px);
    --btn-transform-press: translateY(0);

    /* Bottom-nav FAB. Not a .btn, but it is a filled brand control and belongs
       to the same theme, so it is themed here rather than left behind. */
    --fab-fill: linear-gradient(135deg, var(--btn-primary-start), var(--btn-primary-end));
    --fab-shadow-rest:  0 0 0 4px var(--btn-primary-shadow), 0 4px 16px var(--btn-primary-shadow);
    --fab-shadow-hover: 0 0 0 4px var(--btn-primary-shadow), 0 4px 16px var(--btn-primary-shadow), 0 0 0 6px rgba(var(--blue-primary-rgb), 0.45);
    --fab-shadow-press: 0 0 0 4px var(--btn-primary-shadow), 0 4px 16px var(--btn-primary-shadow);
    --fab-transform-hover: none;
    --fab-transform-press: scale(0.95);

    /* ── HEADER (AppHeader, 58 pages) ──────────────────────────────────────
       Classic: a full-bleed band with a 145deg three-stop gradient, an ornament
       circle bleeding off the top-right, and 12px rounded-square controls.

       --header-inset is the OUTER offset (margin) and --header-pad-top the inner
       one. Classic puts the safe-area inset inside its own padding because the
       band runs to the top of the screen; a floating header has to put it
       outside instead, which is why the two are separate tokens rather than one.

       --header-edge covers a display-zoom anti-aliasing seam: at 1.1x/1.4x the
       band's bottom edge lands on a non-integer Y pixel and Chromium feathers it
       into a pale hairline, so a 1px page-coloured shim is painted under it. That
       trick only works on a straight edge under an opaque band, which is why it
       is themed rather than hardcoded. */
    --header-fill: linear-gradient(145deg, var(--header-grad-start) 0%, var(--header-grad-mid) 65%, var(--header-grad-end) 100%);
    --header-ink: #fff;
    --header-blur: none;
    --header-radius: 0;
    --header-inset: 0;
    /* Classic is a full-bleed band: it spans the viewport and only its INNER nav
       row caps to the 848 content column. */
    --header-width: auto;
    --header-max: none;
    --header-pad-top: max(40px, var(--safe-area-top) + 12px);
    --header-pad-x: 16px;
    --header-pad-bottom: 12px;
    --header-edge: 0 1px 0 0 var(--bg-page);
    --header-ornament: block;
    --header-control-radius: 12px;
    --header-control-bg: rgba(255, 255, 255, 0.12);
    /* Fields a page drops into the header's extra slot — the Beranda and Lowongan
       search bars are the only two. */
    --header-field-radius: var(--radius-sm);

    /* ── TOGGLE (Components/ToggleSwitch) ─────────────────────────────────────
       Classic here is a faithful copy of what the five hand-written toggles did
       before they were merged, so switching back to this theme gives back exactly
       the old control: a 48x28 track, a flat grey off state, a flat brand on state,
       and a focus ring that thickens on press.

       Two values look odd until you know why. --toggle-off-edge is a transparent
       zero shadow rather than `none`, because the cue rules stack it in front of the
       ring (`var(--toggle-off-edge), var(--toggle-cue)`) and `none` in a shadow list
       is a parse error that would silently kill the whole cue. --toggle-press-scale
       is 1 because classic never had a press movement; the ring is its press. */
    --toggle-w: 48px;
    --toggle-h: 28px;
    --toggle-thumb-size: 22px;
    --toggle-inset: 3px;
    --toggle-travel: 20px;
    --toggle-radius: 14px;
    --toggle-off-fill: var(--border);
    --toggle-off-edge: 0 0 0 0 transparent;
    --toggle-on-fill: var(--tab-active-bg);
    --toggle-on-edge: 0 0 0 0 transparent;
    --toggle-thumb-fill: #fff;
    --toggle-thumb-shadow: 0 1px 3px rgba(0,0,0,0.15);
    --toggle-thumb-shadow-press: 0 1px 3px rgba(0,0,0,0.15);
    --toggle-press-scale: 1;
    --toggle-cue: 0 0 0 3px rgba(var(--blue-primary-rgb), 0.18);
    --toggle-cue-press: 0 0 0 4px rgba(var(--blue-primary-rgb), 0.28);
    /* No state word: classic's track is 48px and has no room for one. */
    --toggle-word-display: none;
    --toggle-word-on-color: #fff;
    --toggle-word-off-color: var(--text-muted);

    /* ── SLIDER (Components/RangeSlider) ──────────────────────────────────────
       Classic is the Jasaku radius slider verbatim: a 6px rail, a flat brand fill,
       and a 28px white knob ringed in blue whose only cue is a hard focus ring that
       thickens. Settings' scale slider used to differ in five ways, including having
       no fill at all; it now takes these values, so the two finally agree.

       --slider-track-edge and --slider-fill-edge are transparent ZERO shadows rather
       than `none`, because a shadow list containing `none` is a parse error and the
       component stacks these in front of the cue. Same trap as --toggle-off-edge. */
    --slider-h: 6px;
    --slider-knob: 28px;
    --slider-radius: 3px;
    --slider-track-fill: var(--border);
    --slider-track-edge: 0 0 0 0 transparent;
    --slider-fill: var(--blue-primary);
    --slider-fill-edge: 0 0 0 0 transparent;
    --slider-knob-fill: var(--bg-white);
    --slider-knob-shadow: inset 0 0 0 3px var(--blue-primary), 0 2px 8px rgba(21,101,192,0.25);
    --slider-knob-shadow-hi: inset 0 0 0 3px var(--blue-primary), 0 2px 8px rgba(21,101,192,0.25), 0 0 0 5px rgba(var(--blue-primary-rgb), 0.5);
    --slider-knob-shadow-press: inset 0 0 0 3px var(--blue-primary), 0 2px 8px rgba(21,101,192,0.25), 0 0 0 5px rgba(var(--blue-primary-rgb), 0.5);
    /* Classic has no press movement; the ring is its press. */
    --slider-knob-press-scale: 1;
    /* A white dot on a white knob says nothing, so classic themes it away. */
    --slider-dot-display: none;
    --slider-dot-size: 0;
    --slider-dot-fill: #fff;
    --slider-dot-shadow: none;

    /* ── LIFTED OUT OF THE CALL SITES, 2026-09-11 ────────────────────────────
       Every token below used to exist only in the capsule block, with classic's
       value living as the var() fallback at each place that read it. That is a
       deliberate pattern here and it stays for the families that need it (see
       the note under CAPSULE-ONLY BY DESIGN at the foot of this block). These
       41 did not need it: each had exactly ONE classic value across all 82 of
       its call sites, in both light and dark, so keeping it out of this block
       bought nothing and cost the thing this file promises, which is that you
       can read a theme as one complete set of values.

       Every value here is the literal that was measured at the call sites, not
       a redesign, so classic renders byte-identically. The now-redundant
       fallbacks were removed in the same pass: leaving both would put one fact
       in two places, which is how the two copies quietly stop agreeing.

       A surface that classic simply does not HAVE still gets a token here (a
       square chat header, a rail with no fade, zero room under a chip row).
       "Classic does not do this" is a value, and writing it down is what makes
       the block complete. */

    /* Horizontal scroll tracks. */
    --track-scroll-margin: 0;

    /* Date and time pickers. */
    --opt-hover-bg: var(--blue-pale);
    --calday-width: 100%;
    --calday-radius: 8px;
    --calday-today-cue: inset 0 0 0 1.5px var(--blue-primary);

    /* Field prefix segment (the +62 that shares a box with a phone input). */
    --field-prefix-fill: var(--bg-page);

    /* Search draws its own header rather than using AppHeader. */
    --search-hdr-pad: max(12px, var(--safe-area-top)) max(16px, calc((100% - 848px) / 2)) 12px;

    /* Profil's first content card tucks under the header in classic. */
    --profile-card-top: -24px;
    --profile-avatar-pad-bottom: 36px;

    /* Dropdown menu. */
    --lb-pad: 4px;
    --lb-option-radius: 8px;

    /* Skill cart, beside the skill search on Posting and Jasaku. */
    --cart-radius: 20px;
    --cart-press-transform: translateY(0) scale(0.97);

    /* Chat header. Classic's is a full-bleed square band, which is why three of
       these four are the "no" value rather than a measurement. */
    --chat-hdr-width: auto;
    --chat-hdr-max: none;
    --chat-hdr-radius: 0;
    --chat-hdr-edge: none;

    /* Chat send button. */
    --send-fill: linear-gradient(135deg, var(--chat-bubble-start), var(--chat-bubble-end));
    --send-shadow-rest: 0 2px 8px rgba(21,101,192,.3);
    --send-shadow-hover: 0 2px 8px rgba(21,101,192,.3);
    --send-shadow-press: 0 2px 8px rgba(21,101,192,.3);
    --send-transform-hover: scale(1.05);
    --send-transform-press: scale(.95);

    /* Chat message bubbles, sent and received (Chat, SupportChat). Classic is a
       faithful copy of what the bubbles were: the send button's own diagonal
       fill with no shadow, 16px corners with a 4px tail on the sender's side,
       and a soft lift under the received bubble. The received bubble's 1px
       edge is --card-edge-color, already themed. */
    --bubble-fill: var(--send-fill);
    --bubble-shadow: none;
    --bubble-th-shadow: 0 2px 8px rgba(13,33,55,.10);
    --bubble-radius: 16px;
    --bubble-tail: 4px;

    /* Chat context card (the job under /chat's header, the ticket under
       /support-chat's). Classic is what utilities.css .chat-ctx spelled out
       before these were tokens: a white card on the card radius with the small
       shadow, a 32px pale tile with the icon at its drawn size, and no chrome on
       the open arrow. */
    --ctx-fill: var(--bg-white);
    --ctx-edge: var(--shadow-sm);
    --ctx-radius: var(--card-radius, 10px);
    --ctx-pad: 10px 12px;
    --ctx-hover-fill: var(--bg-white);
    --ctx-hover-edge: 0 4px 16px rgba(13,33,55,.08), 0 0 0 1.5px var(--blue-pale);
    --ctx-hover-transform: translateY(-1px);
    --ctx-tile-size: 32px;
    --ctx-tile-radius: var(--tile-radius, 8px);
    --ctx-tile-bg: var(--blue-pale);
    --ctx-tile-shadow: none;
    --ctx-icon-size: 16px;
    --ctx-icon-shadow: var(--icon-shadow);
    --ctx-go-size: auto;
    --ctx-go-fill: transparent;
    --ctx-go-shadow: none;

    /* Chat dialog cards: the price offer, the agreement proposal, the admin
       recommendation and decision, the job status notice and the call record,
       all drawn inside the conversation (Chat.razor.css). Classic is a faithful
       copy of what those blocks spelled out before they were tokens.

       "me" is the card the current user sent. Classic paints it in the sent
       bubble's brand fill with white ink, so every colour on it has a white
       counterpart here; capsule makes it the same white card as the received
       one (see the capsule block for why), which is what these pairs exist for. */
    --dlg-me-fill: var(--bubble-fill);
    --dlg-me-shadow: var(--bubble-shadow);
    --dlg-me-ink: #fff;
    --dlg-me-ink-soft: rgba(255,255,255,.7);
    --dlg-me-ink-faint: rgba(255,255,255,.5);
    --dlg-me-accent: #fff;
    --dlg-me-tile-bg: rgba(255,255,255,.15);
    --dlg-me-tile-ink: #fff;
    --dlg-me-rule: rgba(255,255,255,.15);
    --dlg-cancel-ink: var(--blue-primary);
    /* Card head: the rule under it and the tint behind it, per kind. */
    --dlg-rule: var(--border);
    --dlg-med-rule: rgba(234,88,12,0.15);
    --dlg-med-edge: #EA580C;
    --dlg-head-fill-price: transparent;
    --dlg-head-fill-med: var(--orange-light);
    --dlg-head-fill-agr: var(--orange-light);
    --dlg-agr-hue: #EA580C;
    /* Icon tile in the head. */
    --dlg-tile-size: 32px;
    --dlg-tile-radius: var(--tile-radius, 8px);
    --dlg-tile-bg: var(--orange-light);
    --dlg-tile-bg-med: var(--bg-white);
    --dlg-tile-bg-agr: rgba(22,163,74,.12);
    --dlg-tile-shadow: none;
    /* Action buttons: the confirm and its pair. */
    --dlg-btn-h: auto;
    --dlg-btn-y-fill: var(--btn-success-start);
    --dlg-btn-y-shadow: 0 2px 6px var(--btn-success-shadow);
    --dlg-btn-y-off-fill: var(--border);
    --dlg-btn-n-shadow: none;
    --dlg-btn-n-hover-fill: var(--cell-fill, var(--bg-white));
    /* Outcome line ("Harga disetujui", ...). Classic is a full-width strip. */
    --dlg-ok-width: auto;
    --dlg-ok-radius: 0;
    --dlg-ok-margin: 0;
    --dlg-ok-pad: 8px 14px 12px;
    --dlg-ok-shadow: none;
    --dlg-ok-lit: transparent;
    --dlg-ok-tint-accepted: transparent;
    /* Admin notes and the "Buat Perjanjian" hint. */
    --dlg-anotes-fill: transparent;
    --dlg-anotes-style: italic;
    --dlg-anotes-margin: 0;
    --dlg-anotes-pad: 4px 14px 8px;
    --dlg-anotes-radius: 0;
    --dlg-hint-fill: rgba(234, 88, 12, 0.08);
    --dlg-hint-bar: 3px solid #EA580C;
    --dlg-hint-radius: 6px;
    /* Status notice: the disc and the panel under it. */
    --dlg-st-icon-size: 32px;
    --dlg-st-icon-bg: var(--blue-pale);
    --dlg-st-icon-shadow: none;
    --dlg-st-icon-overlap: 6px;
    --dlg-st-fill: var(--blue-pale);
    --dlg-st-radius: 12px;
    --dlg-st-pad: 10px 16px;
    --dlg-st-pad-info: 12px 14px;
    --dlg-st-shadow: none;
    --dlg-st-photo-radius: 8px;
    --dlg-st-photo-shadow: none;

    /* Voice call overlay and its minimized pill (Shared/CallManager.razor.css).
       Classic is a faithful copy of the full-bleed screen that component
       spelled out before it was tokenised: the dark-to-light gradient as the
       whole surface, translucent white bumps for the controls, a drawn 2px
       ring that grows for the ring pulse, plain dim status text, the fault
       carried by a 2px ring over a red wash, the diagonal brand pill when
       minimized, and no job trough and no role line (the two display tokens
       hide the elements the capsule card added). */
    --call-ground: transparent;
    --call-inset: 0;
    --call-card-radius: 0;
    --call-card-fill: linear-gradient(180deg, #0D2137 0%, #1565C0 50%, #2196F3 100%);
    --call-card-shadow: none;
    --call-head-fill: transparent;
    --call-body-fill: transparent;
    --call-body-pad: 0 24px 50px;
    --call-job-display: none;
    --call-role-display: none;
    --call-dot-display: none;
    --call-avatar-shadow: none;
    --call-pulse-ring: 2px solid rgba(255,255,255,.25);
    --call-pulse-glow: transparent;
    --call-status-margin: 6px;
    --call-status-height: auto;
    --call-status-pad: 0 32px;
    --call-status-fill: transparent;
    --call-status-ink: rgba(255,255,255,.65);
    --call-status-size: 15px;
    --call-status-weight: 500;
    --call-status-shadow: none;
    --call-status-fault-fill: transparent;
    --call-status-fault-ink: #FFC9C9;
    --call-disc: rgba(255,255,255,.12);
    --call-disc-ink: rgba(255,255,255,.85);
    --call-disc-shadow: none;
    --call-disc-hover: rgba(255,255,255,.2);
    --call-disc-press-shadow: none;
    --call-disc-on: rgba(255,255,255,.3);
    --call-disc-on-ink: #fff;
    --call-disc-on-shadow: none;
    --call-disc-on-hover: rgba(255,255,255,.42);
    --call-disc-on-hover-shadow: none;
    --call-disc-on-press-shadow: none;
    --call-fault-fill: rgba(239,68,68,.3);
    --call-fault-ink: #fff;
    --call-fault-shadow: inset 0 0 0 2px #FF8A8A;
    --call-fault-hover: rgba(239,68,68,.5);
    --call-fault-press: rgba(239,68,68,.6);
    --call-end-fill: #EF4444;
    --call-end-shadow: 0 4px 16px rgba(239,68,68,.4);
    --call-end-hover-shadow: 0 4px 16px rgba(239,68,68,.4);
    --call-end-press-shadow: 0 4px 16px rgba(239,68,68,.4);
    --call-accept-fill: #22C55E;
    --call-accept-shadow: 0 4px 16px rgba(34,197,94,.4);
    --call-accept-hover-shadow: 0 4px 16px rgba(34,197,94,.4);
    --call-accept-press-shadow: 0 4px 16px rgba(34,197,94,.4);
    --call-action-hover: scale(1.05);
    --call-action-press: scale(.95);
    --call-label-ink: rgba(255,255,255,.6);
    --call-label-warn-ink: rgba(255,255,255,.92);
    --call-minimize-top: max(var(--safe-area-top), 12px);
    --call-minimize-fill: rgba(255,255,255,.14);
    --call-minimize-ink: #fff;
    --call-minimize-shadow: none;
    --call-minimize-hover: rgba(255,255,255,.26);
    --call-minimize-press: rgba(255,255,255,.34);
    --call-bubble-fill: linear-gradient(135deg, #1565C0, #2196F3);
    --call-bubble-pad: 5px 6px 5px 12px;
    --call-bubble-shadow: 0 6px 20px rgba(13,33,55,.35);
    --call-bubble-main-pad: 4px 4px 4px 0;
    --call-bubble-main-fill: transparent;
    --call-bubble-main-ink: #fff;
    --call-bubble-main-shadow: none;
    --call-bubble-main-hover: none;
    --call-bubble-main-hover-shadow: none;
    --call-bubble-main-press: none;
    --call-bubble-main-press-shadow: none;
    --call-bubble-dot-halo: none;
    --call-bubble-fault-fill: transparent;
    --call-bubble-fault-ink: #FFC9C9;
    --call-bubble-fault-shadow: none;
    --call-bubble-end-shadow: none;
    --call-bubble-end-hover: scale(1.08);
    --call-bubble-end-press: scale(.92);

    /* Back-to-top. Only the four that do NOT vary by light/dark; the two that
       do stay at the call sites for the reason given up in the BACK-TO-TOP
       note. */
    --btt-fill: var(--blue-pale);
    --btt-on-fill: var(--tab-active-bg);
    --btt-on-border-color: var(--tab-active-bg);
    --btt-press-transform: scale(0.95);

    /* Job progress rail on Detail. */
    --step-rail-margin: 8px -14px 0;
    --step-rail-fill: transparent;
    --step-rail-radius: 0;
    --step-rail-fade: var(--bg-white);
    --step-rail-overflow: visible;
    --step-dot-radius: 50%;
    --step-dot-fill: var(--green-badge);
    --step-dot-shadow: none;
    --step-dot-ring: 0 0 0 4px rgba(22,163,74,.2);
    --step-dot-pending-fill: var(--bg-sunken);
    --step-dot-pending-edge: var(--border);

    /* Zero, and it has to be spelt out: classic's chips paint no shadow, so
       reserving room under a sideways-scrolling row would only open a gap. */
    --chip-row-pad-bottom: 0;

    /* The two clickable cards whose hover cue was hand-copied from an older,
       weaker value. Distinct from --card-hover-transform / --card-hover-shadow
       above on purpose: that difference is what classic actually has. */
    --card-hover-lift: translateY(-1px);
    --card-hover-edge: 0 4px 12px rgba(0, 0, 0, .06);

    /* ── CAPSULE-ONLY BY DESIGN ──────────────────────────────────────────────
       19 tokens are NOT here and must not be moved here. They are listed so the
       gap reads as a decision rather than an oversight, and each one falls into
       one of the three reasons this file already argues:

       CLASSIC'S VALUE DIFFERS PER PAGE. Capsule pulls a scattered set of
       literals onto one scale; classic IS the scatter, and one value cannot
       carry it. --chip-radius has nine distinct values across 40 call sites,
       --card-radius six, --field-radius six, --tile-radius five, --tag-radius
       four, plus --field-radius-block, --field-prefix-radius, --icon-btn-radius,
       --track-margin, --track-pad, --track-scroll-pad, --track-gap,
       --tonal-fill, --cell-on-fill, --cell-on-ink, --cell-on-border-color and
       --cell-focus-shadow. Collapsing any of them here would silently retire
       drift that classic is supposed to keep.

       CLASSIC'S VALUE DIFFERS PER THEME, not per page: --btt-border-color and
       --btt-shadow carry a different shadow stack and hairline opacity in dark
       than in light, so a single declaration here would hand one mode the
       other's value.

       THE MIRROR OF ALL THIS: --cell-fill and --cell-border-color are the two
       that exist only in CLASSIC, and some call sites read them with no
       fallback at all, so under capsule they resolve to nothing on purpose. */
}

/* ═══════════════════════════════════════════════════════════════════════════
   CAPSULE — experimental, 2026-09-07. Opt in with UiTheme.Active = Capsule.

   Vertical padding and font-size are deliberately NOT changed from classic, so
   every button keeps its exact height and no bar, row or card reflows when the
   theme is switched. Only radius, side padding, fill and depth move.
   ═══════════════════════════════════════════════════════════════════════════ */
:root[data-ui="capsule"] {
    /* ── RADIUS SCALE ──────────────────────────────────────────────────────
       Same five roles, regularised. Anything holding a short label goes fully
       round — that is the capsule idea stated once, instead of being re-decided
       per component. Containers step 18 / 20 / 24 rather than classic's
       14 / 16 / 20, so a card reads as clearly rounder than the tag inside it
       and clearly less round than the panel around it.

       Images keep a soft corner rather than going round: a photo forced into a
       circle loses its subject, and a square thumbnail beside a pill is the
       contrast that makes the pill read as a control. */
    --r-pill: 999px;
    --r-tag: 999px;
    --r-thumb: 18px;
    --r-card: 24px;   /* was 20px: the card floats now, so it takes the panel step */
    --r-card-sm: 18px;
    --r-panel: 24px;
    --r-circle: 50%;

    /* Brand surface: the same 180deg fill and sheen the filled buttons use, so a
       brand block and a brand button read as the same material. */
    --brand-fill:
        var(--sheen-layer),
        linear-gradient(180deg, var(--btn-primary-end), var(--btn-primary-start));
    --brand-shadow: 0 1px 2px var(--btn-contact), 0 4px 12px var(--btn-primary-shadow), 0 10px 24px -6px var(--btn-primary-shadow);

    /* ── BOTTOM NAV ────────────────────────────────────────────────────────
       A floating card at the bottom, mirroring the header's card at the top, on
       the same --r-panel radius and the same 12px side inset. Without this the
       nav is the last thing on Beranda still welded to the screen edge with
       square corners, which is what gave the page away as half-converted.

       TRANSLUCENT, on a second attempt (2026-09-10). The first glass experiment
       was reverted on legibility, and the note here used to read "OPAQUE,
       deliberately". What brought it back is a measured defect the opaque version
       has: --nav-fill and the cards behind it are both --bg-white, literally
       rgb(255,255,255), and --nav-edge cast its shadow DOWNWARD (positive y), away
       from the only side anything passes. So a card scrolling under the nav met no
       shadow, no tonal step and no drawn edge, and the two surfaces read as one.

       What is different this time is one term: brightness() inside the backdrop
       filter, which pushes the blurred content toward the nav's own surface colour
       before it composites. Measured, that is worth 0.43 of label contrast and it
       is the difference between failing the 4.5 floor and matching an opaque bar.
       The numbers and the method are in theme.css beside --nav-backdrop-src.
       --nav-edge now also carries an upward layer, so the top edge exists even when
       nothing is behind it to blur.

       If this fails on legibility again, set --nav-backdrop to none and take
       --nav-fill back to var(--bg-white); the shadow direction fix is independent
       and should stay either way.

       Leaving the flow means it no longer reserves height, so --scroll-pad-bottom
       makes that space back or the last card hides underneath. The two tokens are
       a pair; never change one alone. */
    --nav-position: absolute;
    --nav-inset: auto 0 calc(var(--safe-area-bottom-fixed) + 10px) 0;
    --nav-radius: var(--r-panel);
    --nav-fill: color-mix(in srgb, var(--bg-white) 76%, transparent);
    --nav-backdrop: var(--nav-backdrop-src);
    --nav-border-top: none;
    /* Three layers. The first two are the nav's own shadow, now cast UPWARD into
       the content that passes behind it; the third keeps it reading as a floating
       card above the page ground. The old value had only the downward pair, which
       is the measured reason the top edge disappeared. */
    --nav-edge:
        0 -1px 3px rgba(13,33,55,0.10),
        0 -8px 20px -10px rgba(13,33,55,0.22),
        0 10px 26px -8px rgba(13,33,55,0.28);
    /* No safe-area inside the box any more — the inset above carries it, exactly
       as the floating header moves it outside its own padding. */
    --nav-height-box: var(--nav-height);
    --nav-pad-bottom: 0px;
    --nav-pad-x: 0px;
    /* The floating card is out of the flow and capped by --header-max, so auto
       is what centres it. See the classic note for why this is a token. */
    --nav-margin-inline: auto;
    --scroll-pad-bottom: calc(var(--nav-height) + var(--safe-area-bottom-fixed) + 22px);
    /* A floating bar's top edge is its height PLUS the 10px it is lifted, so
       everything anchored above it has to rise by the same amount. Measured
       without this: the BackToTop button overlapped the nav by 2px, and a
       slide-up sheet would tuck under its lower corner. The 8px on top keeps a
       visible gap rather than having the two just touch. */
    --nav-clearance: calc(var(--nav-height) + var(--safe-area-bottom-fixed) + 18px);
    /* Selected destination becomes a filled pill behind the icon and label. The
       3px top bar had to go: on a rounded card it cuts straight across the corner
       radius on the first and last item. A pill is also what the current Android
       navigation bar uses, so this lands closer to the reference. */
    /* A rounded RECTANGLE, not a pill. At 999px CSS clamps the radius to half
       the box, and the indicator is 52px tall, so it was rendering at 26px: a
       stadium, which is what read as too round. 14px on a 48px box is about
       58 percent of the way to that clamp, so it stays unambiguously a
       rectangle at this size.

       This is deliberately NOT a value from the radius scale, and that is worth
       stating rather than hiding: the scale's roles describe what a box HOLDS,
       and this box holds nothing. It is a state layer painted behind the icon
       and label. Every value on the scale renders too close to the clamp here,
       which is the same reason --r-pill was wrong.

       The inset goes 6px to 8px so the indicator keeps a visible gap from the
       nav card's own edge on all four sides. It matters most on the first and
       last destination, where the card's 24px corner curves in: with an 8px
       inset and a 14px corner the indicator's nearest corner sits 16px from
       the card's corner centre against a 24px radius, so it clears with room
       to spare instead of crowding the curve. */
    --nav-ind-inset: 8px;
    --nav-ind-height: auto;
    --nav-ind-radius: 14px;
    /* NOT PAINTED under capsule (2026-09-10). The icons became solid raised
       glyphs that light up blue on the selected destination, and the label lights
       with them, so a tinted pill behind them was a second answer to a question
       already answered. Two selection cues on one control read as emphasis, not
       as clarity.

       transparent rather than deleting the ::before: the geometry above is still
       live, because the hover and focus pill below copies it exactly, and the box
       still has to exist for the paint order that keeps the icon above the state
       layer. Putting the tint back is this one line. */
    --nav-ind-bg: var(--nav-state-bg-src);
    /* The pill hugs the icon and label instead of flooding the flex slot. The
       item still stretches — that is the tap target and it should stay a fifth
       of the bar — but at desktop width each slot is ~200px, and a pill that
       wide reads as a slab rather than a selection. Caught at 1440. */
    --nav-ind-max: 76px;
    /* Interaction keeps that geometry, and since the selection pill stopped
       painting this is now the only pill in the nav: it appears on hover, focus
       and press, and never on the selected destination at rest. Classic's full-slot
       square cannot be reused here: the nav is a rounded card with
       overflow:visible, and a square tint on the first or last item paints
       straight over the corner the card rounds away — measured, its right edge
       sat flush with the card's at 376px against a 24px radius. */
    --nav-hover-inset: 8px;
    --nav-hover-radius: 14px;
    --nav-hover-max: 76px;
    /* BEHIND the glass, and therefore much stronger than it looks. The pill is
       painted into the surface layer's backdrop, so before it reaches the eye it
       is blurred 24px, then saturated 1.5, then brightened 1.3, and finally seen
       through 76 percent white. A 0.13 alpha tint, which is what sat here while
       the pill was on top of the bar, disappears completely through that stack.

       The strength is capped by the label sitting on top of it, not by taste.
       Measured on the hovered item, grey label over the pill, at 390 to 1440:

           alpha   blueness of the wash   label contrast
           0.13    6                      4.84
           0.40    15                     4.81
           0.55    20                     4.76
           0.70    26                     4.68   <- used
           0.92    34                     4.46   FAILS the 4.5 floor

       So 0.70 is the deepest wash the label survives, and it still carries four
       times the colour the old on-top tint did. Raise it and the hover label goes
       under; the numbers are in theme.css beside --nav-state-bg-src. */
    --nav-hover-bg: var(--nav-state-bg-src);
    --nav-state-z: -1;

    /* A card that lifts further and lands softer, matching the deeper elevation
       the capsule buttons use. The 1.5px blue-pale ring stays: it follows the
       element's own radius, so it rounds with the card for free. */
    --card-hover-transform: translateY(-2px);
    --card-hover-shadow: 0 2px 6px rgba(13,33,55,.07), 0 10px 24px -6px rgba(13,33,55,.16), 0 0 0 1.5px var(--blue-pale);
    /* Presses settle rather than shrink: the card drops back to its resting
       plane instead of scaling down, matching how the capsule buttons sink. */
    --card-press-transform: translateY(0) scale(0.99);

    /* No lift on hover any more. It was here because a 1.5px border shift is a
       weak cue at full radius, but the trough below removes the border
       entirely and replaces the cue with a fill, and an item sitting INSIDE a
       container must not cast a shadow onto it. The selected item still lifts,
       because it genuinely sits proud of the trough. */
    --pill-hover-shadow: none;
    --pill-active-shadow: 0 1px 2px var(--tab-active-shadow), 0 4px 12px -2px var(--tab-active-shadow);

    /* ── SELECTABLE ROW ("trough putih") ────────────────────────────────────
       The row becomes one container and the items give up their individual
       borders. Same reasoning as the nav and the header: this theme separates
       surfaces by shape and shadow, never by a drawn edge, and a row of four
       outlined items was the last place on Beranda still drawing four of them.

       THE DARK TROUGH IS A DIFFERENT COLOUR, not the same token dimmed.
       --bg-white on --bg-page is #1A1E24 on #111418, a 9/255 step that does
       not read as a container, so dark gets --bg-raised #242A33 instead. Light
       has nowhere above #FFFFFF to go and leans on the edge instead. Both live
       in theme.css so they follow [data-theme] for free.

       CAPPED, like the header. Uncapped this spans the whole viewport at
       desktop width while the cards below stay at 848, which is exactly the
       marooned-slab bug caught at 1440 on the floating header. The
       max(16px, …) term is the same idiom the action bar and --nav-pad-x use:
       a 16px gutter on phones, centred on the content column once it binds. */
    --track-fill: var(--bg-raised);
    --track-edge: var(--shadow-raised);
    --track-radius: var(--r-pill);
    --track-margin: 12px max(16px, calc((100% - 848px) / 2)) 6px;
    --track-pad: 5px;
    --track-fade: var(--bg-raised);
    /* Clips .scroll-fade-wrap's two edge gradients to the trough's radius.
       Without it they paint square over the rounded ends, the same corner
       spill the nav hover cue had. The trough's own shadow is unaffected: an
       element's box-shadow is not clipped by its own overflow. */
    --track-overflow: hidden;
    /* THE SCROLLER FILLS THE TROUGH'S PADDING BOX, and these two are what make it
       do that. Capsule-only; see the note in the classic block above for why.

       A horizontal scroller clips BOTH axes, and this one used to sit exactly on
       the pills with no padding at all, so the selected pill's lift was sliced
       flat at its own edge. On the first pill of a rail that lands right at the
       left end of the trough, which is where it reads as a straight cut through
       the glow rather than as a shadow.

       The fix is not more space: the padding is given back as an equal negative
       margin, so the pills do not move by a pixel and the trough does not grow.
       All it changes is where the clip happens — at the trough's inner edge
       instead of at the pill's edge — so the lift has the trough's own 5px band
       to fall off into. Whatever is left beyond that is clipped by the trough,
       which is correct: a shadow cast inside a container must not escape it.

       The two values move together and must stay equal and opposite. */
    --track-scroll-pad: 5px;
    --track-scroll-margin: -5px;
    --track-gap: 4px;

    /* Items lose their fill and border to the trough. Hover and focus share
       the pale tint the nav pill uses, so the cue is the same shape and colour
       wherever a destination or a choice is being previewed. */
    --pill-fill: transparent;
    --pill-border-color: transparent;
    --pill-hover-border-color: transparent;
    --pill-hover-bg: var(--blue-pale);
    /* Selected: the same 180deg fill and sheen as a filled button, replacing
       the flat --tab-active-bg. A chosen item and a primary action should be
       made of the same thing. */
    --pill-on-fill: var(--brand-fill);
    --pill-on-border-color: transparent;

    /* Outline out, fill in. An unselected cell becomes a tonal block and a        selected one takes the button's own fill plus the layered glow, which is        the same pair the pills and chips use. --bg-sunken rather than --bg-page,        because these sit on a white card where --bg-page is invisible.         --cell-radius is capsule-only, so each component keeps its own classic        literal as the fallback. A stacked date cell takes the compact-notice        step; a single-line time slot is a short label and takes --chip-radius        instead, which is the radius scale doing its job. */     --cell-fill: var(--bg-sunken);     --cell-border-color: transparent;     --cell-radius: var(--r-card-sm);     --cell-on-shadow: var(--pill-active-shadow);
    /* Keyboard focus on a SELECTED cell. It cannot be a border recolour any
       more: the border is transparent here, and painting it --blue-primary put
       a dark ring over a fill whose top is #2196F3, which is the line you can
       see on a focused chip. An outer ring sits outside the shape instead, so
       it reads on any fill. Classic keeps none, which is what it already had:
       its selected chip's border was already --blue-primary, so focus changed
       nothing there either. */
    --cell-focus-shadow: var(--cell-on-shadow), 0 0 0 3px rgba(var(--blue-primary-rgb), 0.35);
    /* A selected cell or loose chip takes the button's own fill, so a chosen
       option and a primary action are made of the same thing. Capsule-only,
       because classic disagrees with itself about what "selected" looks like
       here (a pale tint on Contact and Search, a solid blue on the booking
       grids), and each component keeps the one it already had. */
    --cell-on-fill: var(--brand-fill);
    --cell-on-ink: #fff;
    /* The selected border has to go transparent, not follow the fill. Painting
       it --blue-primary over a fill whose top stop is #2196F3 draws a visibly
       darker line around the top of the chip: the same defect the
       --cell-focus-shadow note below was written for, arriving by a different
       route. Classic keeps whichever colour each component already used. */
    --cell-on-border-color: transparent;

    /* Hover and keyboard focus on an UNSELECTED row in a picker. It cannot stay
       --blue-pale once the wheel columns become a --bg-sunken trough: that is
       #E8F1FB on #EDF0F4 in light mode, a step of five values, which is not a
       cue at all. Measured, not eyeballed, the same way --bg-sunken itself was
       picked.

       A translucent brand tint composites onto whatever is behind it, so it
       reads on the trough, on a white calendar sheet, and in dark mode, from
       one value. Capsule-only; classic keeps the --blue-pale each picker
       already has. */
    --opt-hover-bg: rgba(var(--blue-primary-rgb), 0.12);

    /* CALENDAR DAY. A date cell holds one or two digits and nothing else, so it
       takes --r-circle and stops stretching to the column: a 34px circle is the
       one shape that cannot be read as anything but a single day.

       THE FULL-WIDTH CAPSULE WAS REJECTED ON PURPOSE. It is the more obvious
       capsule move, but the grid gap is 2px, so a stadium ends two pixels short
       of its neighbour and starts to read as a segment of a bar. That shape is
       worth keeping free in case a date range is ever built.

       Today's ring goes with it. An inset 1.5px hairline is the drawn edge this
       theme removes everywhere else, and it also has a real pixel size, so it
       smears at display zoom the way the trough edge did. Today becomes a tonal
       dot instead (--cell-fill), which is the same material an unselected chip
       is made of. */
    --calday-width: 34px;
    --calday-radius: var(--r-circle);
    --calday-today-cue: none;

    /* FORM FIELD. The drawn outline goes the way every other drawn edge in this
       theme went: a soft tonal fill at rest, the brand edge and a ring on
       focus. One radius for a single-line field, because a field holds a short
       label and that is what --r-pill is for, and the compact-notice step for a
       textarea, because a box you write paragraphs into is a container and a
       stadium-shaped one would be absurd.

       The footer strip loses its fill and its rule: on a tonal field a
       page-coloured strip reads as a lighter panel stuck to the bottom, and the
       counter needs no separating edge to be read as a counter.

       DECIDED 2026-09-11, after a prototype with a 1px ink hairline at 14 to
       36 percent was rejected: the field stays borderless. Known cost, accepted
       by the owner: Windows HDR tone-maps SDR near-whites so that the 7 percent
       step from --bg-sunken to --bg-white can vanish, and the field reads as
       bare text on such a screen. Phones and SDR desktops show it. Do not
       reopen this by adding a hairline; a field that lost its box is a missing
       --field-fill on that wrapper, not a theme problem. */
    --field-fill: var(--bg-sunken);
    /* The same tonal fill for the four field surfaces whose classic fill is the
       PAGE colour rather than white: the two currency buttons that share a row
       with a money field, and the two modal textareas. Capsule-only, so classic
       keeps the --bg-page each of them already has. Without it those four would
       have to borrow --field-fill and gain white in light mode and a card-dark
       grey in dark mode, which is a visible change to a theme that is supposed
       to be untouched. */
    --tonal-fill: var(--bg-sunken);
    --field-border-color: transparent;
    --field-radius: var(--r-pill);
    --field-radius-block: var(--r-card-sm);
    /* Focus lifts the field out of the tonal rest state onto the card's own
       surface, so the cue is a change of material and not only a line. */
    --field-focus-fill: var(--bg-white);
    --field-focus-border-color: var(--blue-primary);
    --field-focus-shadow: 0 0 0 3px rgba(var(--blue-primary-rgb), 0.28);
    --field-footer-fill: transparent;
    --field-footer-edge: none;

    /* The dial-code prefix segment (+62) that shares a box with a phone input.
       It painted its own --bg-page block with a 10px left corner, which was
       right when the field was a 10px white box and wrong in both ways once the
       field became a --bg-sunken pill: the block is now LIGHTER than the field
       it sits in, and its corner is far squarer than the curve around it. On
       hover the field turns white while the prefix stayed grey, which is what
       made it read as a slab overflowing the pill with the border cut behind it.

       So the prefix stops painting anything and simply sits on the field's own
       fill, in every state. Its radius still follows the field's, because the
       hover and press tint DOES paint, and a square-ended tint inside a pill is
       the same defect one interaction later. */
    --field-prefix-fill: transparent;
    --field-prefix-radius: var(--r-pill) 0 0 var(--r-pill);

    /* DROPDOWN MENU. The menu took the card radius and went from 12px to 24px,
       and its rows did not move: 4px of padding and an 8px corner cleared a
       12px curve and does not clear a 24px one, so overflow:hidden cut a nick
       out of the top-left of the first row. It shows on the selected row
       because that row is the only one that paints a fill.

       This is the nav indicator's problem exactly, and it takes the same
       answer: give the state layer room to sit inside the curve, and a corner
       round enough to follow it. Run the geometry rather than eyeballing it.
       The menu's inner curve is 22.5px (24 less the 1.5px border); a corner of
       radius r inset by p clears it when the distance between the two corner
       centres, sqrt(2) * (22.5 - p - r), stays under 22.5 - r. At 4px and 8px
       that is 14.8 against 14.5, which is why it clipped by a hair. At 6px and
       12px it is 6.4 against 10.5, clear with room to spare.

       The row went the whole way on 2026-09-08 and became a pill, so the
       clearance above stopped being a near miss and became a wide margin: a
       corner that is rounder than the container's own inner corner can never
       poke outside it, and a 41px row clamps to 20.5px against the 16.5px the
       menu leaves at 6px of padding.

       12px was the earlier answer, chosen because a fill painted behind a
       label holds nothing and had no business taking a scale value. What
       changed is the decision, not the reasoning: the selected row now takes
       the same fill, sheen and lift as a filled button, so it IS a control and
       --r-pill is the value that says so. */
    /* Search draws its own header instead of using AppHeader, and it hardcoded
       classic's 145deg band with a comment saying it matched "the same one
       AppHeader paints". That was true when it was written; AppHeader has since
       become a floating card, so Search was left as the only page still painting
       the retired band, and its straight full-bleed bottom edge against the page
       was the grey line reported above the filter chips.

       It now reads every header token. Only the padding needs its own, because
       Search's band is shorter than AppHeader's and caps its content with side
       padding rather than with an inner row, so one shared value cannot carry
       both. Capsule-only, component keeps its literal, the usual arrangement. */
    --search-hdr-pad: var(--header-pad-top) var(--header-pad-x) var(--header-pad-bottom);

    /* PROFIL: the first content card stops tucking under the header.

       The -24px pull was the classic device for stitching a full-bleed band to a
       sheet below it, and it worked because the band had a straight bottom edge
       worth hiding. The header is a floating card now, so the pull only damages
       both shapes: it covers the blue card's rounded bottom corners, which stops
       it reading as a card at all, and it drops the white card's shadow onto a
       surface that is itself floating.

       THESE TWO MOVE TOGETHER, like --nav-position and --scroll-pad-bottom. The
       36px of padding under the avatar exists only to keep the badge clear of the
       card that pulls up over it; remove the pull and leave the padding, and the
       header card grows 36px of dead space under the badge. Change one alone and
       the page is wrong either way. */
    --profile-card-top: 14px;
    --profile-avatar-pad-bottom: 6px;

    --lb-pad: 6px;
    --lb-option-radius: var(--r-pill);

    /* SKILL CART, the button beside the skill search on Posting and Jasaku. Its
       own comment says it mirrors .btn-primary, and it half did: someone had
       already pointed its HOVER at --btn-transform-hover and
       --btn-shadow-primary-hover, so it lifted correctly and then settled back
       onto a 135deg fill with one flat shadow and a 20px corner. Half converted
       is worse than untouched, because the button only looks wrong once you stop
       touching it.

       Only these two are new. Everything else it needs, the fill, the resting
       and press shadows, already exists in BOTH blocks with classic values that
       are byte-identical to the literals this button was carrying, so it can
       read the button tokens directly and classic does not move. */
    --cart-radius: var(--r-pill);
    --cart-press-transform: var(--btn-transform-press);

    /* OUTLINED CARD. Same move the real cards already made, reaching the ones
       that separated with a drawn edge instead of a shadow. --card-edge-shadow
       reads --shadow-card rather than --shadow-float directly, so these follow
       the card role wherever it is repointed next.

       This is for a card that sits ON THE PAGE. A bordered block INSIDE a card
       is a different object and must not cast a shadow onto the surface holding
       it: those take --cell-fill and --cell-border-color, which is why that
       pair is described as a small surface on a card rather than as a chip. */
    --card-edge-color: transparent;
    --card-edge-shadow: var(--shadow-card);

    /* CHAT joins the floating header. The alternative was defensible (a
       conversation screen keeps a flat bar on purpose) and it is written down
       here so the choice is visible rather than assumed: every other screen in
       this theme separates its chrome by shape, and a single screen keeping a
       welded bar reads as unfinished rather than as deliberate.

       The page has to leave white for the same reason the bar does. A white
       card floating on a white page is only its own shadow; the conversation
       moves onto --bg-page so the bar, the bubbles and the composer all read as
       objects on a ground, exactly as cards do everywhere else.

       The safe-area inset moves OUT of the bar's padding and into its margin,
       the same swap the AppHeader note above describes: while the bar runs to
       the top of the window it must own the notch, and the moment it detaches
       the notch belongs to the space around it. */
    --chat-page-fill: var(--bg-page);
    /* Auto side margins, capped width: the same pair AppHeader uses, and for the
       same reason. A fixed 12px each side left this card spanning the whole
       viewport at desktop width while its own .ch-top row stayed capped at 848,
       so the name and the call button sat marooned in the middle of a very wide
       white slab. The cap is --header-max, so the chat bar and every other
       header line up on one column. */
    --chat-hdr-inset: max(12px, var(--safe-area-top)) auto 0;
    --chat-hdr-width: calc(100% - 24px);
    --chat-hdr-max: var(--header-max);
    --chat-hdr-pad-top: 12px;
    --chat-hdr-radius: var(--r-panel);
    --chat-hdr-edge: var(--shadow-float);

    /* BACK-TO-TOP. The shape was already right (it has always been a circle),
       which is why it survived the sweep unnoticed. What was wrong is what it is
       made of: a drawn 1.5px hairline, and a first shadow layer of 0 0 0 4px,
       zero blur and 4px spread, which is not a shadow but a second ring. That is
       the exact pair the FAB note above records removing, arriving here because
       this button was written from the .btn-maps family instead of the FAB.

       So: no drawn edge, and the float every other detached surface in this theme
       uses, which follows the page theme for free rather than needing its own
       dark stack. The idle stays the calm blue tint on purpose, because this
       button is not a primary action and should not compete with the FAB sitting
       a thumb away from it; the brand fill arrives only when you touch it, and it
       is the real one now, sheen and all, rather than a flat --tab-active-bg.

       Press keeps a shrink rather than gaining the buttons' inset sink. The sink
       would have to be written into the :active rule, which sits after the dark
       override at equal specificity, so classic dark would take the light
       theme's resting shadow through the fallback chain. A visibly wrong classic
       is worse than a slightly quieter capsule press. */
    /* CHAT SEND. Circular already, so the sweep walked past it, and underneath
       it was still wearing every classic habit at once: the 135deg diagonal,
       one flat coloured shadow, and a hover that GROWS the button. Nothing else
       in this app grows on hover, and a control that swells under the cursor is
       the cue this theme replaced with a lift.

       The hue stays --chat-bubble-*, not --btn-primary-*. In light the two are
       the same pair (#1565C0 / #2196F3) so it makes no difference; in dark the
       bubble family is deliberately deeper, and the send button belongs to the
       conversation it sits in. Only the MATERIAL changes: the 180deg fill with
       the sheen, the button's own three-layer glow, a lift on hover and the
       inset sink on press. The depth and motion read the button tokens rather
       than restating them, so the send button follows wherever those go next.

       Capsule-only, like every token here whose classic value the component
       already spells out. */
    --send-fill:
        var(--sheen-layer),
        linear-gradient(180deg, var(--chat-bubble-end), var(--chat-bubble-start));
    --send-shadow-rest: var(--btn-shadow-primary-rest);
    --send-shadow-hover: var(--btn-shadow-primary-hover);
    --send-shadow-press: var(--btn-shadow-primary-press);
    --send-transform-hover: var(--btn-transform-hover);
    /* The FAB's press, not the pill's: a 34px circle needs more travel than a
       wide capsule to read as the same movement. */
    --send-transform-press: var(--fab-transform-press);

    /* The sent bubble is made of the send button's material, not just its hue:
       --send-fill is already the 180deg ramp with the sheen, so the bubble and
       the button that produced it read as the same object. The shadow is the
       chip's selected pair (contact under, brand glow around) rather than the
       button's three-layer glow, because a column of bubbles casting the full
       button glow reads as a stack of buttons. Corners step 16 to 18 with the
       tail 4 to 6: the tail still says "from here", but no longer as a sharp
       notch on an otherwise round object. The received bubble drops its edge
       (--card-edge-color is transparent here) and takes --shadow-raised, the
       same lift as every other white object on this ground. Chosen 2026-09-11
       from five treatments (flat, this, pill, pill with the time inside, soft
       tail with the time inside). */
    --bubble-fill: var(--send-fill);
    --bubble-shadow: 0 1px 2px var(--btn-contact), 0 4px 12px -2px var(--btn-primary-shadow);
    --bubble-th-shadow: var(--shadow-raised);
    --bubble-radius: 18px;
    --bubble-tail: 6px;

    /* The context card becomes a TROUGH, not a card: it sinks into --bg-sunken
       with an inset edge, and the icon tile and the open button stand on it as
       white raised discs. Chosen 2026-09-11 from five treatments (card with a
       raised glyph, full pill, this, a bare row with a "Lihat" pill, and this
       trough with a brand tile). The reasoning: the card sits INSIDE the white
       header card, and a white card on a white card was only ever its border;
       a sunken container is the one shape that reads as "part of the header"
       rather than "another card stuck to it". Same inversion the toggle's off
       state makes.

       The wrench is the point of the tile, and it was not reading: a 16px emoji
       in a 32px square. The glyph goes to 24 in a 40px disc, the disc is lit
       from above (--ctx-tile-fill, theme.css) and stands on --shadow-raised, and
       the glyph gets a deeper contact shadow plus a faint brand glow so it
       reads as an object on an object. The disc stays white rather than brand
       filled because the call button just above it is already the header's one
       filled brand circle; two of them side by side compete.

       Hover on the trough is a wash to --blue-pale, the list-row cue, with no
       lift: a sunken thing does not rise. */
    --ctx-fill: var(--bg-sunken);
    --ctx-edge: inset 0 1px 2px var(--btn-contact), inset 0 0 0 0.5px rgba(13,33,55,0.04);
    --ctx-radius: var(--r-pill);
    --ctx-pad: 6px 8px 6px 6px;
    --ctx-hover-fill: var(--blue-pale);
    --ctx-hover-edge: var(--ctx-edge);
    --ctx-hover-transform: none;
    --ctx-tile-size: 40px;
    --ctx-tile-radius: var(--r-circle);
    --ctx-tile-bg: var(--sheen-layer), var(--ctx-tile-fill);
    --ctx-tile-shadow: var(--shadow-raised);
    --ctx-icon-size: 24px;
    --ctx-icon-shadow:
        drop-shadow(0 0.5px 0.5px var(--icon-contact-src))
        drop-shadow(0 1.5px 1.5px var(--icon-contact-src))
        drop-shadow(0 4px 6px rgba(var(--blue-primary-rgb), 0.28));
    --ctx-go-size: 36px;
    --ctx-go-fill: var(--bg-white);
    --ctx-go-shadow: var(--shadow-raised);

    /* CHAT DIALOG CARDS. One rule decides most of what follows: a dialog card
       is a white raised card whoever sent it, and only its tail moves. The
       brand fill stays reserved for text bubbles. Classic paints the sent
       price card in the bubble's blue and then has to invent a white-on-blue
       version of every part (tile, rule, button, amount); on this theme that
       produced glass buttons and glass discs that belonged to no other surface
       in the app. So every "me" pair below points at the received value.

       The rest is the theme's usual grammar, applied to six parts:
       - no drawn edge: the 1.5px orange border on the admin card, the 1px rule
         under every head, the 3px bar beside the hint and the 1px edge on the
         status photos all go; tint and shadow separate instead
       - the head is a tint lit from above (--lit-overlay-top over the kind's
         light colour), still semantic: orange for price and admin, green for
         the agreement, which classic had painted orange like the admin card
       - the tile is a white disc lit from above on --shadow-raised, the same
         object as the header job card's tile
       - the confirm button is made of the button system's own success
         material; its pair is a tonal pill on --bg-sunken with no edge
       - the outcome line becomes a pill: lit tint, glyph, hugging its text
       - the status notice's disc stands half into its panel, and the panel is
         the lit blue-pale ramp the card tags already use.
       Chosen 2026-09-11, agreed on the prototype with nine cards side by side. */
    --dlg-me-fill: var(--bg-white);
    --dlg-me-shadow: var(--bubble-th-shadow);
    --dlg-me-ink: var(--text-primary);
    --dlg-me-ink-soft: var(--text-muted);
    --dlg-me-ink-faint: var(--text-muted);
    --dlg-me-accent: var(--blue-primary);
    --dlg-me-tile-bg: var(--dlg-tile-bg);
    --dlg-me-tile-ink: var(--orange-badge);
    --dlg-me-rule: transparent;
    --dlg-cancel-ink: var(--red);
    --dlg-rule: transparent;
    --dlg-med-rule: transparent;
    --dlg-med-edge: transparent;
    --dlg-head-fill-price: linear-gradient(180deg, var(--lit-overlay-top), transparent), var(--orange-light);
    --dlg-head-fill-med: linear-gradient(180deg, var(--lit-overlay-top), transparent), var(--orange-light);
    --dlg-head-fill-agr: linear-gradient(180deg, var(--lit-overlay-top), transparent), var(--green-light);
    --dlg-agr-hue: var(--green-badge);
    --dlg-tile-size: 34px;
    --dlg-tile-radius: var(--r-circle);
    --dlg-tile-bg: linear-gradient(180deg, var(--bg-white), var(--bg-sunken));
    --dlg-tile-bg-med: var(--dlg-tile-bg);
    --dlg-tile-bg-agr: var(--dlg-tile-bg);
    --dlg-tile-shadow: var(--shadow-raised);
    --dlg-btn-h: 36px;
    --dlg-btn-y-fill: var(--btn-fill-success);
    --dlg-btn-y-shadow: var(--btn-shadow-success-rest);
    --dlg-btn-y-off-fill: var(--bg-sunken);
    --dlg-btn-n-shadow: inset 0 1px 1px rgba(13,33,55,.06);
    --dlg-btn-n-hover-fill: var(--blue-pale);
    --dlg-ok-width: max-content;
    --dlg-ok-radius: var(--r-pill);
    --dlg-ok-margin: 2px 12px 12px;
    --dlg-ok-pad: 7px 12px;
    --dlg-ok-shadow: 0 1px 2px rgba(13,33,55,.06);
    --dlg-ok-lit: var(--lit-overlay-top);
    --dlg-ok-tint-accepted: var(--green-light);
    --dlg-anotes-fill: var(--bg-sunken);
    --dlg-anotes-style: normal;
    --dlg-anotes-margin: 4px 14px 8px;
    --dlg-anotes-pad: 8px 10px;
    --dlg-anotes-radius: 12px;
    --dlg-hint-fill: linear-gradient(180deg, var(--lit-overlay-top), transparent), var(--orange-light);
    --dlg-hint-bar: none;
    --dlg-hint-radius: 14px;
    --dlg-st-icon-size: 36px;
    --dlg-st-icon-bg: var(--dlg-tile-bg);
    --dlg-st-icon-shadow: var(--shadow-raised);
    --dlg-st-icon-overlap: -10px;
    --dlg-st-fill: linear-gradient(180deg, var(--blue-pale-top), var(--blue-pale-bottom));
    --dlg-st-radius: 18px;
    --dlg-st-pad: 18px 16px 12px;
    --dlg-st-pad-info: 18px 16px 12px;
    --dlg-st-shadow: 0 1px 2px rgba(13,33,55,.06);
    --dlg-st-photo-radius: 14px;
    --dlg-st-photo-shadow: var(--shadow-raised);

    /* THE CALL CARD (2026-09-11). The voice call becomes one floating card with
       the anatomy of the price card in chat: a lit brand head holding the
       person, their role and a status pill, a white body holding the job trough
       and the controls. Nothing here is a new shape: the head is the header's
       ramp lit from the top, the status pill is the header tab strip's selected
       tab (--header-tabs-fill with blue ink), the job trough is the chat
       header's job card, the controls are the button system's materials, and
       every white control shares one disc: a ramp from --bg-white to
       --bg-sunken, a light line at the top edge, a shade line at the bottom
       edge and a three-layer drop. The first draft ramped to a near-white and
       the discs vanished into the white body; the foot has to be visibly darker
       than the surface the disc stands on. All controls are one size and differ
       by material only. The ring pulse is a glow that fades out, not a line
       that grows. The minimized pill is the tab strip's anatomy too: a lit
       trough with the live call inside as the selected tab, which is what parts
       it from the blue header it sits on. Chosen on the prototype after two
       rounds; the states it was checked against are in the design artifact. */
    --call-ground: var(--bg-page);
    --call-inset: calc(var(--safe-area-top) + 12px) 12px calc(var(--safe-area-bottom-fixed) + 12px);
    --call-card-radius: 28px;
    --call-card-fill: var(--bg-white);
    --call-card-shadow: var(--shadow-float);
    --call-head-fill:
        var(--sheen-layer),
        linear-gradient(180deg, #4A90D9 0%, #1565C0 55%, #0F4C8F 100%);
    --call-body-fill: var(--bg-white);
    --call-body-pad: 14px 16px 18px;
    --call-job-display: flex;
    --call-role-display: block;
    --call-dot-display: block;
    --call-avatar-shadow: 0 0 0 6px rgba(255,255,255,.18), 0 0 0 14px rgba(255,255,255,.07), 0 10px 26px rgba(13,33,55,.4);
    --call-pulse-ring: none;
    --call-pulse-glow: rgba(255,255,255,.32);
    --call-status-margin: 14px;
    --call-status-height: 34px;
    --call-status-pad: 6px 16px;
    --call-status-fill: var(--header-tabs-fill);
    --call-status-ink: var(--blue-primary);
    --call-status-size: 13.5px;
    --call-status-weight: 700;
    --call-status-shadow: 0 1px 2px var(--btn-contact), 0 4px 12px -2px rgba(13,33,55,.3);
    --call-status-fault-fill: var(--red-light);
    --call-status-fault-ink: var(--red);
    --call-disc: linear-gradient(180deg, var(--bg-white), var(--bg-sunken));
    --call-disc-ink: var(--blue-primary);
    --call-disc-shadow:
        inset 0 1px 0 var(--lit-overlay-top),
        inset 0 -1px 0 var(--btn-contact),
        0 1px 2px var(--btn-contact),
        0 4px 10px -2px rgba(13,33,55,.22),
        0 10px 20px -8px rgba(13,33,55,.26);
    --call-disc-hover: linear-gradient(180deg, var(--bg-white), var(--blue-pale));
    --call-disc-press-shadow: inset 0 2px 4px rgba(13,33,55,.14), 0 1px 2px var(--btn-contact);
    --call-disc-on: var(--btn-fill-primary);
    --call-disc-on-ink: #fff;
    --call-disc-on-shadow: var(--btn-shadow-primary-rest);
    --call-disc-on-hover: var(--btn-fill-primary);
    --call-disc-on-hover-shadow: var(--btn-shadow-primary-hover);
    --call-disc-on-press-shadow: var(--btn-shadow-primary-press);
    --call-fault-fill: linear-gradient(180deg, var(--red-light), color-mix(in srgb, var(--red-light) 85%, var(--red)));
    --call-fault-ink: var(--red);
    --call-fault-shadow: inset 0 1px 0 var(--lit-overlay-top), inset 0 -1px 0 var(--btn-contact), 0 1px 2px var(--btn-contact), 0 4px 10px -2px rgba(239,68,68,.3);
    --call-fault-hover: var(--call-fault-fill);
    --call-fault-press: var(--call-fault-fill);
    --call-end-fill: var(--btn-fill-danger);
    --call-end-shadow: var(--btn-shadow-danger-rest);
    --call-end-hover-shadow: var(--btn-shadow-danger-hover);
    --call-end-press-shadow: var(--btn-shadow-danger-press);
    --call-accept-fill: var(--btn-fill-success);
    --call-accept-shadow: var(--btn-shadow-success-rest);
    --call-accept-hover-shadow: var(--btn-shadow-success-hover);
    --call-accept-press-shadow: var(--btn-shadow-success-press);
    --call-action-hover: var(--btn-transform-hover);
    --call-action-press: var(--btn-transform-press);
    --call-label-ink: var(--text-muted);
    --call-label-warn-ink: var(--text-primary);
    --call-minimize-top: 14px;
    --call-minimize-fill: var(--call-disc);
    --call-minimize-ink: var(--blue-primary);
    --call-minimize-shadow: var(--call-disc-shadow);
    --call-minimize-hover: var(--call-disc-hover);
    --call-minimize-press: var(--call-disc);
    --call-bubble-fill: var(--header-tabs-fill);
    --call-bubble-pad: 3px;
    --call-bubble-shadow: var(--shadow-float);
    --call-bubble-main-pad: 0 14px 0 12px;
    --call-bubble-main-fill: var(--btn-fill-primary);
    --call-bubble-main-ink: #fff;
    --call-bubble-main-shadow: 0 1px 2px var(--btn-contact), 0 4px 12px -2px var(--btn-primary-shadow);
    --call-bubble-main-hover: var(--btn-transform-hover);
    --call-bubble-main-hover-shadow: var(--btn-shadow-primary-hover);
    --call-bubble-main-press: var(--btn-transform-press);
    --call-bubble-main-press-shadow: var(--btn-shadow-primary-press);
    --call-bubble-dot-halo: 0 0 0 3px rgba(74,222,128,.3);
    --call-bubble-fault-fill: var(--red-light);
    --call-bubble-fault-ink: var(--red);
    --call-bubble-fault-shadow: 0 1px 2px var(--btn-contact);
    --call-bubble-end-shadow: 0 1px 2px var(--btn-contact), 0 2px 6px var(--btn-danger-shadow);
    --call-bubble-end-hover: var(--btn-transform-hover);
    --call-bubble-end-press: var(--btn-transform-press);

    --btt-fill: var(--blue-pale);
    --btt-border-color: transparent;
    --btt-shadow: var(--shadow-float);
    --btt-on-fill: var(--brand-fill);
    --btt-on-border-color: transparent;
    --btt-press-transform: translateY(1px) scale(0.96);

    /* ICON TILE and ICON CONTROL. Two rules instead of twenty: a tile HOLDS an
       icon, so it takes the thumb step; a control you PRESS takes the circle
       the theme already gives every other icon button.

       --tile-radius IS the thumb step, expressed as a proportion rather than as
       18px, and that is not a dodge. These tiles run 32px to 60px, and CSS
       clamps a radius to half the box: 18px on a 32px tile renders at 16px,
       which is a circle, and a circle is the shape this theme reserves for a
       control you press. 38 percent gives 12px at 32, 18px at 48 (the thumb
       step exactly, where most of them sit) and 21px at 56, so every tile reads
       as the same rounded square whatever size the page made it. Same failure
       family as the nav indicator note above, and the same fix: stop naming a
       pixel where the box size decides what that pixel means. */
    --tile-radius: 38%;
    --icon-btn-radius: var(--r-circle);

    /* ── STEPPER RAIL (Detail's "Progres Status" strip) ─────────────────────
       Capsule-only, for the reason the --chip-radius note above gives: the
       classic literals belong to this one component, so they stay in
       Detail.razor.css as the var() fallback rather than being restated here
       as if they were a shared step.

       The strip used to bleed out to the card's border box with a -14px
       margin and clip its scroller square against it. On a 24px card corner
       that reads as a half dot sitting outside the card, because the card
       rounds away from under it and nothing clips the scroller to the curve.
       So it becomes a RAIL INSIDE the card: inset to the card's padding edge,
       pill-rounded, clipping its own content. A dot scrolling out is now cut
       by the rail's own curve, which is the same fix --track-overflow is for
       the category trough. --step-rail-fade retints the two edge gradients to
       the rail's fill for the same reason --track-fade exists: they used to
       fade to the card's white, which is not what is behind the dots any more.

       The fill is --bg-sunken, not the trough's --bg-raised. A rail is a
       groove the progress runs ALONG, and in light mode --bg-raised is #FFFFFF
       with nowhere to go above a white card, so a raised rail would have to be
       separated by a shadow it does not earn sitting inside another card. */
    --step-rail-margin: 10px 0 0;
    --step-rail-fill: var(--bg-sunken);
    --step-rail-radius: var(--r-pill);
    --step-rail-fade: var(--bg-sunken);
    --step-rail-overflow: hidden;

    /* MARKER. A tile by the rule two blocks up: it holds an icon and is never
       pressed, so it takes the thumb step and not the circle this theme
       reserves for a control. Done and current take the success button's own
       fill, so a completed step reads as the same green material as the button
       that completed it. Pending becomes a plain white tile with its outline
       dropped, because the rail behind it now does the separating; the border
       stays declared at 2px in both themes so the box never changes size.

       The ring on the current step stays a flat 3px spread with no blur and no
       offset: the rail clips at 8px of track padding, so a glow with a 8px
       blur and a 4px offset would be sliced off at the rail's edge. */
    --step-dot-radius: var(--tile-radius);
    --step-dot-fill: var(--btn-fill-success);
    --step-dot-shadow: 0 1px 2px var(--btn-contact);
    --step-dot-ring: 0 0 0 3px var(--btn-success-shadow), 0 1px 2px var(--btn-contact);
    --step-dot-pending-fill: var(--bg-white);
    --step-dot-pending-edge: transparent;

    /* ── CARD: "floating + strip lepas" ─────────────────────────────────────
       The role token is repointed at the deep layered drop the floating header
       and nav already carry, so a card becomes the same kind of object as the
       chrome above and below it instead of being outlined on the page between
       them. This reaches every card in the app in one move, which is the point:
       36 stylesheets read --shadow-card and none of them needs editing.

       It also drops the `0 0 0 1px` ring that stack ended with. That ring is a
       drawn edge, and drawn edges are the one thing this theme has removed
       everywhere else.

       The trough must NOT follow: it is a container, not a card, and a floating
       trough under a floating card is two objects fighting. It is repointed at
       --shadow-hairline above, which is what --shadow-card used to be. */
    --shadow-card: var(--shadow-float);
    --card-hover-shadow-base: 0 2px 6px rgba(13,33,55,.07), 0 10px 24px -6px rgba(13,33,55,.16);

    /* "Strip lepas": the availability bar stops being welded to the card's edge
       and becomes a capsule floating inside it. This is the same move already
       made in the bottom nav, where a 3px bar clamped to the top edge became a
       pill, and for the same reason: a square-ended bar held against an edge is
       the one shape this language has no room for. On a 24px radius it is also
       wrong geometrically, because a straight full-height bar cuts across the
       corner the card rounds away.

       The strip moving inward is what costs the extra 10px of left padding. */
    --strip-inset: 16px auto 16px 12px;
    --strip-radius: var(--r-pill);
    --strip-gutter: 10px;

    /* Tag: "lit". Same tint, with light falling from above, so the marker is
       made of the same material as everything else here. A sheen cannot be
       added to a white card (there is nothing above #FFFFFF), but at 17px on a
       tinted fill it has room to read. */
    --tag-fill: linear-gradient(180deg, var(--blue-pale-top), var(--blue-pale-bottom));
    /* The same lighting for a badge of any colour: a translucent layer over the
       badge's own tint, so green, red, orange, yellow and muted all read as the
       material --tag-fill gives the blue tag, without a top and bottom stop per
       colour. The stops are themed in theme.css and use currentColor for the
       ink, which is why this is a layer the badge stacks over its fill rather
       than a fill of its own. */
    --badge-lit: linear-gradient(180deg, var(--lit-overlay-top), var(--lit-overlay-bottom));

    /* Monogram avatar: the same material as a filled button. The two stops
       run bottom to top, dark at the foot and light at the crown (0deg is "to
       top", and AvatarFill.cs writes the dark stop first), and --sheen-layer
       is laid over them exactly as it is over a pill. The diagonal was the
       last 135deg fill left on Beranda once the buttons went to 180: a person's
       hue still identifies them, but the light now falls on it from the same
       direction as on everything around it. The sheen is white at 20 percent
       for the top sixth of the disc and gone by the middle, so the initials,
       which sit in the middle, keep the contrast the generator measured. */
    --avatar-angle: 0deg;
    --avatar-sheen: var(--sheen-layer);

    /* Radius for the chips and tags that still hold a literal. Capsule-only,
       same reason as --track-scroll-pad: their classic values differ per page
       (6, 8, 12, 16, 20px) and no single shared value can carry them all, so
       each component keeps its own literal as the var() fallback. */
    --chip-radius: var(--r-pill);
    --tag-radius: var(--r-tag);
    /* Room under a row of chips that scrolls sideways. overflow-x: auto clips the
       vertical axis too, so a chip's resting edge shadow and a chosen chip's lift
       are cut off flat at the container's bottom edge unless the scroller reserves
       the space inside itself. 12px clears --shadow-card and the 12px falloff of
       --cell-on-shadow. Capsule-only: classic's chips paint no shadow at all, so
       adding the space there would only open a gap. */
    --chip-row-pad-bottom: 12px;
    /* Same pattern for the cards and control groups whose classic radius is
       drift rather than a decision: 12, 14 and 20px all over the app, next to
       the 16px the scale calls a card. Capsule pulls every one of them onto
       the scale; classic keeps its own literal as the fallback, so none of
       that drift is touched. */
    --card-radius: var(--r-card);
    /* And for the two clickable cards whose hover cue was hand-copied from an
       older, weaker value (0 4px 12px rgba(0,0,0,.06), no ring). Capsule
       unifies them with every other card; classic keeps what it has. */
    --card-hover-lift: var(--card-hover-transform);
    --card-hover-edge: var(--card-hover-shadow);

    --btn-radius-lg: 999px;
    --btn-radius-md: 999px;
    --btn-radius-sm: 999px;
    /* A circle, not a rounded square: it sits beside capsule siblings in the
       action bar, and a squarish icon button next to a pill reads as a mistake. */
    --btn-radius-icon: 50%;
    --btn-radius-social: 999px;
    /* Wider than classic because at radius 999px the corners eat into the usable
       width, and a label set at classic's padding sits tight against the curve. */
    --btn-pad-lg: 12px 22px;
    --btn-pad-md: 8px 16px;
    --btn-pad-sm: 6px 14px;

    /* Two layers. The lower one is the fill, running 180deg rather than classic's
       135deg because light falls from above and on a pill the top edge is the lit
       one; a diagonal fights the highlight instead of agreeing with it.

       The upper layer IS the highlight, and it is a gradient stop rather than a
       1px inset shadow on purpose. This app runs zoom: 1.1 on <html>, so a 1px
       feature renders as 1.1 physical pixels and smears across two rows as a pale
       band — measured, an inset version put a #85C5F8 row against a #2A99F2 fill,
       which read as a border drawn around the pill. A gradient stop has no pixel
       size and survives any display scale. Same failure family as the AppHeader
       hairline note; do not convert it back to an inset. */
    --btn-fill-primary:
        var(--sheen-layer),
        linear-gradient(180deg, var(--btn-primary-end), var(--btn-primary-start));
    --btn-fill-success:
        var(--sheen-layer),
        linear-gradient(180deg, var(--btn-success-end), var(--btn-success-start));
    --btn-fill-danger:
        var(--sheen-layer),
        linear-gradient(180deg, var(--btn-danger-end), var(--btn-danger-start));

    /* A tight contact shadow anchors the pill, two coloured layers carry the
       glow. Press swaps the drop for an inset so the button visibly sinks — the
       state classic never had, and the main reason it felt flat. */
    --btn-shadow-primary-rest:  0 1px 2px var(--btn-contact), 0 3px 8px var(--btn-primary-shadow), 0 8px 20px -4px var(--btn-primary-shadow);
    --btn-shadow-primary-hover: 0 2px 4px var(--btn-contact), 0 6px 14px var(--btn-primary-shadow), 0 14px 28px -4px var(--btn-primary-shadow);
    --btn-shadow-primary-press: inset 0 2px 6px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.10), 0 1px 1px var(--btn-contact);
    --btn-shadow-success-rest:  0 1px 2px var(--btn-contact), 0 3px 8px var(--btn-success-shadow), 0 8px 20px -4px var(--btn-success-shadow);
    --btn-shadow-success-hover: 0 2px 4px var(--btn-contact), 0 6px 14px var(--btn-success-shadow), 0 14px 28px -4px var(--btn-success-shadow);
    --btn-shadow-success-press: inset 0 2px 6px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.10), 0 1px 1px var(--btn-contact);
    --btn-shadow-danger-rest:   0 1px 2px var(--btn-contact), 0 3px 8px var(--btn-danger-shadow), 0 8px 20px -4px var(--btn-danger-shadow);
    --btn-shadow-danger-hover:  0 2px 4px var(--btn-contact), 0 6px 14px var(--btn-danger-shadow), 0 14px 28px -4px var(--btn-danger-shadow);
    --btn-shadow-danger-press:  inset 0 2px 6px rgba(0,0,0,0.28), inset 0 1px 0 rgba(255,255,255,0.10), 0 1px 1px var(--btn-contact);

    --btn-transform-hover: translateY(-1px);
    --btn-transform-press: translateY(1px) scale(0.985);

    /* Classic's first FAB shadow layer is `0 0 0 4px`: zero blur, 4px spread, so
       a hard flat ring rather than a shadow. That is what reads as an outline
       drawn around the FAB once the other buttons lose theirs, so the blurred
       coloured layers do the separating instead.

       The outer focus ring stays, deliberately. design-discipline §2 records that
       filled buttons carry NO focus ring and calls it the app's weak spot against
       WCAG 2.4.11; the FAB is one of the few controls that never had that problem,
       so matching the others here would be a downgrade, not consistency.
       More press scale than a capsule (0.96 vs 0.985) because a 54px circle needs
       more travel than a wide pill to read as the same movement. */
    --fab-fill:
        var(--sheen-layer),
        linear-gradient(180deg, var(--btn-primary-end), var(--btn-primary-start));
    --fab-shadow-rest:  0 1px 2px var(--btn-contact), 0 4px 10px var(--btn-primary-shadow), 0 10px 24px -4px var(--btn-primary-shadow);
    --fab-shadow-hover: 0 2px 4px var(--btn-contact), 0 8px 16px var(--btn-primary-shadow), 0 16px 32px -4px var(--btn-primary-shadow), 0 0 0 4px rgba(var(--blue-primary-rgb), 0.40);
    --fab-shadow-press: inset 0 2px 6px rgba(0,0,0,0.28), 0 1px 2px var(--btn-contact);
    --fab-transform-hover: translateY(-1px);
    --fab-transform-press: translateY(1px) scale(0.96);

    /* ── HEADER — "Floating" ───────────────────────────────────────────────
       The header detaches into a 24px rounded card inset from the edges, sitting
       on the page ground with a coloured glow under it: the sibling of a floating
       control at the other end of the screen, and the most committed reading of
       the capsule idea.

       THE SAFE AREA MOVES OUTSIDE THE CARD. Classic swallows it in padding-top
       because the band reaches the top of the screen; here the card must start
       BELOW the status bar, so the inset carries it and the padding goes back to a
       normal 14px. Get this wrong and the card either slides under the notch or
       grows a 40px empty strip inside itself.

       Consequences worth knowing before shipping this one:
       - The page ground shows behind the status bar, so the app no longer opens
         on a full-bleed brand block. That is the option's main cost.
       - App.razor's theme-color meta still follows --bg-page, which is now
         correct for this theme and was arguably wrong for classic.
       - The --header-edge shim is dropped: it fixes a straight edge and cannot
         follow a corner radius. The card's own shadow separates it instead. */
    --header-fill:
        var(--sheen-layer),
       /* mid FIRST, then start: lighter at the top, deeper at the bottom.
          It ran the other way and that is what made the header read flat while
          the buttons popped. Measured down the middle, the blue channel climbed
          94 at the top edge to 183 at the bottom, so the surface was lit from
          BELOW, and the sheen layer above it was fighting its own base instead
          of agreeing with it. Every other filled brand surface here runs
          light-to-dark downward; the header was the only one inverted, and it
          inherited that from classic's 145deg diagonal without the direction
          being rechecked.
          Lighter than a straight mid-to-start ramp, which put the darkest value
          at the BOTTOM where it carries the most visual weight and made the bar
          too heavy for a light theme. This puts the highlight in the top 42% and
          holds --header-grad-mid for the rest, which is how a lit surface
          actually behaves: a bright band near the top, body colour below.

          THE 42% IS A CONTRAST FLOOR, not a taste call. The title sits at about
          half the header's height. Ramping --header-grad-end all the way down
          would leave it on roughly #3079CD, which measures 4.39:1 against white
          and fails the 4.5:1 AA floor for text this size. Holding
          --header-grad-mid from 42% puts the title on #1565C0 instead, which
          measures 5.69:1. Do not stretch the stop past the title. */
        linear-gradient(180deg, var(--header-grad-end) 0%, var(--header-grad-mid) 42%);
    --header-ink: #fff;
    --header-blur: none;
    --header-radius: 24px;
    /* Auto side margins, not a fixed 12px, because the card is capped below and
       has to centre once the viewport is wider than the cap. */
    --header-inset: calc(var(--safe-area-top) + 10px) auto 0;
    /* A floating card must not stretch. Left uncapped it spanned the whole
       viewport at desktop width while its nav row stayed capped at 848, so the
       title sat marooned in the middle of a very wide blue slab — caught at
       1440 during the viewport sweep. 880 = the 848 content column plus the
       card's own 2x16 padding, so the card's inner edge lines up with the cards
       below it. The width term keeps a 12px gutter on phones, where the cap
       never binds. */
    --header-width: calc(100% - 24px);
    --header-max: 880px;
    --header-pad-top: 14px;
    --header-pad-x: 16px;
    --header-pad-bottom: 14px;
    /* Three layers, and the THIRD one is the scroll-edge fix, not decoration.
       Every page that pins this header is a 100dvh flex column: header on top,
       then .wallet-scroll / .detail-scroll and 27 more siblings taking the rest
       with overflow-y:auto. That scroller's top edge is a straight full-width
       line directly under this card, and content crossing it stops rendering at
       full opacity: a card body, a photo, one line of text, sliced square.
       The third layer is an opaque page-coloured wash that covers that line and
       decays downward, so content dissolves into the page instead of being cut.

       WHY THE SPREAD IS 64 AND THE OFFSET NEGATIVE, which looks wrong until you
       measure it. A shadow's blur softens EVERY edge, the sides included, so the
       wash arrives at the two 12px gutters beside the card already half faded. At
       spread 12 the coverage measured 0.33 alpha in the gutter against 0.81 in the
       middle: the fade stopped at the capsule's own width and the strips left and
       right of it kept their hard cut. Chromium's falloff reaches full strength
       about a whole blur radius inside the shadow rect, not half of one, so the
       rect has to start ~25px beyond the screen edge. Spread 64 puts it there and
       measures 0.78 at both screen edges against 0.81 in the middle: flat.
       The offset then buys the vertical geometry back. Spread inflates the box on
       all four sides, so 64 alone would push the wash 64px down the page; -49
       returns the rect's bottom edge to 15px below the card, which is the band
       that was tuned. Read them as one pair: offset = 15 - spread.

       ORDER IS LOAD-BEARING. Shadows paint first-listed on top, so the wash goes
       LAST and the two real shadows paint over it. Put the wash first, or paint
       it as a ::after on this element instead, and it covers the header's own
       drop shadow: measured at the rounded bottom corner, that turns a 7-unit
       luminance step at the card's edge into a 15-unit one, which is a HARDER
       line than the one being fixed. In this order the step measures 3.

       A mask on the scroller would be the textbook answer and it is not available
       to us: a mask applies to the element's whole rendered subtree, so it clips
       position:fixed descendants. Probed in Chromium on 2026-09-09, a fixed child
       inside a masked scroller disappears entirely, and those scrollers hold every
       photo lightbox (Detail, Posting, Contact, VerifikasiKtp, ProfilTukang) plus
       the ListboxMenu backdrop on JasaForm. A shadow paints without clipping
       anything, so nothing inside the page changes.

       Classic keeps two layers and no wash: its band is full-bleed with a straight
       edge and already answers that edge with the 1px shim in its own
       --header-edge. Two treatments on one edge would fight. */
    --header-edge:
        0 2px 6px rgba(13,33,55,0.14),
        0 10px 26px -8px var(--btn-primary-shadow),
        0 -49px 22px 64px var(--bg-page);
    --header-ornament: none;
    /* Circles, matching --btn-radius-icon. These sit next to pill buttons; a
       12px rounded square among them was the tell that the header had not been
       brought over. */
    --header-control-radius: 50%;
    --header-control-bg: rgba(255, 255, 255, 0.12);
    --header-field-radius: 999px;

    /* ── TOGGLE ── "Pil berlabel" ─────────────────────────────────────────────
       Three things change, and each fixes something classic got wrong here.

       The OFF state becomes a groove: a sunken tonal surface instead of a flat grey
       bar. That is the only reading of "off" that carries meaning rather than
       colour, and it is why this survives dark mode, where classic's --border track
       sat a few values away from the card behind it. --btn-contact does the shading
       because it is already the theme's contact colour in both modes; a new literal
       would have needed its own light and dark branch.

       The ON state becomes --btn-fill-primary, the exact same sheen-over-gradient
       every other filled brand control uses. A flat --tab-active-bg next to a
       gradient button was the tell that the toggle belonged to a different theme.

       And the track carries a WORD. That is not decoration: it takes the state off
       colour alone, which is the one thing a toggle most needs and the reason the
       track grows from 48 to 58. Travel grows with it, 30 instead of 20, so the
       thumb still lands 3px from the far edge.

       The press is a real one now: the thumb shrinks to .92 and loses its lift, so
       the control sinks the way every capsule button does. */
    --toggle-w: 58px;
    --toggle-travel: 30px;
    --toggle-h: 28px;
    --toggle-thumb-size: 22px;
    --toggle-inset: 3px;
    --toggle-radius: var(--r-pill);
    --toggle-off-fill: var(--bg-sunken);
    --toggle-off-edge: inset 0 1px 3px var(--btn-contact), inset 0 2px 6px var(--btn-contact);
    --toggle-on-fill: var(--btn-fill-primary);
    --toggle-on-edge: inset 0 1px 2px rgba(0,0,0,0.18);
    --toggle-thumb-fill: #fff;
    --toggle-thumb-shadow: 0 1px 2px var(--btn-contact), 0 3px 8px var(--btn-primary-shadow);
    --toggle-thumb-shadow-press: 0 1px 2px var(--btn-contact);
    --toggle-press-scale: 0.92;
    /* One cue for hover, focus and press. The movement is the press signal, so the
       ring no longer has to thicken to say something the thumb already says. */
    --toggle-cue: 0 0 0 4px var(--btn-primary-shadow);
    --toggle-cue-press: 0 0 0 4px var(--btn-primary-shadow);
    --toggle-word-display: flex;
    --toggle-word-on-color: #fff;
    --toggle-word-off-color: var(--text-muted);

    /* ── SLIDER ── "Ujung menebal" ────────────────────────────────────────────
       The rail thins to 10px and the knob becomes a 22px brand capsule carrying the
       SAME fill as the bar, so the knob is not a second object sitting on the bar:
       it is the bar's end, swollen. That is where the harmony comes from here, and
       it is why the knob reads as one body with the fill even though it is twice as
       tall.

       FOUR THINGS MAKE IT POP, and they are worth naming because dropping any one of
       them flattens it back out:

         1. The knob carries the full three-layer button rest shadow, contact plus two
            glows, so it lifts clear of the rail instead of resting on it.
         2. It is 2.2x the rail's height. The size difference IS the pop; a thicker
            rail would only reduce it.
         3. The rail is a sunken groove, so the lit knob sits against a recess rather
            than against another lit surface.
         4. The white dot has a contact shadow of its own, adding a SECOND elevation
            step inside the knob. Without it the dot reads as a flat sticker printed
            on the cap and the whole thing loses its depth.

       Deliberately kept thin: this control is a tool for picking a radius, not a
       feature of the page, so the rail is the thinnest of everything considered while
       the knob keeps all the elevation. */
    --slider-h: 10px;
    --slider-knob: 22px;
    --slider-radius: var(--r-pill);
    --slider-track-fill: var(--bg-sunken);
    --slider-track-edge: inset 0 1px 3px var(--btn-contact), inset 0 2px 6px var(--btn-contact);
    --slider-fill: var(--btn-fill-primary);
    --slider-fill-edge: 0 1px 2px var(--btn-contact), 0 2px 6px var(--btn-primary-shadow);
    --slider-knob-fill: var(--btn-fill-primary);
    --slider-knob-shadow: var(--btn-shadow-primary-rest);
    --slider-knob-shadow-hi: var(--btn-shadow-primary-hover), 0 0 0 5px var(--btn-primary-shadow);
    --slider-knob-shadow-press: 0 1px 2px var(--btn-contact), 0 0 0 5px var(--btn-primary-shadow);
    --slider-knob-press-scale: 0.94;
    --slider-dot-display: block;
    --slider-dot-size: 8px;
    --slider-dot-fill: #fff;
    --slider-dot-shadow: 0 1px 2px var(--btn-contact), 0 1px 3px rgba(0,0,0,0.22);
}

/* ══════════════════════════════════════════════════════════════════════════════
   ICON  (Icon.razor + IconDefs.razor)

   One silhouette referenced three times: the body in currentColor, a white
   gradient on top for the lit side, a dark gradient at the bottom for the side
   turned away. Two blurred shadows underneath. That is the whole recipe, and it
   is the button's recipe at icon scale.

   THE BODY IS currentColor ON PURPOSE. An earlier draft baked a fixed blue ramp
   into every glyph, which meant each of the 689 call sites that wanted a grey,
   white or red icon needed a ramp of its own. Putting the light falloff in the
   sheen and shade layers instead means the icon keeps inheriting its colour from
   whatever rule already coloured it, so a muted field icon stays muted and a
   header icon stays white with no per-site work.

   BOTH BLOCKS, like the toggle, and here that includes WHICH SHAPE IS DRAWN.
   The two themes do not disagree about an icon's colour or depth, they disagree
   about what an icon IS: classic draws a line, capsule draws a solid body with
   light on it. No token can express that, so Icon.razor emits both sets and
   these blocks hide one. That is what makes flipping UiTheme.Active back to
   Classic give the old interface, instead of new solid icons sitting on old flat
   buttons. WebAdmin and WebAgent used to carry no data-ui attribute at all and
   so took the classic set whole; since 2026-09-11 they render UiTheme.Active
   like the customer app, so the solid set reaches those portals too and the
   revert takes them back with everything else.

   The hidden set costs four <use> elements per icon that never paint and never
   take part in layout. That was judged cheaper than a runtime branch, which
   would have to reach the theme from inside a component rendered 671 times.

   `display: none` hides a set; `none` on fill and stroke is what hides a single
   LAYER (see .ic-flat). Both leave the <use> in the tree, so nothing reflows
   when the theme changes.

   No display or vertical-align is set on .ic itself. It renders into 671 places
   that already position their own svg, and changing the default box would move
   every one of them.
   ══════════════════════════════════════════════════════════════════════════════ */
:root {
    /* Classic: the line set, drawn flat, exactly as the old interface had it. */
    --icon-line-display: inline;
    --icon-solid-display: none;
    --icon-sheen: none;
    --icon-shade: none;
    --icon-shadow: none;
}

:root[data-ui="capsule"] {
    --icon-line-display: none;
    --icon-solid-display: inline;
    --icon-sheen: var(--icon-sheen-src);
    --icon-shade: var(--icon-shade-src);
    --icon-shadow: drop-shadow(0 0.5px 0.5px var(--icon-contact-src))
                   drop-shadow(0 1.5px 2.5px var(--icon-contact-soft-src));
}

.ic {
    overflow: visible;
    filter: var(--icon-shadow);
}

.ic .ic-line { display: var(--icon-line-display); }

.ic .ic-base,
.ic .ic-sheen,
.ic .ic-shade { display: var(--icon-solid-display); }

.ic .ic-base { fill: currentColor; stroke: currentColor; }
.ic .ic-sheen { fill: var(--icon-sheen); stroke: var(--icon-sheen); }
.ic .ic-shade { fill: var(--icon-shade); stroke: var(--icon-shade); }

/* The escape hatch for the rare spot where a raised icon fights what is behind
   it. Flat="true" on the component, not a page-level override. It drops the
   light and the shadow but keeps whichever set the theme chose, so it stays a
   softening rather than a second theme. */
.ic-flat { filter: none; }
.ic-flat .ic-sheen,
.ic-flat .ic-shade { fill: none; stroke: none; }
