/* -----------------------------------------------------------------------------------
   ONE PRESENTATION LAYER.

   Everything this application draws -- the eleven hand-authored displays, their eleven
   dialogs, the command bar, the wall route, and the pages the renderer composes
   from the primitive catalog -- reads from the vocabulary declared in this file and from
   nothing else. There are three tiers and a page may only ever reach for the next one up:

     1. TOKENS (this block and the dark block below). Every colour, radius, control
        measure, spacing step and type step in the application is named here. A rule
        elsewhere in this file that writes a colour literal, a bare radius or a bare
        font-size is a bug, and `visual-system.test.ts` fails on it.
     2. PRIMITIVES (`.app-*`, declared once in "The shared primitive vocabulary" below).
        A surface, a control, a row, a list, a tag, a checkbox, a notice, a scroller, a
        dialog. Both the hand-authored displays and `view.js`'s renderer compose these.
     3. PANE RULES. What is genuinely this pane's own: its layout, its semantics, its
        one or two deliberate departures. A pane rule may position a primitive, give it
        a modifier, or zero one of its declarations. It may not restate one.

   The extension rule, for a new display and for a new registered primitive alike: build
   it out of tier 2, and if tier 2 has no word for what you need, ADD ONE THERE -- named,
   commented, and used by at least the two callers that wanted it -- rather than writing
   a lookalike in tier 3. See docs/architecture/shared-app-layout.md, "One presentation layer".

   The palette is one wallpaper (see "THE WALLPAPER" below) behind every pane, with equal
   cards floating on it -- no featured-surface wash and no "which pane am I on" colour cue on
   the card itself beyond each destination's own icon colour. The one accent is teal,
   reserved for the thing you press or the thing that is selected; the three ICON INKS
   (--icon-gold/-blue/-teal) are used only to tell one destination's nav icon from another.
   :root is the LIGHT preference: a pale blue-grey wallpaper, white cards, dark slate ink for
   text set on the wallpaper and a deep teal accent that clears contrast on those cards. The
   dark block below is THE APPROVED, default appearance -- see "Appearance default + migration"
   in chat.js and index.html's boot script for how a device actually lands there.
   ----------------------------------------------------------------------------------- */

/* DM Sans (body) and Manrope (display/headings), both Open Font License, self-hosted so the
   app never depends on Google Fonts at runtime. Both files are the Google latin-subset
   variable fonts (weights 400-500, the only two this design uses -- see --w-* below) and
   both declare the same unicode-range Google publishes for that subset, so a glyph outside
   it falls through to the system fallback in --font-body/--font-display instead of showing
   a tofu box. font-display: swap shows the fallback immediately rather than a blank paint. */
@font-face {
  font-family: 'DM Sans';
  src: url('/fonts/dm-sans-latin.woff2') format('woff2');
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

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

:root {
  color-scheme: light;
  /* Full-screen surfaces pad by these so content clears the status bar and home indicator; tests override them. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --fullscreen-pad: var(--safe-top) var(--safe-right) var(--safe-bottom) var(--safe-left);
  /* Engines without dvh must still resolve the full-height var fallbacks. */
  --vh100: 100vh;
  /* Tallest a centred dialog may be: the viewport minus the larger inset on both sides, so it stays centred and clear of the status bar and home indicator. */
  --dialog-max-height: calc(var(--vh100) - 2 * max(var(--safe-top), var(--safe-bottom)) - 2 * var(--space-xl));
  --font-body: 'DM Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-display: 'Manrope', var(--font-body);
  /* THE WALLPAPER. One fixed backdrop behind every pane in both themes -- see .app-wallpaper
     near the shared primitives below. Light is a plain pale field: the wave tokens exist so
     the layer's markup is shared, and they are transparent in both themes. The dark block
     restates the deep navy field. */
  --wall-base-radial: #f4f7fb;
  --wall-grad-1: #f4f7fb;
  --wall-grad-2: #f4f7fb;
  --wall-grad-3: #f4f7fb;
  --wave-gold-1: transparent;
  --wave-gold-2: transparent;
  --wave-gold-3: transparent;
  --wave-teal-1: transparent;
  --wave-teal-2: transparent;
  --wave-teal-3: transparent;
  --wave-blue-1: transparent;
  --wave-blue-2: transparent;
  /* The launcher and dock tiles follow the theme: a white-to-cool-grey ground here (#ffffff
     to #f4f7fb, the page's cool tint) with the destination ink colours, and the dark block
     restates the dark bevelled ground with its own pale inks. */
  --icon-tile-1: #ffffff;
  --icon-tile-2: #f4f7fb;
  /* The tile's gradient ground, shared by the launcher and dock icons via .app-tile-icon. */
  --icon-tile-ground: linear-gradient(145deg, var(--icon-tile-1), var(--icon-tile-2) 80%);
  /* How much of the tile's own --app-ink the tinted ground mixes into each stop. */
  --icon-tile-tint: 9%;
  --icon-gold: #1e7f71;
  --icon-blue: #1f6cc7;
  --icon-teal: #1e7f71;
  /* The wallpaper's own overlay (a top-to-bottom darken in the dark theme) and the two
     waves' inset highlights. Light draws none of them: the field is already pale. */
  --wall-overlay-top: transparent;
  --wall-overlay-bottom: transparent;
  --wave-gold-shadow: transparent;
  --wave-teal-shadow: transparent;
  /* Text set directly on the wallpaper, not on a card: a page heading (--ink-heading) and a
     quieter caption (this token) are dark slate on the pale field, so no text-shadow is
     needed. */
  --ink-on-wall-muted: #5b6878;
  --ink-on-wallpaper: var(--ink-heading);
  --muted-on-wallpaper: var(--ink-on-wall-muted);
  --scrim-on-wall: transparent;
  --text-shadow-on-wall: none;
  /* A secondary control set directly on the wallpaper, not on a card: a faint dark tint
     over the pale field. */
  --top-button-ground: rgb(38 51 63 / 0.06);
  --top-button-ground-hover: rgb(38 51 63 / 0.1);
  /* The Review/Emails switch's own track (.email-review-bar): a slate tint a little
     stronger than --top-button-ground, so the switch's selected pill (--panel-subtle)
     still reads as the lighter, "chosen" surface against it. */
  --switch-track-ground: rgb(38 51 63 / 0.12);
  /* --page is the flat fallback for wherever the fixed wallpaper layer cannot reach (behind a
     dialog's own scrim maths, print, a canvas too small to keep it in view) -- --wall-grad-3,
     literally, the colour the wallpaper itself settles to at its own lower edge, so a fallback
     never reads as a seam against the real thing. Card headings sit on the wallpaper, not on
     --panel-subtle, so they read in --ink-heading (dark slate here, white in the dark theme)
     rather than --ink. */
  --page: #f4f7fb;
  --ink-heading: #1c2738;
  /* A faint slate chip for a control drawn straight on the wallpaper (the Agent page's Back). */
  --chip-on-wall: rgb(38 51 63 / 0.08);
  /* The light preference: white cards on the pale wallpaper. */
  --panel-subtle: #ffffff;
  --panel-inset: color-mix(in srgb, var(--panel) 55%, var(--panel-subtle));
  /* The persistent player bar: a cool blue-grey tint of the page, so the bar reads as a
     distinct control rather than a white slab beside the composer pill. Its edge and the
     unplayed part of its progress line step a little darker. */
  --player-surface: color-mix(in srgb, var(--page) 16%, var(--panel-subtle));
  --player-line: color-mix(in srgb, var(--page) 30%, var(--line-strong));
  --player-progress-track: color-mix(in srgb, var(--page) 22%, var(--panel-subtle));
  /* Sunken and hover both read as one step down from --panel-subtle, same role as always. */
  --panel: #f4f7fb;
  --surface-raised: var(--panel-subtle);
  --surface-high: var(--panel);
  --ink: #1c2738;
  --muted: #5b6878;
  --faint: color-mix(in srgb, var(--muted) 68%, var(--panel-subtle));
  --line: #edf1f6;
  --line-strong: #e3e9f1;
  /* The one action/selection colour: a deep teal, chosen so it clears 4.5:1 as TEXT on the
     light card as well as working as a fill with white ink. Hover darkens further, toward
     black, since it is already the darker of the pair (the dark theme's own hover brightens
     instead). */
  --accent: #1e7f71;
  --accent-hover: color-mix(in srgb, var(--accent) 85%, black);
  --on-accent: #ffffff;
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--panel-subtle));
  --error: #b42318;
  --code: #efefe9;
  --accent-ink: color-mix(in srgb, var(--accent) 55%, var(--ink));
  /* The dock's own current-item ink and ground (see .dock-button[aria-current]): the teal
     accent as text on the card, and a filled teal tile with its own ring for the current item. */
  --dock-ink-current: #1e7f71;
  --dock-current-ground: #32a091;
  --dock-current-ring: #32a091;
  /* The Calendar's Now marker sits on the wallpaper, not on a card, so it takes the on-wall
     heading ink (9.0:1 on the slate field). */
  --now-ink: var(--ink-heading);
  /* THE DESTINATION IDENTITY TOKENS. Each destination's strong icon colour (--accent-X) and its
     tint (--wash-X) come from the logo's own palette: orange for Meals and Inbox, purple for
     Music, green for Grocery, blue for Calendar. The rest are neutral slate. Library, Settings
     and Photos have no tint. */
  --accent-meals: #c25a0a;
  --wash-meals: #fff0e3;
  --accent-grocery: #1e8c3a;
  --wash-grocery: #e5f6e9;
  --accent-household: #4a5668;
  --wash-household: #eef2f7;
  --accent-podcasts: #4a5668;
  --wash-podcasts: #eef2f7;
  --accent-music: #6a2fe0;
  --wash-music: #f1ebff;
  --accent-calendar: #1f6cc7;
  --wash-calendar: #e7f1fd;
  --accent-email: #c25a0a;
  --wash-email: #fff0e3;
  --accent-library: #4a5668;
  --accent-settings: #4a5668;
  --accent-photos: #4a5668;
  /* The logo's own colours, for the Today rainbow rule and nothing else; the dark theme
     inherits them unchanged. */
  --logo-purple: #7b3ff2;
  --logo-blue: #2b7fe0;
  --logo-cyan: #00a0e9;
  --logo-green: #05a54b;
  --logo-yellow: #f4b503;
  --logo-orange: #f57c1f;
  --logo-rainbow: linear-gradient(90deg, var(--logo-purple), var(--logo-blue), var(--logo-cyan), var(--logo-green), var(--logo-yellow), var(--logo-orange));
  /* Inline links in body text (email). */
  --link: #345ca7;
  /* Android reads these as the car_* colors (generated by scripts/android-design-tokens.mjs). */
  --car-panel: var(--panel-subtle);
  --car-row: color-mix(in srgb, var(--ink) 6%, var(--panel-subtle));
  --car-pane-bg: #ffffff;
  --car-pane-row: #f4f7fb;
  --car-gold: var(--accent);
  --car-on-gold: var(--on-accent);
  --car-teal: #0f6674;
  --car-ink: var(--ink);
  --car-muted: var(--muted);
  /* The chart surface is shared by the visual chart renderer and notice sparkline. */
  --chart-surface: #191814;
  --chart-ink: #8a8578;
  --chart-line: color-mix(in srgb, var(--chart-ink) 30%, var(--chart-surface));
  /* The Library's removal affordance keeps its own quiet warning border. */
  --library-note-ink: #93221f;
  --library-remove-border: #d9a39d;
  --shadow-float: 0 -4px 10px rgb(48 66 82 / 0.14);
  --shadow-sheet: 0 -6px 20px rgb(48 66 82 / 0.16);
  --shadow-keyboard-bar: 0 -1px var(--line-strong), 0 -8px 20px color-mix(in srgb, var(--ink) 14%, transparent);
  --scrim: rgb(38 51 63 / 0.34);
  /* A soft slate drop, so a card reads as gently raised off the wallpaper rather than
     flat-pasted onto it. Colour literals only, no radius/size token involved, so it needs no
     entry in the radius vocabulary below. */
  /* The 1px ring is the card's edge: a box-shadow, not a border, so .app-card's box size is unchanged. */
  --shadow-card: 0 0 0 1px var(--line-strong), 0 5px 15px rgb(48 66 82 / 0.09);
  /* The composer pill's own hairline highlight (see .composer-row): the reference's
     #ffffff0b, converted to rgb()/alpha the same way the wallpaper's own literals above
     were. Lighter than --shadow-card's inset alone, and with no drop shadow of its own --
     the pill sits inside the command bar's already-elevated surface, not on the wallpaper
     directly. */
  --shadow-inset-highlight: inset 0 1px rgb(255 255 255 / 0.043);
  /* The launcher tile icon's own three-layer shadow (see .app-tile-icon): two insets that
     read as a bevel plus a soft drop. Light: a white highlight on top, a faint dark inset
     below and a soft drop like --shadow-card's. */
  --shadow-tile-bevel:
    inset 0 1px 1px rgb(255 255 255 / 0.9),
    inset 0 -1px 1px rgb(48 66 82 / 0.1);
  --shadow-tile-icon: var(--shadow-tile-bevel), 0 6px 12px rgb(48 66 82 / 0.12);
  /* The dock's smaller tile: the same bevel under a shorter drop. */
  --shadow-tile-icon-dock: var(--shadow-tile-bevel), 0 3px 6px rgb(48 66 82 / 0.12);
  /* The same tile pressed (see .app-tile:active .app-tile-icon): the drop shortens and
     the lower inset drops out. */
  --shadow-tile-icon-active: inset 0 1px 1px rgb(255 255 255 / 0.9), 0 2px 5px rgb(48 66 82 / 0.12);
  /* Status colours: what they mean, they mean everywhere, never a category or an accent.
     --warn is amber, a hue the teal --accent does not share, and it clears 4.5:1 on
     --panel-subtle (see the dark block for its own figure). */
  --ok: #0d7a4a;
  --warn: #96600a;
  /* Person-slot category hues. Slot 4 is a cool slate, kept well apart from --accent and from
     the warm status colours, so a person's row does not read as the selected one. */
  --cat-1: #0f8f8a;
  --cat-2: #7a5ad0;
  --cat-3: #c2567f;
  --cat-4: #5b6b83;
  --cat-5: #2a5e3c;
  --cat-1-wash: color-mix(in srgb, var(--cat-1) 12%, var(--panel-subtle));
  --cat-2-wash: color-mix(in srgb, var(--cat-2) 12%, var(--panel-subtle));
  --cat-3-wash: color-mix(in srgb, var(--cat-3) 12%, var(--panel-subtle));
  --cat-4-wash: color-mix(in srgb, var(--cat-4) 12%, var(--panel-subtle));
  --cat-5-wash: color-mix(in srgb, var(--cat-5) 12%, var(--panel-subtle));
  --cat-border-mix: 35%;
  --wash-shared: #eef2f7;
  /* The card-list idiom's visible border, unchanged in role. */
  --card-line: var(--line-strong);
  /* An input/select/textarea's own ground: the light theme's already-sunken --panel reads
     fine as a field, so it just aliases; the dark block overrides this with the reference's
     own translucent-white field fill instead of repeating --panel there (see the dark
     block's own comment on this token for why a real fill and a translucent one are not
     interchangeable). */
  --field-fill: var(--panel);
  /* THE CARD GEOMETRY. The reference's own panel: a noticeably rounder corner and roomier
     padding than the old 10px/12px card, so a card reads as a deliberate object floating on
     the wallpaper rather than a bounded list row. --card-pad stays the single property most
     rules read (.app-card's own signature is `padding: var(--card-pad)`); the two halves below
     exist for the handful of rules that pad an axis on its own. */
  --card-radius: var(--radius-surface);
  --card-pad-y: 15px;
  --card-pad-x: 17px;
  --card-pad: var(--card-pad-y) var(--card-pad-x);
  --card-gap: 12px;
  /* THE RADIUS VOCABULARY. Six named roles, none of which scales with text size -- see
     visual-system.test.ts, which fails on a bare radius outside this list.
       --radius-nested   a small element inside another: a compact badge or status label, a small
                         thumbnail, a selected inner segment, an icon inset inside a tile
       --radius-control  buttons, inputs, selects, menu-item highlights
       --radius-tile     app tiles: dock icons, All Apps icons, app-selection tiles, device-type
                         icon containers. Equal to --radius-control today, but a separate role
       --radius-surface  cards, grouped lists, the composer, the mini-player, bubbles
       --radius-overlay  dialogs, sheets, the app's large floating frame panels
       --radius-pill     an explicit exception only: switch tracks, meters, progress bars, count
                         chips, round icon buttons
       --card-radius     alias of --radius-surface, kept because many rules read this name
       50%               a circle, written literally, because it is a shape not a size */
  --radius-nested: 6px;
  --radius-control: 10px;
  --radius-tile: 10px;
  --radius-surface: 16px;
  --radius-overlay: 20px;
  --radius-pill: 999px;
  /* Rundell OS card: work-grid panel, state dots, and the purple suggestion tint. */
  --agent-panel: rgba(244, 244, 240, 0.78);
  --agent-dot-amber: #e7a73f;
  --agent-dot-green: #55ad7a;
  --mic-fill: #32a091;
  --mic-ink: #ffffff;
  --agent-gold-tint: #f1ebff;
  --agent-gold-badge: #f1ebff;
  --agent-gold-ink: #6a2fe0;
  --agent-gold-kicker: #6a2fe0;
  --agent-page-row: rgba(247, 247, 243, 0.95);
  /* A QR's quiet zone is white in every theme. */
  --qr-ground: #ffffff;
  /* THE CONTROL MEASURES: phone/touch values at every width, including wide car view. */
  --control-min: 44px;
  --touch-min: 48px;
  --touch-floor: 48px;
  --control-pad-y: 0.25rem;
  --control-pad-x: 0.7rem;
  /* The ringed-circle checkbox (.app-check), sized to the reference's own 22px ring (from its
     24px predecessor). */
  --check-size: 22px;
  /* --control-line is given directly by the reference for the dark/approved theme (see the
     dark block); the light value keeps the established derivation -- a mix of --muted toward
     --line-strong -- so it re-centres on the new light --muted/--line-strong automatically. */
  --control-line: color-mix(in srgb, var(--muted) 64%, var(--line-strong));
  /* THE WEIGHT LADDER. Only two weights are used -- large Manrope
     display type reads at 400, everything that used to step up to 600/700 (a title, a control
     label, a section marker) now steps to 500. --w-display and --w-meta collapse onto --w-body
     and --w-title's own new values respectively; all four tokens stay, since every existing
     font-weight in the file points at one of them and nothing here restates a number. */
  --w-display: 400;
  --w-title: 500;
  --w-meta: 500;
  --w-body: 400;
  --keyline: 2.5px;
  /* List geometry: the phone's own side gutter widens to the reference's 17px; --page-gutter
     is the new, slightly roomier measure a page HEADER reads (21px, the reference's own
     `.gp-header` padding) now that a header sits on the wallpaper rather than in a bounded
     card. --list-lead/-gap are unchanged: the rail still has to hold `10:00 PM` whole. */
  --list-gutter: 17px;
  --page-gutter: 21px;
  --list-lead: 76px;
  --list-lead-gap: 12px;
  /* Half the difference between a checkbox's box and its 48px touch target. */
  --touch-slack: 13px;
  /* The semantic text-size tokens, set per preset by data-text-size (text-size.js); these
     are the Medium values. Inputs never drop below 16px, which avoids iPhone focus zoom. */
  --text-detail-meta: 1rem;
  --label-row-min: calc(var(--text-detail-meta) * 1.5);
  --text-detail-body: 1.125rem;
  --text-detail-title: 1.375rem;
  --leading-detail-tight: 1.3;
  --leading-detail-body: 1.5;
  --text-page-title: 2rem;
  --title-tracking: -0.035em;
  --text-section: 1.25rem;
  --text-item: var(--text-detail-body);
  --text-support: var(--text-detail-meta);
  --text-caption: 0.875rem;
  --text-control: 1.125rem;
  --text-dock-label: 1rem;
  --text-launcher-label: 0.875rem;
  --text-input: max(1rem, var(--text-detail-body));
  --row-title-lines: 2;
  --row-meta-lines: 2;
  /* Multiplier for literal-size text with no semantic token: calc(Nrem * var(--text-scale)). */
  --text-scale: 1;
  /* Fixed: transport controls do not scale with the text preset. */
  --podcast-control-size: 1.6875rem;
  /* Dock tile is 14.4vmin (the phone's 56px at 390 wide), capped, same at every size and
     orientation; never by text preset. An installed Android app's own icon sits inset in
     the tile ground at this share of the tile, the same room a page glyph has, so no tile
     is ever a bare square bitmap. */
  --launcher-native-inset: 0.72;
  --dock-tile: min(14.4vmin, 7.5rem);
  --dock-glyph: calc(var(--dock-tile) / 2);
  /* The composer's own 67px resting height (50px row + the reference's own 17px top /
     0 bottom footer padding, .composer below). Only the pre-measure fallback: chat.js
     measures the real composer, which grows with the player, zone and rail. */
  --command-bar-space: 4.1875rem;
  /* The composer's transparent top padding (see .composer): the part of the measured bar
     height that is not the opaque pill. */
  --composer-pad-top: 17px;
  /* The one gap between stacked bottom-shell surfaces (the mini player above the composer pill). */
  --stack-gap: 8px;
  /* Spacing scale: px steps (2xs-xl) and rem steps (r1-r4). */
  --space-2xs: 2px;
  --space-xs: 4px;
  --space-sm: 6px;
  --space-md: 8px;
  --space-lg: 10px;
  --space-xl: 12px;
  --space-r1: 0.25rem;
  --space-r2: 0.5rem;
  --space-r3: 0.75rem;
  --space-r4: 1rem;
  /* Named page rhythm keeps title, labels, and cards on one spacing vocabulary. */
  --rhythm-title-subtitle: var(--space-xs);
  --rhythm-header-body: var(--space-xl);
  --rhythm-label-card: var(--space-md);
  --rhythm-card-card: var(--card-gap);
  --setup-prompt-gap: var(--rhythm-card-card);
  /* .chat's own bottom padding (without the safe-area inset it also adds); the phone
     breakpoint below lowers it. .chat-lower's reservation subtracts it. */
  --shell-pad-bottom: 1rem;
  /* The flex gap between .chat's rows (.chat-upper, .chat-lower); a row that renders
     nothing cancels it (.chat-upper below). */
  --shell-gap: 0.75rem;
  --shell-max: 45rem;
  /* The reading column every page's header and content share on a wide screen (Today, Inbox and the lists all centre on it). */
  --content-max: 60rem;
  --shell-gutter: 1rem;
  /* The one outer side inset on a phone: pane headers, cards, the composer pill and the dock
     tiles all start this far from the screen edge, so their outer edges align. */
  --side-gutter: 18px;
  /* Press feedback (scale(.98) on :active, the reference's own button feel) and a page
     transition's own easing, named so both are the one thing prefers-reduced-motion turns
     off across every consumer rather than a per-component transition duration. */
  --motion-press: 140ms;
  --motion-page: 260ms;
  --ease-page: cubic-bezier(.2, .8, .2, 1);
}

/* Registered as colours so the wallpaper gradients interpolate during the theme fade. */
@property --wall-base-radial { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wall-grad-1 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wall-grad-2 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wall-grad-3 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-gold-1 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-gold-2 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-gold-3 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-teal-1 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-teal-2 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-teal-3 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-blue-1 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-blue-2 { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wall-overlay-top { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wall-overlay-bottom { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-gold-shadow { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --wave-teal-shadow { syntax: '<color>'; inherits: true; initial-value: transparent; }

/* :not(#theme-fade) only raises specificity above per-component transitions; the fade class is never added under reduced motion. */
html.theme-fading, html.theme-fading :not(#theme-fade), html.theme-fading :not(#theme-fade)::before, html.theme-fading :not(#theme-fade)::after {
  transition: background-color 1.6s ease-in-out, color 1.6s ease-in-out, border-color 1.6s ease-in-out, fill 1.6s ease-in-out, stroke 1.6s ease-in-out, box-shadow 1.6s ease-in-out;
}

html.theme-fading {
  transition: background-color 1.6s ease-in-out, color 1.6s ease-in-out, border-color 1.6s ease-in-out, fill 1.6s ease-in-out, stroke 1.6s ease-in-out, box-shadow 1.6s ease-in-out, --wall-base-radial 1.6s ease-in-out, --wall-grad-1 1.6s ease-in-out, --wall-grad-2 1.6s ease-in-out, --wall-grad-3 1.6s ease-in-out, --wave-gold-1 1.6s ease-in-out, --wave-gold-2 1.6s ease-in-out, --wave-gold-3 1.6s ease-in-out, --wave-teal-1 1.6s ease-in-out, --wave-teal-2 1.6s ease-in-out, --wave-teal-3 1.6s ease-in-out, --wave-blue-1 1.6s ease-in-out, --wave-blue-2 1.6s ease-in-out, --wall-overlay-top 1.6s ease-in-out, --wall-overlay-bottom 1.6s ease-in-out, --wave-gold-shadow 1.6s ease-in-out, --wave-teal-shadow 1.6s ease-in-out;
}

/* The tint resolves per tile (--app-ink is set on the tile), so the ground lives here, not in :root. */
.app-tile-icon {
  --icon-tile-ground-tinted: linear-gradient(145deg, color-mix(in srgb, var(--app-ink, var(--ink-heading)) var(--icon-tile-tint), var(--icon-tile-1)), color-mix(in srgb, var(--app-ink, var(--ink-heading)) var(--icon-tile-tint), var(--icon-tile-2)) 80%);
}

@supports (height: 100dvh) { :root { --vh100: 100dvh; } }

/* Text size presets (text-size.js sets data-text-size on <html>). Only the base steps are
   redefined; --text-item, --text-support and --text-input follow them. */
:root[data-text-size="small"] {
  --text-detail-body: 1rem;
  --text-detail-meta: 0.875rem;
  --label-row-min: calc(var(--text-detail-meta) * 1.5);
  --text-detail-title: 1.125rem;
  --text-section: 1.0625rem;
  --text-page-title: 1.875rem;
  --text-caption: 0.75rem;
  --text-control: 1rem;
  --text-dock-label: 0.875rem;
  --text-launcher-label: 0.8125rem;
  --text-scale: 0.875;
}

:root[data-text-size="large"] {
  --text-detail-body: 1.375rem;
  --text-detail-meta: 1.125rem;
  --label-row-min: calc(var(--text-detail-meta) * 1.5);
  --text-detail-title: 1.625rem;
  --text-section: 1.5rem;
  --text-page-title: 2.25rem;
  --text-caption: 1rem;
  --text-control: 1.375rem;
  --text-dock-label: 1.125rem;
  --text-launcher-label: 1.125rem;
  --text-scale: 1.2;
}

/* Dock tile is 14.4vmin (the phone's 56px at 390 wide), capped, same at every size and
   orientation; never by text preset. */

/* Phone: touch targets are 48px (never below 44px) and the gutters tighten. Density comes
   from dropping redundant inset, not from shrinking type or targets. --card-pad is restated
   because it is computed from the two it follows where it is declared. */
@media (max-width: 47.99rem) {
  :root {
    --control-min: 44px;
    --touch-min: 48px;
    --list-gutter: var(--side-gutter);
    --page-gutter: var(--side-gutter);
    --card-pad-x: 14px;
    --card-pad: var(--card-pad-y) var(--card-pad-x);
  }

  /* A header with no subtitle needs less vertical room; safe-area padding is separate. */
  .app-page-header:where(:not(:has(.app-page-header-subtitle))) {
    padding-block-start: var(--space-xl);
    padding-bottom: var(--rhythm-header-body);
  }
}

@media (max-width: 47.99rem) {
  /* These keep the longest label (Groceries) inside its slot with side padding. */
  :root {
    --text-dock-label: 0.6875rem;
    --text-dock-label-tracking: -0.01em;
  }

  :root[data-text-size="large"] {
    --text-dock-label: 0.71875rem;
    --text-dock-label-tracking: -0.02em;
  }
}

@media (orientation: landscape) {
  :root {
    --text-dock-label: 0.875rem;
    --text-dock-label-tracking: 0;
  }

  /* The side rail column is 76px wide, so Groceries needs a smaller step than the 1.125rem Large token. */
  :root[data-text-size="large"] {
    --text-dock-label: 0.9375rem;
    --text-dock-label-tracking: -0.02em;
  }
}

/* Touch input, at any width: a tablet, the touchscreen wall or a landscape phone keeps the
   same 44px/48px targets the narrow viewport gets. Only the target tokens -- the gutters stay
   a width decision. */
@media (pointer: coarse) {
  :root {
    --control-min: 44px;
    --touch-min: 48px;
  }
}

@media (max-width: 359px) {
  :root {
    --side-gutter: 12px;
    --card-pad-x: 12px;
    --card-pad: var(--card-pad-y) var(--card-pad-x);
  }
}

/* Dark theme. Same roles as the light :root above -- most of what changes is the base
   surface/ink/status/category values; anything defined as a color-mix() of those (
   --accent-ink, --cat-N-wash) follows them automatically and needs no restating here.
   --accent-hover and the destination accent/wash pairs are formulas over their OWN literal
   (not over --accent or the light block's value), so each gets its own line below rather
   than silently keeping its light-theme shade.

   This is its own neutral-charcoal composition, not the light block darkened: a cool
   near-black canvas, two lighter elevation steps for raised surfaces (below), and the
   richer blue lifted to a paler, still-legible sky tone that reads as the one action
   colour. It stays a genuinely separate direction rather than collapsing to one theme
   because it is a contract this application argued for and shipped -- a wall-mounted
   kitchen panel that is on all day in a room whose light changes, where the OS answer is
   routinely wrong (see DESIGN.md, "Appearance"). Every display reads in this vocabulary too,
   so it is light exactly when the app is, which on the default `system` preference is
   almost always.

   Elevation runs the OPPOSITE direction from light mode here: light's --panel is a step
   DARKER than --panel-subtle (a sunken field, a hovered row), but stacking dark surfaces
   the same way reads as murkier rather than raised. --surface-raised is a step LIGHTER
   than the panel pair (--panel and --panel-subtle share one ground here), and
   --surface-high is a second, lighter step again for a card that must read as one tier
   above a raised field (see .app-card's dark override and the Podcasts/agenda-tint fixes
   near the shared primitives below). */
:root[data-theme="dark"] {
  color-scheme: dark;
  --wall-base-radial: #0f141b;
  --wall-grad-1: #0f141b;
  --wall-grad-2: #0f141b;
  --wall-grad-3: #0f141b;
  --wave-gold-1: transparent;
  --wave-gold-2: transparent;
  --wave-gold-3: transparent;
  --wave-teal-1: transparent;
  --wave-teal-2: transparent;
  --wave-teal-3: transparent;
  --wave-blue-1: transparent;
  --wave-blue-2: transparent;
  --wall-overlay-top: transparent;
  --wall-overlay-bottom: transparent;
  --wave-gold-shadow: transparent;
  --wave-teal-shadow: transparent;
  /* The halo is the darkening behind glyphs; the contrast test models it as --scrim-on-wall. */
  --ink-on-wall-muted: #9aa8b7;
  --ink-on-wallpaper: var(--ink-heading);
  --muted-on-wallpaper: var(--ink-on-wall-muted);
  --scrim-on-wall: rgb(10 14 18 / 0.72);
  --text-shadow-on-wall: 0 0 3px var(--scrim-on-wall), 0 0 6px var(--scrim-on-wall), 0 0 10px var(--scrim-on-wall), 0 1px 2px var(--scrim-on-wall);
  --top-button-ground: rgb(255 255 255 / 0.11);
  --top-button-ground-hover: rgb(255 255 255 / 0.16);
  --switch-track-ground: rgb(10 14 18 / 0.32);
  /* A dark glass chip for a control drawn straight on the wallpaper. */
  --chip-on-wall: rgb(10 14 18 / 0.28);
  --shadow-card: 0 0 0 1px var(--line-strong), 0 5px 14px rgb(10 14 18 / 0.2), inset 0 1px rgb(255 255 255 / 0.04);
  --dock-current-ground: #3fc7b3;
  --dock-current-ring: #3fc7b3;
  --agent-panel: rgb(10 14 18 / 0.38);
  --mic-fill: #3fc7b3;
  --mic-ink: #0f141b;
  --agent-gold-tint: #2a2244;
  --agent-gold-badge: #2a2244;
  --agent-gold-ink: #b48cff;
  --agent-gold-kicker: #b48cff;
  --agent-page-row: rgba(39, 49, 62, 0.96);
  /* THE APPROVED APPEARANCE. --page is --wall-grad-3 restated above, literally: the flat colour the
     wallpaper itself settles to, so wherever the fixed layer cannot reach reads as a
     continuation of it rather than a seam. */
  --page: #0f141b;
  /* An opaque card surface. Raised/high step a little lighter again, the same "lighter, not
     darker" elevation direction this block uses. */
  --panel-subtle: #18202a;
  --panel-inset: color-mix(in srgb, var(--surface-high) 55%, var(--surface-raised));
  /* The player bar keeps the dark look it always had; the tokens exist so the light block can
     differ. */
  --player-surface: var(--panel-subtle);
  --player-line: var(--line-strong);
  --player-progress-track: rgb(255 255 255 / 0.14);
  --surface-raised: #1f2935;
  --surface-high: #1f2935;
  --panel: #18202a;
  --line: #222c38;
  --line-strong: #263241;
  --ink: #eaf0f6;
  /* Page headings sit on the wallpaper, not on --panel-subtle -- see --page-header below --
     so they read in --ink-heading, and .app-page-header-title keeps its soft text-shadow
     (below) besides. */
  --ink-heading: #eaf0f6;
  --muted: #9aa8b7;
  --faint: color-mix(in srgb, var(--muted) 68%, var(--panel-subtle));
  /* THE one action/selection colour, a bright teal. Nothing else in this theme is
     accent-coloured by default; a destination's nav icon uses one of the three ICON INKS
     below instead (--icon-gold/-blue/-teal), reserved for "which destination is this", never
     for "press this". */
  --accent: #3fc7b3;
  --accent-hover: color-mix(in srgb, var(--accent) 85%, white);
  /* Ink on the teal fill: 8.8:1 against --accent itself. */
  --on-accent: #0f141b;
  /* ~16% teal over the card. */
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--panel-subtle));
  --error: #ef6f66;
  --code: #161c23;
  --accent-ink: color-mix(in srgb, var(--accent) 55%, var(--ink));
  /* The dock's own current-item ink (see .dock-button[aria-current]): the teal accent itself,
     as text on the card. */
  --dock-ink-current: #3fc7b3;
  /* Inline links in body text (email). */
  --link: #8fb2ff;
  --now-ink: var(--accent);
  /* THE THREE ICON INKS. A destination's nav icon takes one of the three rather than a hue of
     its own, so many destinations share a family. */
  --icon-gold: #3fc7b3;
  --icon-blue: #6fb0ff;
  --icon-teal: #3fc7b3;
  /* The dark bevelled tile ground, lighter tile stop first. */
  --icon-tile-1: #34404e;
  --icon-tile-2: #2c3642;
  --icon-tile-tint: 14%;
  --shadow-tile-bevel:
    inset 0 1px 1px rgb(255 255 255 / 0.18),
    inset 0 -1px 1px rgb(10 14 18 / 0.33);
  --shadow-tile-icon: var(--shadow-tile-bevel), 0 6px 12px rgb(10 14 18 / 0.2);
  --shadow-tile-icon-dock: var(--shadow-tile-bevel), 0 3px 6px rgb(10 14 18 / 0.22);
  --shadow-tile-icon-active: inset 0 1px 1px rgb(255 255 255 / 0.18), 0 2px 5px rgb(10 14 18 / 0.2);
  /* THE DESTINATION IDENTITY TOKENS. Each destination's strong icon colour (--accent-X) and its
     tint (--wash-X): orange for Meals and Inbox, purple for Music, green for Grocery, blue for
     Calendar. The rest are neutral slate. Library, Settings and Photos have no tint. */
  --accent-meals: #ffa65c;
  --wash-meals: #3a2717;
  --accent-podcasts: #b7c3d0;
  --wash-podcasts: #222b36;
  --accent-music: #b48cff;
  --wash-music: #2a2244;
  --accent-calendar: #6fb0ff;
  --wash-calendar: #1b2b42;
  --accent-household: #b7c3d0;
  --wash-household: #222b36;
  --accent-library: #b7c3d0;
  --accent-settings: #b7c3d0;
  --accent-email: #ffa65c;
  --wash-email: #3a2717;
  --accent-grocery: #5fd67c;
  --wash-grocery: #1a3326;
  --accent-photos: #b7c3d0;
  /* Android reads these as the car_* colors (generated by scripts/android-design-tokens.mjs). */
  --car-panel: #0f141b;
  --car-row: #18202a;
  --car-pane-bg: #0f141b;
  --car-pane-row: #18202a;
  --car-gold: #3fc7b3;
  --car-on-gold: #0f141b;
  --car-teal: #3fc7b3;
  --car-ink: #eaf0f6;
  --car-muted: #9aa8b7;
  /* Person-slot category hues. Slot 4 is a cool slate, kept well apart from --accent and from
     the warm status colours, so a person's row does not read as the selected one. */
  --ok: #4ec2a0;
  --warn: #d9a23e;
  --cat-1: #5cc9c9;
  --cat-2: #a48bea;
  --cat-3: #e184a6;
  --cat-4: #8ba3c2;
  --cat-5: #3e8f63;
  --cat-1-wash: color-mix(in srgb, var(--cat-1) 26%, var(--panel-subtle));
  --cat-2-wash: color-mix(in srgb, var(--cat-2) 26%, var(--panel-subtle));
  --cat-3-wash: color-mix(in srgb, var(--cat-3) 26%, var(--panel-subtle));
  --cat-4-wash: color-mix(in srgb, var(--cat-4) 26%, var(--panel-subtle));
  --cat-5-wash: color-mix(in srgb, var(--cat-5) 26%, var(--panel-subtle));
  --wash-shared: var(--panel-subtle);
  /* The field ground is given directly: its implicit transparency plus the sheet's own inset
     highlight reads as a faint lift, not a real surface. --panel is opaque here (a real
     card/scroller ground, #18202a), so a field needs a token of its own rather than reusing
     it translucent-by-coincidence. */
  --field-fill: rgb(255 255 255 / 0.06);
  /* Sits WITH the surrounding dark surfaces instead of fighting them: reuses this theme's own
     panel and muted ink rather than a colour of its own. --chart-line needs no entry here --
     its formula in the light block already reads whichever of these two is active. */
  --chart-surface: var(--panel);
  --chart-ink: var(--muted);
  --library-note-ink: color-mix(in srgb, #93221f 70%, white);
  --library-remove-border: color-mix(in srgb, var(--library-note-ink) 45%, var(--panel-subtle));
  /* Given directly: the control ring has 5.1:1 contrast against --panel-subtle. */
  --control-line: #93a3b3;
  --shadow-float: 0 -3px 8px rgb(0 0 0 / 0.35);
  --shadow-sheet: 0 -4px 14px rgb(0 0 0 / 0.5);
  /* A near-black scrim, so a dialog still reads as dimming AWAY from the charcoal ground
     rather than toward a colour indistinguishable from it. */
  --scrim: rgb(10 14 18 / 0.6);
}

:root {
  --weather-sun: #8f6200;
  --weather-cloud: #4d6174;
  --weather-rain: #2058b0;
  --weather-snow: #277a96;
  --weather-storm: #6d47b8;
  --weather-fog: #5f6b73;
  --weather-temp-cold: #1f5fae;
  --weather-temp-mild: #0f766e;
  --weather-temp-warm: #9a5a00;
  --weather-temp-hot: #b3361b;
  --weather-rain-low: var(--muted);
  --weather-rain-mid: color-mix(in srgb, var(--weather-rain) 65%, var(--muted));
}

:root[data-theme="dark"] {
  --weather-sun: #f0c35a;
  --weather-cloud: #b4c3d2;
  --weather-rain: #7db8f5;
  --weather-snow: #9adcf0;
  --weather-storm: #b9a0f0;
  --weather-fog: #aab3ba;
  --weather-temp-cold: #7db3f5;
  --weather-temp-mild: #5fd1be;
  --weather-temp-warm: #f0b048;
  --weather-temp-hot: #ff8a65;
}
