﻿/* Caelowx design tokens.

   Loaded after design-system.css and app.css, and deliberately last: part of what
   this file does is reconcile the two token systems that were already here.

   THE TWO SYSTEMS, AND WHY THIS BRIDGES THEM
   ------------------------------------------
   design-system.css declares --app-* on :root and body.theme-*, and the .app-*
   primitives (.app-input, .app-button, .app-badge, .app-message) are built from
   them. app.css declares a parallel --bg/--surface/--accent set on .weather-shell,
   and every shell page is built from those.

   They hold near-identical values and are declared twice, so a page using an .app-*
   primitive inside a .weather-shell was resolving colour from body while everything
   around it resolved from the shell. The two happened to agree; nothing made them.
   The bridge at the bottom of each theme block points --app-* at the shell's own
   values, so a shared primitive is correct wherever it is used and there is one
   place to change a colour.

   WHAT BELONGS HERE
   -----------------
   Scales that are theme-independent (spacing, radius, type, control heights,
   motion) are declared once on :root. Colour is declared per theme, and only as
   *semantic* names - what a colour means, never what it looks like. A component
   asks for --cx-measurement-rainfall or --cx-balance-deficit; it does not ask for
   blue or red, and it does not carry a hex value of its own.

   Namespaces are separate even where they resolve to the same underlying colour,
   because the meanings move independently: Alert.Warning and Measurement.Solar are
   both amber today and there is no reason they must stay that way. */


/* ============================================================ scales, one copy */

:root {
    /* Spacing. Four steps of 4 and then a coarser tail, which is enough for a
       page header, a toolbar, card padding and the gaps between cards without
       every surface inventing a value. */
    --cx-space-1: 4px;
    --cx-space-2: 6px;
    --cx-space-3: 8px;
    --cx-space-4: 10px;
    --cx-space-5: 12px;
    --cx-space-6: 16px;
    --cx-space-7: 20px;
    --cx-space-8: 24px;
    --cx-space-9: 32px;

    /* Radius. --cx-radius-md is the control radius and matches the platform's
       existing inputs; --cx-radius-lg is the card and panel radius. */
    --cx-radius-sm: 8px;
    --cx-radius-md: 10px;
    --cx-radius-lg: 13px;
    --cx-radius-pill: 999px;

    /* Type. Taken from the Dashboard rather than invented, because the Dashboard is
       the baseline the rest of the application is being aligned to: --cx-text-display
       is its station heading, --cx-text-eyebrow its kicker, --cx-text-xs its
       subtitle. Nothing here is larger than what the Dashboard already uses. */
    --cx-text-eyebrow: .56rem;
    --cx-text-2xs: .58rem;
    --cx-text-xs: .64rem;
    --cx-text-sm: .68rem;
    --cx-text-md: .72rem;
    --cx-text-lg: .82rem;
    --cx-text-xl: .95rem;
    --cx-text-display: 1.55rem;

    --cx-leading-tight: 1.25;
    --cx-leading: 1.45;

    --cx-weight-regular: 400;
    --cx-weight-medium: 500;
    --cx-weight-semibold: 650;
    --cx-weight-bold: 750;
    --cx-weight-heavy: 800;

    --cx-tracking-eyebrow: .11em;
    --cx-tracking-display: -.04em;

    /* The page gutter, taken from the Dashboard's own .dashboard-main so a reader
       moving between it and Reports does not see the content shift in from the
       edges. Named here so a third surface adopts it rather than measuring it. */
    --cx-page-pad-top: 14px;
    --cx-page-pad-x: 18px;
    --cx-page-pad-bottom: 28px;
    --cx-page-pad-x-phone: 13px;
    --cx-page-pad-bottom-phone: 26px;

    /* Mobile shell chrome. The bars own their dimensions and the shell reserves clearance
       from these, so a bar's height and the space kept for it cannot drift apart. */
    --cx-mobile-bar-height: 56px;
    --cx-bottom-bar-height: 68px;

    /* One compact desktop chrome dimension: the collapsed navigation rail and
       artwork-free app top meet in a square logo zone. */
    --cx-compact-chrome-size: 60px;
    --cx-rail-button-size: 48px;
    --cx-rail-divider-size: 1px;
    --cx-rail-gutter: calc(
        (var(--cx-compact-chrome-size) - var(--cx-rail-button-size) - var(--cx-rail-divider-size)) / 2);

    /* What a phone page must keep clear at the bottom: the fixed bar, the device's own
       inset, and a little air so the last item is not flush against the bar. */
    --cx-bottom-bar-clearance: calc(
        var(--cx-bottom-bar-height) + env(safe-area-inset-bottom) + var(--cx-space-3));

    /* ------------------------------------------------------------------ layering

       One ordering, named, because the alternative is what was here: a bottom bar at
       5000 that painted over the Reports Export menu at 810, and no way to see that
       from either file.

       Chrome is deliberately the lowest. A menu the reader has just opened should cover
       the navigation, not be covered by it. */
    --cx-z-chrome: 100;
    --cx-z-popover-backdrop: 800;
    --cx-z-popover: 810;
    --cx-z-menu: 1000;
    --cx-z-dialog: 3000;

    /* Controls. One height for anything that sits in a toolbar row - a search box,
       a select, an icon button - so they read as one family however wide they are. */
    --cx-control-height: 38px;
    --cx-control-height-sm: 30px;
    --cx-control-padding-x: 11px;

    /* The Reports frame's two reserved rows.
       The navigation row's height follows the tallest thing in it - the primary
       action, at --cx-control-height plus its own bottom padding - and Report View
       puts a breadcrumb in the same place. Reserving it here means every Reports
       surface begins its content at the same y, whatever it happens to contain. */
    --cx-reports-row-height: 48px;
    --cx-reports-row-height-phone: 92px;

    /* Interaction. One focus ring and one transition, so hover and focus feel the
       same on a tab, a card, a pill and a toolbar control. */
    --cx-focus-width: 2px;
    --cx-focus-offset: 2px;
    --cx-transition: 120ms ease;

    /* How strongly a surface reacts to a pointer. Kept as tokens because the
       Library's selected card was visibly heavier than anything on the Dashboard,
       and "heavier" is a number somebody has to be able to find. */
    --cx-hover-lift: color-mix(in srgb, var(--cx-ink) 4%, transparent);
    --cx-selected-border: color-mix(in srgb, var(--cx-accent) 55%, var(--cx-line));
}


/* ==================================================================== dark theme */

:root,
body.theme-dark,
.weather-shell.theme-dark.theme-caelowx {
    /* The two structural colours the tokens above refer back to. */
    --cx-ink: #ffffff;
    --cx-line: #3b3b3b;
    --cx-accent: #49b6e8;

    /* --- Measurement: what is being measured -------------------------------- */
    /* Rainfall and water are the Water Balance's accepted inflow and outflow, and
       they are the reason this namespace exists: those two colours were already
       decided by a live review and had to become the platform's names for them
       rather than two literals inside one chart. */
    --cx-measurement-rainfall: #1687e8;
    --cx-measurement-water: #e0a92e;
    --cx-measurement-temperature: #f0895f;
    --cx-measurement-humidity: #5fc9c0;
    --cx-measurement-wind: #9aa7b8;
    --cx-measurement-solar: #f5c451;
    --cx-measurement-pressure: #8b6ce8;
    --cx-measurement-air-quality: #7fcf7f;
    --cx-measurement-conditions: #9db4d0;
    --cx-measurement-lightning: #f5c451;

    /* --- Alert: how serious something is ------------------------------------ */
    --cx-alert-info: #50c3e5;
    --cx-alert-success: #42c77a;
    --cx-alert-warning: #f29f3d;
    --cx-alert-danger: #e84b63;

    /* --- Status: whether a thing is working --------------------------------- */
    --cx-status-ok: #42c77a;
    --cx-status-degraded: #f29f3d;
    --cx-status-offline: #e84b63;
    --cx-status-unknown: #9d9d9d;

    /* --- Delta: which way a value moved ------------------------------------- */
    /* Aliases, deliberately. A change going up is not the same idea as a healthy
       status or a surplus of water, and the day one of those moves this should not
       follow it - but there is no new colour here, only an existing one given the
       name of what it means in a report. */
    --cx-delta-positive: var(--cx-alert-success);
    --cx-delta-negative: var(--cx-alert-danger);
    --cx-delta-neutral: var(--cx-status-unknown);

    /* --- Balance: which side of zero ---------------------------------------- */
    /* The accepted Water Balance semantics: red below the line, green above,
       neutral for a span the report could not compute and will not claim a sign
       for. Mirrored in CaelowxChart.Palette, which Syncfusion needs as literals -
       CaelowxSemanticColourTests pins the two together. */
    --cx-balance-deficit: #e84b63;
    --cx-balance-surplus: #42c77a;
    --cx-balance-neutral: #8b97a8;

    /* --- Condition: what the sky is doing ----------------------------------- */
    --cx-condition-clear: #f5c451;
    --cx-condition-cloud: #9db4d0;
    --cx-condition-precipitation: #1687e8;
    --cx-condition-severe: #f36b3f;
}


/* =================================================================== light theme */

body.theme-light,
.weather-shell.theme-light {
    --cx-ink: #000000;
    --cx-line: #d1d1d1;
    --cx-accent: #1677a5;

    --cx-measurement-rainfall: #1c7ed6;
    --cx-measurement-water: #b3760c;
    --cx-measurement-temperature: #c2551f;
    --cx-measurement-humidity: #17827a;
    --cx-measurement-wind: #5f6d80;
    --cx-measurement-solar: #9a6700;
    --cx-measurement-pressure: #6d43b8;
    --cx-measurement-air-quality: #2f8a3f;
    --cx-measurement-conditions: #4a6480;
    --cx-measurement-lightning: #9a6700;

    --cx-alert-info: #1677a5;
    --cx-alert-success: #237d43;
    --cx-alert-warning: #9a6700;
    --cx-alert-danger: #b52343;

    --cx-status-ok: #2f9e56;
    --cx-status-degraded: #d28b3e;
    --cx-status-offline: #d4635a;
    --cx-status-unknown: #626262;

    /* --- Delta: which way a value moved ------------------------------------- */
    /* Aliases, deliberately. A change going up is not the same idea as a healthy
       status or a surplus of water, and the day one of those moves this should not
       follow it - but there is no new colour here, only an existing one given the
       name of what it means in a report. */
    --cx-delta-positive: var(--cx-alert-success);
    --cx-delta-negative: var(--cx-alert-danger);
    --cx-delta-neutral: var(--cx-status-unknown);

    --cx-balance-deficit: #d63a3a;
    --cx-balance-surplus: #1f9e57;
    --cx-balance-neutral: #7a8798;

    --cx-condition-clear: #9a6700;
    --cx-condition-cloud: #4a6480;
    --cx-condition-precipitation: #1c7ed6;
    --cx-condition-severe: #b52343;
}


/* ====================================================== the bridge, both themes */

/* --app-* inside a shell resolves from the shell rather than from body, so a
   shared .app-* primitive used on a shell page is theme-correct by construction
   instead of by the two palettes happening to match. */
.weather-shell {
    --app-bg: var(--bg);
    --app-surface: var(--surface);
    --app-surface-2: var(--surface-2);
    --app-surface-3: var(--surface-3);
    --app-border: var(--border);
    --app-text: var(--text);
    --app-muted: var(--muted);
    --app-accent: var(--accent);
    --app-accent-soft: var(--accent-soft);
    --app-success: var(--success);
    --app-warning: var(--warning);
    --app-danger: var(--danger);
    --app-radius-sm: var(--cx-radius-sm);
    --app-radius-md: var(--cx-radius-md);
    --app-radius-lg: var(--cx-radius-lg);
    --app-on-accent: var(--on-accent, #ffffff);
}

/* Rainfall had two blues: --rain, used by the Dashboard's rain and humidity
   glyphs, and the Water Balance's inflow. One measurement cannot have two
   colours, and the accepted chart is the one that was reviewed - so --rain now
   resolves to the semantic name. Declared at .theme-* specificity so it wins over
   app.css's own two declarations without either being deleted. */
.weather-shell.theme-dark.theme-caelowx,
.weather-shell.theme-light {
    --rain: var(--cx-measurement-rainfall);
}


/* ======================================================= shared state surfaces */

/* Empty, loading and error look the same wherever they appear. They used to be
   page-local paragraphs, so an empty Library and an empty Templates shelf were two
   different-looking apologies. CaelowxState renders these. */
.cx-state {
    align-items: center;
    background: var(--surface, var(--app-surface));
    border: 1px dashed var(--border, var(--app-border));
    border-radius: var(--cx-radius-lg);
    display: flex;
    flex-direction: column;
    gap: var(--cx-space-3);
    justify-content: center;
    padding: var(--cx-space-9) var(--cx-space-7);
    text-align: center;
}

.cx-state-icon {
    align-items: center;
    background: var(--accent-soft, var(--app-accent-soft));
    border-radius: var(--cx-radius-lg);
    color: var(--accent, var(--app-accent));
    display: inline-flex;
    height: 44px;
    justify-content: center;
    width: 44px;
}

.cx-state-title {
    font-size: var(--cx-text-xl);
    font-weight: var(--cx-weight-semibold);
    margin: 0;
}

.cx-state-body {
    color: var(--muted, var(--app-muted));
    font-size: var(--cx-text-md);
    line-height: var(--cx-leading);
    margin: 0;
    max-width: 52ch;
}

.cx-state-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
    justify-content: center;
    margin-top: var(--cx-space-2);
}

/* An error is the same shape, said in the alert namespace. A solid border rather
   than a dashed one: dashed reads as "nothing here yet", which a failure is not. */
.cx-state-error {
    border-style: solid;
    border-color: color-mix(in srgb, var(--cx-alert-danger) 38%, var(--border, var(--app-border)));
}

.cx-state-error .cx-state-icon {
    background: color-mix(in srgb, var(--cx-alert-danger) 12%, transparent);
    color: var(--cx-alert-danger);
}

/* Loading says so quietly and does not pretend to be a result. */
.cx-state-loading {
    border-style: solid;
}

.cx-state-loading .cx-state-icon {
    animation: cx-state-pulse 1.4s ease-in-out infinite;
}

@keyframes cx-state-pulse {
    0%, 100% { opacity: .45; }
    50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .cx-state-loading .cx-state-icon {
        animation: none;
    }
}


/* ================================================= CaeloWX Dark foundation

   Dark is the first branded theme slice. Light intentionally keeps its existing
   palette and identity until its own approved conversion. Geometry, card anatomy,
   responsive rules and interaction behavior remain owned by their existing files. */

@font-face {
    font-display: swap;
    font-family: "Manrope";
    font-style: normal;
    font-weight: 400 800;
    src: url("fonts/manrope-latin-400-800.woff2") format("woff2");
}

.weather-shell.theme-light.theme-caelowx {
    /* The logo's medium wave blue, desaturated into working surfaces. This is
       daylight, not white paper: every layer retains a visible blue identity. */
    --cx-background: #bdd4e2;
    --cx-surface: #d5e5ee;
    --cx-card: #e4eff5;
    --cx-elevated: #c7dce8;
    --cx-border: #86abc0;
    --cx-text-primary: #06172f;
    --cx-text-secondary: #294f69;
    --cx-text-muted: #486b81;
    --cx-primary: #0879c9;
    --cx-brand-accent: #50c3e5;
    --bg: var(--cx-background);
    --surface: var(--cx-surface);
    --surface-2: var(--cx-card);
    --surface-3: var(--cx-elevated);
    --border: var(--cx-border);
    --text: var(--cx-text-primary);
    --muted: var(--cx-text-muted);
    --accent: var(--cx-primary);
    --accent-soft: color-mix(in srgb, var(--cx-primary) 12%, transparent);
    --on-accent: #ffffff;
    --text-secondary: var(--cx-text-secondary);
    --cx-ink: var(--cx-text-primary);
    --cx-line: var(--cx-border);
    --cx-accent: var(--cx-primary);
    font-family: "Manrope", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    font-variant-numeric: tabular-nums;
}

.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .dashboard-main {
    background:
        radial-gradient(circle at 42% -12%, color-mix(in srgb, var(--cx-brand-accent) 16%, transparent), transparent 34rem),
        linear-gradient(180deg, #c9e0ea 0%, var(--cx-background) 48rem);
}

.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-card {
    background: linear-gradient(148deg, #edf5f8 0%, var(--cx-card) 60%, #d6e6ee 100%);
    border-color: color-mix(in srgb, var(--cx-border) 78%, var(--cx-primary));
    box-shadow: 0 9px 22px rgba(6, 23, 47, .14), inset 0 1px 0 rgba(255, 255, 255, .68);
}

.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-tile {
    background: linear-gradient(180deg, #dceaf1 0%, #c8dde8 100%);
    border-color: color-mix(in srgb, var(--cx-border) 82%, var(--cx-primary));
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7), 0 4px 12px rgba(6, 23, 47, .11);
}

.weather-shell.theme-dark.theme-caelowx {
    --cx-background: #06172f;
    --cx-surface: #081d38;
    --cx-card: #0b2442;
    --cx-elevated: #103054;
    --cx-border: #1b3f6b;
    --cx-text-primary: #f5fbfd;
    --cx-text-secondary: #a9c3d9;
    --cx-text-muted: #7ea0bd;
    --cx-primary: #1687e8;
    --cx-brand-accent: #50c3e5;

    /* Existing components already consume these stable shell names. Mapping the
       approved tokens here reaches the shell without changing component contracts. */
    --bg: var(--cx-background);
    --surface: var(--cx-surface);
    --surface-2: var(--cx-card);
    --surface-3: var(--cx-elevated);
    --border: var(--cx-border);
    --text: var(--cx-text-primary);
    --muted: var(--cx-text-muted);
    --accent: var(--cx-primary);
    --accent-soft: color-mix(in srgb, var(--cx-primary) 16%, transparent);
    --on-accent: #ffffff;
    --success: var(--cx-alert-success);
    --warning: var(--cx-alert-warning);
    --danger: var(--cx-alert-danger);
    --success-indicator: var(--cx-status-ok);
    --warning-indicator: var(--cx-status-degraded);
    --danger-indicator: var(--cx-status-offline);
    --text-secondary: var(--cx-text-secondary);
    --cx-ink: var(--cx-text-primary);
    --cx-line: var(--cx-border);
    --cx-accent: var(--cx-primary);

    font-family: "Manrope", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    font-variant-numeric: tabular-nums;
}

/* CaeloWX Nightshade keeps every approved Dark component, dimension and semantic
   weather color, then deepens only the structural layers to the V2 Night/OLED
   palette. This must follow the Dark foundation so its more-specific structural
   values remain the final word in the cascade. */
.weather-shell.theme-dark.theme-oled.theme-caelowx {
    --cx-background: #3a3a3a;
    --cx-surface: #303030;
    --cx-card: #242424;
    --cx-elevated: #303030;
    --cx-border: #565656;
    --cx-text-primary: #f5fbfd;
    --cx-text-secondary: #d3d3d3;
    --cx-text-muted: #ababab;
    --cx-primary: #d8d8d8;
    --cx-brand-accent: #f0f0f0;

    /* NIGHTSHADE'S ACCENT IS A LIGHT GREY, so white on it is not a colour choice - it is an
       unreadable one. Measured on a primary button: #ffffff on #d8d8d8 is about 1.4:1. This is
       the one theme where the foreground has to invert, and naming it here rather than at each
       call site is what keeps the other three themes from having to know. #242424 is the
       card layer, so the pairing stays inside the palette; it reads at roughly 10.9:1. */
    --on-accent: #242424;
}

/* Cards are the third level in the approved hierarchy. This changes only their
   skin; footprints, placement, padding, internal anatomy and states are untouched.

   `.app-card` USED TO BE IN THIS SELECTOR AND IS DELIBERATELY NOT ANY MORE.

   It pinned the shared card to --cx-card, which IS --surface-2 - the same token every
   surface designed to sit INSIDE a card asks for. So in Administration a card and the
   things nested in it resolved to one colour, #0b2442 on #0b2442, and the ladder the
   theme defines collapsed to a single step separated only by a 1px border: the Overview's
   area tiles, the Cases create panel and agent gate, the AI recommendation body, the group
   cards and the thread entries all read as flat against their own parents.

   Removing it costs the Dashboard nothing, which is the reason this is safe rather than a
   trade. The Dashboard draws no `.app-card` at all - it is a `.wpc-card` surface, and on
   /dashboard-next even those take the hero gradient rather than this background-color. The
   live consumers are Administration and the capture-preview dialog, and both want the
   ladder. `.app-card` now resolves to its own `var(--app-surface)` as design-system.css
   declares, so the steps are --bg -> --surface -> --surface-2 again. */
.weather-shell.theme-dark.theme-caelowx .wpc-card {
    background-color: var(--cx-card);
}

/* The reference dashboard is not a stack of flat navy rectangles. Its depth comes
   from three quiet cues used together: a lifted top-left sheen, a deeper lower edge,
   and a cool border that catches the surrounding blue light. Dashboard owns the
   composition; the shared card system still owns every footprint and region. */
.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .dashboard-main {
    background:
        radial-gradient(
            circle at 42% -12%,
            color-mix(in srgb, var(--cx-primary) 12%, transparent),
            transparent 34rem
        ),
        linear-gradient(
            180deg,
            color-mix(in srgb, var(--cx-background) 86%, var(--cx-card)) 0%,
            var(--cx-background) 48rem
        );
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-card {
    background:
        radial-gradient(
            ellipse at 16% -8%,
            color-mix(in srgb, var(--cx-brand-accent) 10%, transparent) 0%,
            transparent 44%
        ),
        linear-gradient(
            148deg,
            color-mix(in srgb, var(--cx-card) 88%, var(--cx-elevated)) 0%,
            var(--cx-card) 58%,
            color-mix(in srgb, var(--cx-card) 82%, var(--cx-background)) 100%
        );
    border-color: color-mix(in srgb, var(--cx-border) 78%, var(--cx-brand-accent));
    box-shadow:
        0 10px 24px color-mix(in srgb, var(--cx-background) 48%, transparent),
        inset 0 1px 0 color-mix(in srgb, var(--cx-text-primary) 7%, transparent),
        inset 0 -1px 0 color-mix(in srgb, var(--cx-background) 44%, transparent);
}

/* Forecast periods read as individual pieces of glass inside their parent card,
   matching the mockup without changing rail width, scrolling or selection logic. */
.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-tile {
    background:
        linear-gradient(
            180deg,
            color-mix(in srgb, var(--cx-elevated) 38%, var(--cx-card)) 0%,
            color-mix(in srgb, var(--cx-card) 86%, var(--cx-background)) 100%
        );
    border-color: color-mix(in srgb, var(--cx-border) 82%, var(--cx-brand-accent));
    box-shadow:
        inset 0 1px 0 color-mix(in srgb, var(--cx-text-primary) 6%, transparent),
        0 4px 12px color-mix(in srgb, var(--cx-background) 32%, transparent);
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-title,
.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-stat-label {
    text-shadow: 0 1px 10px color-mix(in srgb, var(--cx-background) 72%, transparent);
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-stat-label,
.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-meta {
    color: color-mix(in srgb, var(--cx-text-secondary) 82%, var(--cx-text-primary));
}

/* Keep the dark basemap and radar sweep inside the same luminous frame as the
   surrounding cards. Leaflet remains responsible for all map interaction. */
.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .radar-map {
    border-color: color-mix(in srgb, var(--cx-border) 72%, var(--cx-brand-accent));
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, var(--cx-text-primary) 5%, transparent),
        0 8px 20px color-mix(in srgb, var(--cx-background) 38%, transparent);
}

/* The station identifies the dashboard; it is not the dashboard's hero content.
   Keep it clearly above card headings without letting it overpower the readings. */
.weather-shell.theme-dark.theme-caelowx .station-heading h1 {
    font-size: 1.3rem;
    letter-spacing: -.025em;
}

/* Temperature and UV keep the card system's established thresholds and class
   names. Only the Dark presentation changes: heat runs through amber, orange and
   red; cold runs through cyan, blue and purple. The UV card intentionally reads
   the same bands, producing Low green, Moderate amber, High orange, and Very
   High/Extreme red without introducing a second category table. */
.weather-shell.theme-dark.theme-caelowx .wpc-card {
    --wpc-temp-extreme-hot: color-mix(in srgb, var(--cx-alert-danger) 80%, var(--cx-text-primary));
    --wpc-temp-hot: var(--cx-alert-danger);
    --wpc-temp-warm: var(--cx-condition-severe);
    --wpc-temp-mild: var(--cx-alert-warning);
    --wpc-temp-comfortable: var(--cx-alert-success);
    --wpc-temp-neutral: var(--cx-text-secondary);
    --wpc-temp-cool: var(--cx-brand-accent);
    --wpc-temp-cold: var(--cx-primary);
    --wpc-temp-freezing-risk: color-mix(in srgb, var(--cx-primary) 70%, var(--cx-measurement-pressure));
    --wpc-temp-very-cold: color-mix(in srgb, var(--cx-primary) 35%, var(--cx-measurement-pressure));
    --wpc-temp-extreme-cold: var(--cx-measurement-pressure);
}

/* Preserve the existing Weather Platform identity in Light. Dark swaps in exact
   package artwork; the compact rail uses the approved full-colour C mark. */
.brand-caelowx-lockup,
.brand-caelowx-lockup-light,
.brand-caelowx-lockup-nightshade,
.brand-caelowx-mark,
.brand-caelowx-composite {
    display: none;
}

/*  THE THREE THEME VARIANTS START HIDDEN, IN EVERY BAND.

    They were hidden only inside the >=901px rail block, so on a phone all three
    rendered at once: the desktop band's `display: none` never applied, and the
    <=640px block only ever turned the matching one ON. The light and nightshade
    lockups therefore sat in the mobile bar at their intrinsic 401 x 129 - measured
    in Chrome at 390px, the composite came out 901.5px wide inside a 390px bar - and
    the ™ anchored to the wrong one of them.

    This is a Batch B gap rather than anything the trademark work introduced; it is
    fixed here because the anchors cannot be correct while the box is wrong. Hiding
    them at the base, where the rest of the brand artwork is already hidden, is what
    makes every band opt in explicitly - which is what the rail band was doing all
    along. */
.brand-caelowx-composite-light-static,
.brand-caelowx-composite-dark-static,
.brand-caelowx-composite-nightshade-static {
    display: none;
}

.brand-tm {
    display: none;
}

/* ============================================================ CaeloWX(TM)

   ONE TREATMENT FOR EVERY PRIMARY FULL WORDMARK, ANCHORED TO THE FINAL "X"
   RATHER THAN TO THE LOGO BOX.

   The first implementation positioned the mark at `right: -7px; top: 0` of
   `.brand-caelowx-composite` - the whole lockup container - and that is the wrong
   box in two ways. The container is 174 x 48 on the desktop rail while the artwork
   renders 174 x 56 and overflows it, so `top: 0` is not even the image's top; and
   the wordmark's final glyph ends well inside the artwork, so the container's
   right edge is not the glyph's right edge. The mark therefore floated up and away
   from the wordmark it belongs to. Measured: on the expanded lockup the mark sat
   about 28px above the "X" it should touch.

   SO THE ANCHORS ARE THE ARTWORK'S OWN COORDINATES, measured off the PNGs' alpha
   channel and expressed as percentages of the rendered image box. Percentages
   rather than pixels because every surface renders these lockups at a different
   size - 174px on the rail, ~99px in the phone bar, 260px on the marketing header
   - and a percentage is the same optical position at all of them. The approved
   artwork is NOT modified; nothing here touches a PNG.

     artwork                          final-X right edge   X cap top   cap height
     expanded lockup     401 x 129           99.00%          56.59%      10.97%
     compact header dark 512 x 195           90.43%          41.54%       6.25%
     compact header light 512 x 145          97.46%          38.62%       7.81%

   (cap height as a percentage of WIDTH, which is what makes it usable as a `cqw`
   font size.)

   THE SIZE IS THE WORDMARK'S CAP HEIGHT. `--cx-tm-size` is each artwork's cap
   height at the size that artwork is actually rendered, because a ™ glyph occupies
   roughly 0.42em and the wanted mark is roughly 0.42 of a capital - so font size
   and cap height come out the same number, which is an easy thing to keep right.
   The old `font-size: .5rem` was 8px on every surface, which read as a speck beside
   a 176px marketing lockup and was the "too small" half of the reported defect.

   WHY THE SIZE IS NOT A CONTAINER QUERY, which was the first attempt. `cqw` would
   make it proportional with no per-surface number at all, but it requires
   `container-type: inline-size` on the wrapper, and an inline-size container is
   sized as if it had NO CONTENTS - so a shrink-wrapped wrapper collapses to zero
   width and takes the lockup with it. The wrapper has to hug the artwork for the
   position percentages to mean anything, so hugging wins and the size is a length.
   POSITION is still fully proportional: a percentage resolves against the wrapper's
   own box and needs no containment, so the anchors hold at every rendered size. */
.brand-tm-lockup {
    display: inline-block;
    line-height: 0;
    position: relative;
}

/*  The image defines the wrapper's box, which is the whole point - the wrapper
    shrink-wraps the artwork, so the percentages above land on the glyph.

    THERE IS DELIBERATELY NO `display` RULE FOR THE IMAGE HERE, and that is a
    correction rather than an omission. A `.brand-tm-lockup > img { display: block }`
    was written first and it broke AppBrand: the composite holds three theme-gated
    lockups hidden by `.brand-caelowx-composite-*-static { display: none }` at (0,1,0),
    and a child selector at (0,1,1) in this stylesheet - which loads last - outranked
    it and showed all three at once. The composite then took its height from a
    401px-wide intrinsic image, so the ™ anchors resolved against a 129px box and the
    mark landed 73px below the lockup's top.

    Every call site already sets `display: block` on its own lockup image
    (`.auth-caelowx-logo`, `.marketing-logo img`, `.footer-logo img`, and the
    composite's own per-theme rules), so the rule was redundant as well as harmful.
    The wrapper's `line-height: 0` is what removes the inline descender gap. */

/*  THE ANCHORS ARE INK-TO-INK, NOT BOX-TO-BOX, AND THE `em` CORRECTIONS ARE WHY.

    `--cx-tm-x` / `--cx-tm-y` are the artwork's measured glyph edges. Setting `top` to
    them directly puts the mark's EM BOX top on the cap line, which is not the same as
    putting the GLYPH there: Manrope leaves dead space above the ™, measured on the
    rendered rail at 2.40px of a 17px mark, so the mark landed 2.4px BELOW the "WX" cap
    line - close enough to look almost right and wrong enough to read as detached.
    Horizontally the mark's ink starts about 1.02px LEFT of its box at that size, so a box
    placed exactly at the glyph edge left a 0.10px gap: the ™ touched the "X".

    Both corrections are stated in `em`, so they resolve against the mark's own font size
    and stay exact at every surface's scale rather than being re-derived per breakpoint:

      -0.134em      removes the dead space above the glyph, putting the INK top on the
                    cap line. This one belongs in `em`: the dead space is a property of
                    the face, so it scales with the mark. Measured, not guessed - Manrope
                    leaves 0.134em above the ™, and the mark's own left bearing is a
                    further 0.055em, which the gap below absorbs.
      +--cx-tm-gap  the visual gap, as a percentage of the LOCKUP. Deliberately NOT in
                    `em`: a gap that scales with the mark re-spaces the lockup every time
                    the mark is resized, which is how one pass ended up with the ™
                    touching the "X" and the next with it drifting 4.4px away.

    Verified by pixel measurement of the running Dev render, not by inspection.

    NOT `right`, deliberately: `right` would anchor the mark's RIGHT edge, so it would
    hang a fixed distance from the logo box's edge - which is the defect being fixed.
    `left` anchors where the wordmark ENDS and lets the mark sit just past it. */
.brand-tm-lockup > .brand-tm {
    color: currentColor;
    display: block;
    font-size: var(--cx-tm-size);
    font-weight: 600;
    left: calc(var(--cx-tm-x) + var(--cx-tm-gap));
    line-height: 1;
    opacity: .72;
    position: absolute;
    top: calc(var(--cx-tm-y) - 0.134em);

    /* A <sup> carries a vertical-align shift and a UA font-size reduction. The
       measured anchors already place the mark, so the shift is neutralised rather
       than corrected for a second time. */
    vertical-align: baseline;
}

/*  The three anchor sets: the artwork's measured glyph edges, nothing else. The optical
    gap and the ink corrections live once in the rule above.

    THE SIZE TARGETS THE MARK'S INK AT ABOUT 45% OF THE WORDMARK'S CAP HEIGHT, which is
    what a trademark mark reads as.

    The ratio was measured on the render, and the first measurement was WRONG in a way
    worth recording: the mark's ink box was scanned with a few pixels of slack around the
    element, and because the mark was then touching the "X" (0.10px of gap), the scan
    swallowed part of the X and reported the ™ as 0.835em of ink. It is actually
    **0.367em**. Sizing off the bad figure produced a mark 20% of cap height - a speck.
    So: font size = 0.45 x cap height / 0.367 = 1.226 x cap height, and cap height is
    10.97% of the lockup width, giving size = 0.134 x lockup width. Re-measured after. */
.brand-tm-lockup.is-expanded-lockup {
    --cx-tm-x: 99%;
    --cx-tm-y: 56.59%;
    --cx-tm-gap: 0.9%;

    /* 176px marketing lockup: cap 19.3px, so a 23.6px mark carries 8.7px of ink. */
    --cx-tm-size: 24px;
}

.brand-tm-lockup.is-compact-header-dark {
    --cx-tm-x: 90.43%;
    --cx-tm-y: 41.54%;
    --cx-tm-gap: 0.9%;

    /* 214px auth hero lockup: cap 13.4px, so a 16.4px mark carries 6.0px of ink. */
    --cx-tm-size: 16px;
}

/*  DECLARED BUT NOT CURRENTLY USED BY ANY SURFACE, deliberately and pending an owner
    decision. `caelowx-compact-header-light-512.png` is not the bare wordmark - it reads
    **caelowx.com**, a domain lockup - so a mark placed at its final glyph produces
    "caelowx.com™", which claims the domain rather than the name. The stated requirement
    is that a primary full wordmark reads "CaeloWX™", and this artwork does not read that
    at all, which puts it in the same bucket as the C-mark-only presentations the
    requirement excludes.

    So the auth cards' mobile header (Login, Register, BetaApply) carries no ™ today -
    unchanged from before this pass - while their desktop hero lockup, which IS the bare
    wordmark, does. The anchors below are measured and ready: if the owner wants the
    domain lockup marked, the only change is adding
    `<span class="brand-tm-lockup is-compact-header-light">` around that image plus the
    `<sup class="brand-tm">`, exactly as the dark hero does it. */
.brand-tm-lockup.is-compact-header-light {
    --cx-tm-x: 97.46%;
    --cx-tm-y: 38.62%;
    --cx-tm-gap: 0.9%;
    --cx-tm-size: 14px;
}

/* AppBrand's composite IS the wrapper - it already holds the three theme-gated
   lockups and the mark, and it is already the element the theme rules show and
   hide, so wrapping it again would add a box for nothing and a second ™ for each
   hidden variant. It takes the expanded-lockup anchors and the shared positioning
   above; `container-type` comes from `.brand-tm-lockup` on the same element.

   The mark stays gated with the composite, so the collapsed C-mark rail still
   carries no floating ™ - a C-mark is not a full wordmark. */
.brand-caelowx-composite.brand-tm-lockup {
    --cx-tm-x: 99%;
    --cx-tm-y: 56.59%;
    --cx-tm-gap: 0.9%;
    --cx-tm-size: 20px;
}

.weather-shell.theme-caelowx .brand-caelowx-composite > .brand-tm {
    display: block;
}

.weather-shell.theme-dark.theme-caelowx .brand-legacy {
    display: none;
}

.weather-shell.theme-light.theme-caelowx .brand-legacy {
    display: none;
}

.weather-shell.theme-light.theme-caelowx .brand-caelowx-lockup-light {
    display: block;
    height: auto;
    max-width: 100%;
    object-fit: contain;
    width: 180px;
}

.weather-shell.theme-light.theme-caelowx.sidebar-collapsed .brand-caelowx-lockup-light {
    display: none;
}

.weather-shell.theme-light.theme-caelowx.sidebar-collapsed .brand-caelowx-mark {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-sizing: border-box;
    display: block;
    height: 42px;
    object-fit: contain;
    transform: translateY(12px);
    width: 42px;
}

.weather-shell.theme-dark.theme-caelowx .brand-caelowx-lockup {
    display: block;
    height: auto;
    max-width: 100%;
    object-fit: contain;
    width: 180px;
}

.weather-shell.theme-dark.theme-caelowx.sidebar-collapsed .brand-caelowx-lockup {
    display: none;
}

.weather-shell.theme-dark.theme-caelowx.sidebar-collapsed .brand-caelowx-mark {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-sizing: border-box;
    display: block;
    height: 42px;
    object-fit: contain;
    transform: translateY(12px);
    width: 42px;
}

/* On an expanded desktop rail, the first destination shares the dashboard's
   content datum. The logo still clears it by 6px; only the old empty gap is removed. */
/*  ============================================================ THE COLLAPSED RAIL CARRIES NO ™

    THE COMPOSITE IS ONLY EVER SHOWN CONDITIONALLY AND WAS NEVER HIDDEN, which is the whole
    defect. Every rule that reveals the expanded lockup is written
    `...:not(.sidebar-collapsed) .brand-caelowx-composite`, so collapsing the rail simply stops
    those rules applying - it does not remove the container. The span stays in the layout at
    0 x 0, and `.brand-tm-lockup > .brand-tm` turns the mark on UNCONDITIONALLY and positions it
    `left: calc(var(--cx-tm-x) + var(--cx-tm-gap))` of its parent. Against a zero-width parent
    that resolves to the parent's own origin, so the ™ floated beside the compact C-mark with no
    wordmark to belong to. Measured in Chrome at 1440: rail 60px wide, composite 0 x 0 at x 57,
    and the mark still painted at x 57.

    AppBrand's own comment already said the mark was "hidden with the composite, so the collapsed
    C-mark rail carries no floating mark". That was the intent; nothing implemented it.

    OUTSIDE THE 901px MEDIA QUERY ON PURPOSE. The reveal rules live in one, but the rail collapses
    from 641px up (see `.weather-shell.sidebar-collapsed` in app.css), so between 641 and 900 the
    lockup was never shown AND the mark still rendered - the same defect, with even less to
    attach to. Hiding it is not viewport-dependent.

    SCOPED TO `.sidebar`, ALSO ON PURPOSE. The phone header renders the same `AppBrand`, and its
    lockup is not the rail's; an unscoped rule would blank the phone wordmark whenever the shell
    happened to be carrying the collapsed class. */
.weather-shell.sidebar-collapsed .sidebar .brand-caelowx-composite {
    display: none;
}

/*  ============================================================ SCROLLBARS

    THE APPLICATION'S OWN SCROLLBAR, EVERYWHERE, not per pane.

    `.wp-scroll` in report-controls.css already established this treatment - thin, rounded,
    transparent track, `--muted` at rest and `--text` while dragging - but it is opt-in, so every
    surface nobody remembered to mark kept the operating system's bright rail. On a dark
    application that rail is the loudest thing on the page. This is the same design promoted to
    the default, and `.wp-scroll` stays as the pane-level variant that also reserves its gutter.

    NO NEW COLOUR - AND THE TOKEN FAMILY MATTERS HERE. The pane version uses `--muted`, which is
    declared on `.weather-shell`. The document's own scrollbar is painted from <html>, which is
    OUTSIDE that element, so a rule written against `--muted` resolves to nothing exactly where
    the bright rail is most visible. Measured: `scrollbar-color` computed `auto` on the root.
    `--app-muted` and `--app-text` are declared on `:root`, so they reach the document bar and
    every pane beneath it alike.

    ONE KNOWN LIMIT. The light theme redefines `--app-*` on `body.theme-light`, and custom
    properties do not travel upwards, so the DOCUMENT bar keeps the dark values under a light
    theme; panes inside the shell inherit correctly either way. Hoisting those overrides to the
    root would fix it and is a change to the theme's structure rather than to this treatment.

    DELIBERATELY NOT `scrollbar-gutter: stable` GLOBALLY. `.wp-scroll` reserves the track because
    a results pane that gains a scrollbar must not shift sideways; imposing that on every element
    in the product would change layouts that are currently correct.

    THE OPT-OUTS STILL WIN. Several surfaces hide their scrollbar on purpose - the Reports tab
    strip, the alerts and dynamic-alerts card panes. Those are class selectors, most of them
    `!important`, and the base here is the universal selector, which loses to all of them. That
    is why this is safe to make global rather than a sweep of per-surface edits. */

/*  Firefox and the standard property. Inherited, so the root covers the document and every pane
    under it; the two-value form is thumb then track. */
:root {
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--app-muted) 42%, transparent) transparent;
}

:root:hover {
    scrollbar-color: color-mix(in srgb, var(--app-muted) 60%, transparent) transparent;
}

/*  WebKit. A 10px track carrying a 4px thumb: the 3px transparent border plus
    `background-clip: padding-box` is what makes it slim without being hard to grab. */
*::-webkit-scrollbar {
    height: 10px;
    width: 10px;
}

*::-webkit-scrollbar-track,
*::-webkit-scrollbar-corner {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--app-muted) 42%, transparent);
    background-clip: padding-box;
    border: 3px solid transparent;
    border-radius: 999px;
    min-height: 36px;
}

*::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--app-muted) 68%, transparent);
    background-clip: padding-box;
}

*::-webkit-scrollbar-thumb:active {
    background: color-mix(in srgb, var(--app-text) 55%, transparent);
    background-clip: padding-box;
}

/*  ---------------------------------------------------------------- the chevrons

    STYLING `::-webkit-scrollbar-button` IS WEBKIT-ONLY, and that is the whole reason it is safe
    to use: Firefox ignores these rules entirely and simply shows the thin thumb above, which is
    the graceful fallback rather than a missing feature. No JavaScript is involved and scrolling
    stays native - these are the browser's own buttons, given the application's face.

    DRAWN WITH A MASK, NOT A BACKGROUND IMAGE. A chevron baked into a data URI carries its colour
    with it, and this palette is a token. Masking a shape and filling it with `background-color`
    keeps the arrow on `--muted` and lets it brighten with the same steps the thumb uses.

    CHROME RENDERS FOUR BUTTON SLOTS PER AXIS - a decrement and an increment at BOTH ends. Only
    the outer pair is wanted; the phantom inner pair is hidden, or every bar grows a second
    chevron pointing the wrong way in the middle of nowhere. */

*::-webkit-scrollbar-button {
    background-color: color-mix(in srgb, var(--app-muted) 42%, transparent);
    -webkit-mask-position: center;
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-size: 9px 9px;
    display: block;
}

*::-webkit-scrollbar-button:hover {
    background-color: color-mix(in srgb, var(--app-muted) 68%, transparent);
}

*::-webkit-scrollbar-button:active {
    background-color: color-mix(in srgb, var(--app-text) 55%, transparent);
}

*::-webkit-scrollbar-button:vertical {
    height: 12px;
    width: 10px;
}

*::-webkit-scrollbar-button:horizontal {
    height: 10px;
    width: 12px;
}

*::-webkit-scrollbar-button:vertical:start:decrement {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 7.25 6 3.75l3.5 3.5' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

*::-webkit-scrollbar-button:vertical:end:increment {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.75 6 8.25l3.5-3.5' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

*::-webkit-scrollbar-button:horizontal:start:decrement {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M7.25 2.5 3.75 6l3.5 3.5' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

*::-webkit-scrollbar-button:horizontal:end:increment {
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M4.75 2.5 8.25 6l-3.5 3.5' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/*  The phantom inner pair. Chrome reserves them whether or not they are wanted. */
*::-webkit-scrollbar-button:vertical:start:increment,
*::-webkit-scrollbar-button:vertical:end:decrement,
*::-webkit-scrollbar-button:horizontal:start:increment,
*::-webkit-scrollbar-button:horizontal:end:decrement {
    display: none;
}


/*  ============================================================ SINGLE SELECT

    The platform's own dropdown, for `CaelowxSelect`. A native <select> cannot be themed once
    open - the popup belongs to the operating system - so on a dark application it flashes
    white. This is the replacement, and it is global because that problem is.

    THEMED THROUGH CUSTOM PROPERTIES, NOT THROUGH VARIANTS. Every colour the control uses reads
    from a `--cx-select-*` property that defaults to the application palette. A surface with its
    own palette - Account is themed blue - sets those properties on a container and gets its own
    appearance from the one component. Duplicating the component per surface is the thing this
    arrangement exists to prevent.

    THE TRIGGER IS THE CANONICAL FIELD. It takes `--app-field-*` and `--app-radius-md`, the same
    geometry `.app-select` and the Account inputs use, so a dropdown and a text input on one form
    are the same height and shape. */

.cx-select {
    --cx-select-bg: var(--app-surface-2);
    --cx-select-border: var(--app-border);
    --cx-select-text: var(--app-text);
    --cx-select-accent: var(--app-accent);
    --cx-select-accent-soft: var(--app-accent-soft);
    --cx-select-menu-bg: var(--app-surface-2);
    --cx-select-option-hover: var(--app-surface-3);

    position: relative;
}

.cx-select-trigger {
    align-items: center;
    background: var(--cx-select-bg);
    border: 1px solid var(--cx-select-border);
    border-radius: var(--app-radius-md);
    color: var(--cx-select-text);
    cursor: pointer;
    display: flex;
    font: inherit;
    gap: var(--cx-space-3);
    justify-content: space-between;
    min-height: var(--app-field-min-height);
    padding: var(--app-field-padding-y) var(--app-field-padding-x);
    text-align: left;
    width: 100%;
}

/*  The same focus treatment every other field has - see `.app-select:focus`. */
.cx-select-trigger:focus-visible {
    border-color: var(--cx-select-accent);
    box-shadow: 0 0 0 var(--app-field-focus-ring) var(--cx-select-accent-soft);
    outline: none;
}

/*  OPEN IS A FOCUSED STATE EVEN WHEN THE RING IS NOT DRAWN, because a menu is hanging off it. */
.cx-select.is-open .cx-select-trigger {
    border-color: var(--cx-select-accent);
}

/*  Unavailable rather than merely empty, matching `.app-select:disabled`. */
.cx-select.is-disabled .cx-select-trigger,
.cx-select-trigger:disabled {
    cursor: not-allowed;
    opacity: .6;
}

.cx-select-value {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cx-select-caret {
    color: var(--app-muted);
    display: inline-flex;
    flex: 0 0 auto;
    transition: transform var(--cx-transition);
}

.cx-select-caret svg {
    fill: none;
    height: 16px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2;
    width: 16px;
}

.cx-select.is-open .cx-select-caret {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .cx-select-caret {
        transition: none;
    }
}

/*  One element rather than a document listener, the same dismissal the Builder uses. */
.cx-select-backdrop {
    background: transparent;
    inset: 0;
    position: fixed;
    z-index: var(--cx-z-popover-backdrop);
}

.cx-select-menu {
    /*  A HAIR BETWEEN ROWS, SO TWO LIT ROWS ARE TWO ROWS.

        A selected option and the hovered option beside it both carry a background, and with the
        rows flush those two backgrounds met and read as one block. `--cx-space-1` is the
        smallest step on the scale and is all this needs - enough to separate the fills, not
        enough to make the panel feel like a list of cards. No borders, no per-row chrome. */
    display: flex;
    flex-direction: column;
    gap: var(--cx-space-1);

    background: var(--cx-select-menu-bg);
    border: 1px solid var(--cx-select-border);
    border-radius: var(--app-radius-md);
    box-shadow: var(--app-shadow);
    left: 0;
    list-style: none;
    margin: 0;

    /*  Enough to show that scrolling is worthwhile without becoming the page. */
    max-height: 17rem;
    overflow-y: auto;
    padding: var(--cx-space-2);
    position: absolute;
    top: calc(100% + var(--cx-space-2));

    /*  AS WIDE AS THE TRIGGER, AND NEVER WIDER THAN THE SCREEN. A menu narrower than the
        control it belongs to reads as a different object; one wider than the viewport is the
        phone defect every hand-placed popover in this repo has had at least once. */
    min-width: 100%;
    max-width: calc(100vw - (var(--cx-space-6) * 2));
    box-sizing: border-box;

    z-index: var(--cx-z-popover);
}

/*  FLOATING, WHICH IS THE NORMAL CASE.

    Absolute positioning is clipped by any ancestor whose overflow is not visible, and the
    Account sections are `overflow: hidden` - so the menu was cut off by the card it opened
    inside. Fixed positioning is measured against the viewport and escapes that entirely.

    The coordinates are inline because they are measurements, not style: left, top and width come
    from the trigger's own box, and `max-height` appears only when neither side of the trigger
    can hold the panel whole. The rule above still applies when there is no JavaScript to measure
    with, which is why it keeps its absolute placement. */
.cx-select-menu.is-floating {
    position: fixed;
    top: auto;
    left: auto;
    min-width: 0;
    max-height: 17rem;
}

.cx-select-option {
    align-items: center;
    border-radius: var(--cx-radius-sm);
    color: var(--cx-select-text);
    cursor: pointer;
    display: flex;
    font-size: var(--cx-text-md);
    gap: var(--cx-space-3);

    /*  A finger's worth, which is the floor the Reports menus already adopted for touch. */
    min-height: 40px;
    padding: var(--cx-space-2) var(--cx-space-3);
}

/*  HOVER AND KEYBOARD READ THE SAME, because they mean the same thing. `is-active` is where the
    arrow keys are; the browser never draws that, so it has to be drawn here. */
.cx-select-option:hover,
.cx-select-option.is-active {
    background: var(--cx-select-option-hover);
}

/*  SELECTED IS THE ACCENT AND A TICK - not the browser's own bright blue row, which is the
    treatment this control exists to stop showing. */
.cx-select-option.is-selected {
    color: var(--cx-select-accent);
    font-weight: 700;
}

.cx-select-option.is-selected.is-active,
.cx-select-option.is-selected:hover {
    background: var(--cx-select-accent-soft);
}

.cx-select-tick {
    display: inline-flex;
    flex: 0 0 auto;
    height: 14px;
    width: 14px;
}

.cx-select-tick svg {
    fill: none;
    height: 14px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2.5;
    width: 14px;
}

.cx-select-option-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}


/*  ============================================================ RESULT LISTS

    A SEARCH RESULT IS SOMEWHERE TO GO, NOT SOMETHING TO READ. Every one of these rows answers
    the same shape of question - which thing did I mean? - with a primary label, a line of
    supporting text and a chevron saying that choosing it takes you somewhere. The Journal's note
    search is the first caller; reports, stations and cases all want the same row, so it is here
    rather than in one page's stylesheet.

    NOT `.rb-opt`, WHICH IS THE OTHER THING THIS COULD HAVE BEEN. A filter option sets a value
    and stays put; a result row navigates. They read differently on purpose - an option is a
    choice within a control, a result is a destination - and collapsing them would make the
    chevron a lie on half the call sites.

    WHAT A CALLER SUPPLIES is a `.cx-result-summary` line and a `.cx-results` list of
    `.cx-result` buttons. Placement, width and how the list is introduced belong to the page. */

.cx-result-summary {
    align-items: baseline;
    color: var(--muted);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--cx-text-md);
    gap: var(--cx-space-3);
    justify-content: space-between;
}

/*  The count reads in the page's own voice, so the searched term is the only emphasis. */
.cx-result-summary strong {
    color: var(--text);
    font-weight: 700;
}

/*  DISMISSAL BELONGS TO THE INTERACTION THAT CREATED THE RESULTS, which is why it sits on the
    summary line rather than floating as an action of its own. Text, not a bordered button: a box
    here would compete with the rows underneath, which are the things meant to be clicked. */
.cx-result-dismiss {
    background: none;
    border: 0;
    border-radius: var(--cx-radius-sm);
    color: var(--accent);
    cursor: pointer;
    font: inherit;
    font-size: var(--cx-text-md);
    font-weight: 700;
    padding: var(--cx-space-1) var(--cx-space-2);
}

.cx-result-dismiss:hover {
    background: var(--accent-soft);
}

.cx-results {
    display: flex;
    flex-direction: column;
    gap: var(--cx-space-2);
    list-style: none;
    margin: var(--cx-space-4) 0 0;
    padding: 0;

    /*  BOUNDED, SO RESULTS NEVER BECOME THE PAGE. Long enough to show that scrolling is
        worthwhile, short enough that the surface being searched stays visible underneath. */
    max-height: 19rem;
    overflow-y: auto;
}

.cx-result {
    align-items: center;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--cx-radius-md);
    color: var(--text);
    cursor: pointer;
    display: flex;
    font: inherit;
    gap: var(--cx-space-5);
    padding: var(--cx-space-4) var(--cx-space-5);
    text-align: left;
    transition: border-color var(--cx-transition), background var(--cx-transition);
    width: 100%;
}

.cx-result:hover {
    background: color-mix(in srgb, var(--surface-2) 84%, var(--accent) 16%);
    border-color: var(--accent);
}

/*  THE SAME FOCUS TREATMENT EVERY CONTROL USES - see `.rbrowse-control:focus-within`. A result
    list is reached by keyboard more often than most surfaces, because it appears in response to
    typing and the hands are already there. */
.cx-result:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
    outline: none;
}

.cx-result-body {
    display: flex;
    flex-direction: column;
    gap: var(--cx-space-1);
    min-width: 0;
}

.cx-result-label {
    color: var(--text);
    font-size: var(--cx-text-lg);
    font-weight: 700;
    line-height: 1.25;
}

/*  ONE LINE, CLAMPED. The excerpt is there to confirm the row is the one meant, not to be read
    in full - the destination has the whole text. */
.cx-result-detail {
    color: var(--muted);
    display: -webkit-box;
    font-size: var(--cx-text-md);
    line-height: 1.45;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
}

/*  THE AFFORDANCE. It says the row leads somewhere, and it brightens with the row so the whole
    thing reads as one target rather than as a row with a button on the end. */
.cx-result-go {
    color: var(--muted);
    flex: 0 0 auto;
    margin-left: auto;
    transition: color var(--cx-transition), transform var(--cx-transition);
}

.cx-result:hover .cx-result-go,
.cx-result:focus-visible .cx-result-go {
    color: var(--accent);
    transform: translateX(2px);
}

@media (prefers-reduced-motion: reduce) {
    .cx-result,
    .cx-result-go {
        transition: none;
    }

    .cx-result:hover .cx-result-go,
    .cx-result:focus-visible .cx-result-go {
        transform: none;
    }
}



/*  AND THE OTHER TRIGGER, BECAUSE THE COMPACT RAIL HAS TWO.

    `.sidebar-collapsed` is a persisted reader preference; the 641-900px band is a width rule.
    app.css states the principle - "they are one design with two triggers" - and the same has to
    be true of the lockup, or the defect simply moves to whichever trigger was not handled.

    It was: at 800px the rail is already 68px and the reveal rules are 901px-up, so the EXPANDED
    rail in that band showed no wordmark and still painted the mark. Measured in Chrome at 800:
    rail 68px, composite display block, ™ at x 60 - hard against the rail edge.

    The band's own compact-rail rules live in app.css; this one lives here with the rest of the
    composite because the composite is this file's, and splitting a brand rule across two
    stylesheets is how the next reader fails to find it. */
@media (min-width: 641px) and (max-width: 900px) {
    .sidebar .brand-caelowx-composite {
        display: none;
    }
}


@media (min-width: 901px) {
    body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header:not(.sidebar-collapsed) .sidebar {
        padding-top: 0;
    }

    .weather-shell.theme-light.theme-caelowx:not(.sidebar-collapsed) .brand,
    .weather-shell.theme-dark.theme-caelowx:not(.sidebar-collapsed) .brand {
        align-items: center;
        background-image: linear-gradient(var(--cx-border), var(--cx-border));
        background-position: center bottom;
        background-repeat: no-repeat;
        background-size: calc(100% - 24px) 1px;
        box-sizing: border-box;
        flex: 0 0 var(--cx-compact-chrome-size);
        height: var(--cx-compact-chrome-size);
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
        padding-bottom: 0;
    }

    .weather-shell.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-lockup-light,
    .weather-shell.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-lockup {
        display: none;
    }

    /*  THE BOX IS THE ARTWORK'S BOX, which is what lets the ™ anchors above be the
        artwork's own coordinates.

        It used to be `height: 48px` while the image rendered 174 x 55.97 and
        overflowed it (`overflow: visible` hid the consequence). So the container's
        top was 4px BELOW the image's top and its centre was not the image's centre -
        which is why a ™ positioned against this box could never be positioned against
        the wordmark. `aspect-ratio` derives the height from the one definite
        dimension, so the container and the artwork are the same rectangle by
        construction, at any width. 401 x 129 is the lockup PNG's intrinsic size. */
    .weather-shell.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-composite {
        /*  THE LOCKUP IS THE ARTWORK PLUS THE MARK, AND THE UNIT HAS TO FIT THE RAIL'S OWN
            CONTENT COLUMN.

            The rail is 198px with symmetric 12px padding, so its content column is 174px -
            the width every nav item already respects. The brand was the only element
            ignoring it: the artwork alone was 174px and the ™ hung ~22px past the column,
            leaving 12px of air on the left and about 2px on the right. Read as one lockup,
            that is not a mark placement problem, it is a lockup that does not fit.

            So the artwork is sized so that artwork + mark lands inside the column:
            the mark starts at 99.5% of the artwork and its advance is about 0.91 of its
            font size, which is itself the artwork's cap height (10.97% of width) - so the
            unit is about 1.095x the artwork. 156px of artwork gives a 170.7px unit, which
            sits inside the 174px column with 15px of clear space to the rail edge against
            12px on the left. The mark stays tight to the "X"; the whole lockup moved.

            NOTHING VERTICAL CHANGES. `.brand` is a fixed 60px row and the composite is
            centred in it, so a shorter lockup (50.2px against 56px) shifts no nav item -
            measured: the nav list stays at y 70. The rail keeps its width and the top bar
            keeps its height. The artwork is scaled on one axis with `aspect-ratio`
            preserved, so it is not distorted. */
        --cx-brand-rail-lockup: 150px;

        align-items: center;
        aspect-ratio: 401 / 129;
        display: flex;
        gap: 0;
        height: auto;
        justify-content: center;
        overflow: visible;
        position: relative;
        width: var(--cx-brand-rail-lockup);
    }

    .brand-caelowx-composite-light-static,
    .brand-caelowx-composite-dark-static,
    .brand-caelowx-composite-nightshade-static {
        display: none;
    }

    .weather-shell.theme-light.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-composite-light-static {
        display: block;
        height: auto;
        object-fit: contain;
        width: var(--cx-brand-rail-lockup);
    }

    /*  THE RAIL IS THE ONE SURFACE WITH NO ROOM FOR THE FULL-SIZE MARK, and it was
        measured rather than guessed. The lockup ends 13px short of the rail's inner
        edge (ink at x 185, rail boundary at 198), and a 19px ™ has a 17.3px advance -
        so at full size the mark crossed the boundary by 4.5px and the content panel
        painted over its right half. Verified in Chrome at 1440: the "M" was cut clean
        down the middle at the rail edge.

        13px fits with its ink ending about 2px inside the boundary, and reads as a
        deliberately subtle mark rather than a clipped one. This is a constraint of the
        174px rail lockup, not a change of intent: every surface with room keeps the
        proportional cap-height size. If the rail lockup were narrowed by ~8px the
        override could go, which is an owner decision about approved chrome and not a
        change to make while fixing a ™. */
    .weather-shell.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-composite.brand-tm-lockup {
        /* 150px rail lockup: cap 16.5px, so a 20px mark carries 7.3px of ink. */
        --cx-tm-size: 20px;
    }

    /*  THE LOCKUP SHARES THE NAV'S LEFT EDGE.

        `.brand` is a centring flex row, which was invisible while the lockup filled the
        whole 173px column and became visible the moment it did not: a 156px lockup was
        centred at x 20.5, so it sat 8.5px right of the 12px datum every nav item starts
        on, and the ™ then finished at 191.2 - past the nav column's own right edge and
        back within 7px of the rail.

        An `auto` end margin beats the parent's `justify-content` for this one item, so the
        brand row's rule is left alone and only the lockup moves. The unit now runs
        12 -> 182.7: flush left with Dashboard, Stations and Forecast, ending inside the
        173px column, with 15px of clear space before the rail edge. */
    .weather-shell.theme-caelowx:not(.sidebar-collapsed) .brand .brand-home {
        margin-inline-end: auto;
    }

    /*  THE -2px OPTICAL NUDGE MOVED FROM THE IMAGE TO THE CONTAINER, so the ™ travels
        with the artwork it belongs to. A transform does not affect layout, so the rail
        is unchanged and the lockup lands exactly where it did; the difference is that
        the mark, a child of the container, is now carried by the same 2px rather than
        being left behind by it. Applied to the container once for both dark variants
        instead of to each image. */
    .weather-shell.theme-dark.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-composite {
        transform: translateY(-2px);
    }

    .weather-shell.theme-dark.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-composite-dark-static {
        display: block;
        height: auto;
        object-fit: contain;
        width: var(--cx-brand-rail-lockup);
    }

    .weather-shell.theme-dark.theme-oled.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-composite-dark-static {
        display: none;
    }

    .weather-shell.theme-dark.theme-oled.theme-caelowx:not(.sidebar-collapsed) .brand-caelowx-composite-nightshade-static {
        display: block;
        height: auto;
        object-fit: contain;
        width: var(--cx-brand-rail-lockup);
    }

    body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header:not(.sidebar-collapsed) .nav-list {
        margin-top: 10px;
    }

    .weather-shell.theme-dark.theme-caelowx:not(.sidebar-collapsed) .nav-list {
        margin-top: 6px;
    }

    /* The Dashboard's app-top artwork establishes a taller first-row datum.
       Keep the first destination aligned with the Alerts strip in both rail
       states, matching the approved desktop composition. */
    .weather-shell.theme-dark.theme-caelowx.dashboard-hero-header:not(.sidebar-collapsed) .nav-list {
        margin-top: 43px;
    }

    .weather-shell.theme-dark.theme-caelowx.dashboard-hero-header.sidebar-collapsed .nav-list {
        margin-top: 52px;
    }
}

@media (min-width: 641px) and (max-width: 900px) {
    .weather-shell.theme-light.theme-caelowx .brand-caelowx-lockup-light {
        display: none;
    }

    .weather-shell.theme-light.theme-caelowx .brand-caelowx-mark {
        display: block;
        height: 42px;
        transform: translateY(8px);
        width: 42px;
    }

    .weather-shell.theme-dark.theme-caelowx .brand-caelowx-lockup {
        display: none;
    }

    .weather-shell.theme-dark.theme-caelowx .brand-caelowx-mark {
        background: transparent;
        border: 0;
        border-radius: 0;
        box-sizing: border-box;
        display: block;
        height: 42px;
        object-fit: contain;
        transform: translateY(8px);
        width: 42px;
    }
}

@media (max-width: 640px) {
    /* Mobile now uses the same approved expanded lockup as desktop - one shared size
       for all three themes (the three assets are dimensionally identical), so light,
       dark and nightshade present at a consistent scale and baseline. The
       compact-header lockups and the standalone C mark are not shown on the mobile
       bar, including when the shell carries a persisted desktop rail-collapsed state
       on a phone. */
    .weather-shell.theme-light.theme-caelowx .brand-caelowx-lockup-light,
    .weather-shell.theme-dark.theme-caelowx .brand-caelowx-lockup,
    .weather-shell.theme-dark.theme-oled.theme-caelowx .brand-caelowx-lockup-nightshade,
    .weather-shell.theme-light.theme-caelowx .brand-caelowx-mark,
    .weather-shell.theme-dark.theme-caelowx .brand-caelowx-mark,
    .weather-shell.theme-light.theme-caelowx.sidebar-collapsed .app-mobile-bar .brand-caelowx-mark,
    .weather-shell.theme-dark.theme-caelowx.sidebar-collapsed .app-mobile-bar .brand-caelowx-mark {
        display: none;
    }

    /*  Same reasoning as the rail: the container is the artwork's rectangle. `width:
        auto` cannot stay - an inline-size query container is sized without consulting
        its contents, so an auto width would collapse to zero and take the lockup with
        it. The height is the definite dimension here (the phone bar sets 32px), so
        `aspect-ratio` supplies the width. */
    .weather-shell.theme-caelowx .brand-caelowx-composite {
        /*  28px, DOWN FROM 32px, TO BUY THE HEADER'S CENTRE ZONE ITS ROOM.

            The phone bar is three zones and the centre one is the station name. At a 32px
            lockup the left zone's visual width is 111px (99.5 of artwork plus the ™'s 12px
            of overhang), the controls need 84, and true centring requires both outer columns
            to be equal - which capped the station name at 125px and clipped
            `WR Weather Station` at any size inside the agreed 55-65% range. At 28px the
            lockup is 87px wide, the left zone needs 99, and the name fits at 63% of the
            lockup's height with the centre still exactly centred.

            The bar's own height is NOT touched: it stays 56px, set by
            --cx-mobile-bar-height, and the lockup was never what determined it. Width and
            height move together through `aspect-ratio`, so the artwork is not distorted. */
        align-items: center;
        aspect-ratio: 401 / 129;
        display: flex;
        height: 28px;
        justify-content: center;
        overflow: visible;
        position: relative;
        width: auto;
    }

    /*  THE ONE SIZE OVERRIDE IN THE WHOLE TREATMENT, and it is a real difference rather
        than a tweak: the phone bar renders the same expanded lockup at 32px tall - about
        99px wide against the rail's 174px - so its wordmark's cap height is 11px, not
        19px. The POSITION anchors are percentages and need no override; only the length
        does. */
    .weather-shell.theme-caelowx .brand-caelowx-composite.brand-tm-lockup {
        /* 87px phone-bar lockup: cap 9.5px. */
        --cx-tm-size: 12px;
    }

    .weather-shell.theme-light.theme-caelowx .brand-caelowx-composite-light-static {
        display: block;
        height: 28px;
        object-fit: contain;
        width: auto;
    }

    .weather-shell.theme-dark.theme-caelowx .brand-caelowx-composite-dark-static {
        display: block;
        height: 28px;
        object-fit: contain;
        width: auto;
    }

    .weather-shell.theme-dark.theme-oled.theme-caelowx .brand-caelowx-composite-dark-static {
        display: none;
    }

    .weather-shell.theme-dark.theme-oled.theme-caelowx .brand-caelowx-composite-nightshade-static {
        display: block;
        height: 28px;
        object-fit: contain;
        width: auto;
    }
}

/* ===================================================== Nightshade charcoal glass

   Nightshade reverses the usual CaeloWX depth model: its neutral graphite canvas
   is lighter than the smoked-black cards sitting on it. Weather measurements keep
   their semantic colors, but chrome, borders, shadows and navigation contain no
   blue tint. These rules live last so Dark's blue atmospheric depth cannot leak
   back into this deliberately achromatic treatment. */
.weather-shell.theme-dark.theme-oled.theme-caelowx.dashboard-hero-header .dashboard-main {
    background:
        radial-gradient(circle at 42% -12%, rgba(255, 255, 255, .055), transparent 34rem),
        linear-gradient(180deg, #3d3d3d 0%, var(--cx-background) 48rem);
}

.weather-shell.theme-dark.theme-oled.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-card {
    background:
        radial-gradient(ellipse at 16% -8%, rgba(255, 255, 255, .075) 0%, transparent 44%),
        linear-gradient(148deg, #292929 0%, var(--cx-card) 58%, #1d1d1d 100%);
    border-color: #565656;
    box-shadow:
        0 12px 26px rgba(0, 0, 0, .34),
        inset 0 1px 0 rgba(255, 255, 255, .11),
        inset 0 -1px 0 rgba(0, 0, 0, .58);
}

.weather-shell.theme-dark.theme-oled.theme-caelowx.dashboard-hero-header .wpdn-grid .wpc-tile {
    background: linear-gradient(180deg, #222222 0%, #191919 100%);
    border-color: #4d4d4d;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .09),
        0 5px 14px rgba(0, 0, 0, .28);
}

.weather-shell.theme-dark.theme-oled.theme-caelowx .sidebar {
    background: linear-gradient(180deg, #303030 0%, #292929 100%);
    border-color: #565656;
}

body.banner-compact .weather-shell.theme-dark.theme-oled.theme-caelowx.dashboard-hero-header .topbar {
    background: linear-gradient(100deg, #343434 0%, #2b2b2b 100%);
    border-color: #565656;
}

.weather-shell.theme-dark.theme-oled.theme-caelowx .nav-item,
.weather-shell.theme-dark.theme-oled.theme-caelowx .header-button,
.weather-shell.theme-dark.theme-oled.theme-caelowx .sidebar-toggle,
.weather-shell.theme-dark.theme-oled.theme-caelowx .user-menu-panel {
    border-color: #515151;
}

/* Nightshade reserves brand color for the C mark and pairs it with a neutral-silver wordmark. */
.weather-shell.theme-dark.theme-oled.theme-caelowx .brand-caelowx-mark {
    filter: none;
}

.weather-shell.theme-dark.theme-oled.theme-caelowx .nav-item.active {
    background: linear-gradient(145deg, #3d3d3d 0%, #373737 100%);
    border-color: #5c5c5c;
    color: #eeeeee;
}

.weather-shell.theme-dark.theme-oled.theme-caelowx .nav-item.active:hover,
.weather-shell.theme-dark.theme-oled.theme-caelowx .nav-item.active:focus-visible {
    background: linear-gradient(145deg, #414141 0%, #3a3a3a 100%);
    border-color: #686868;
    color: #ffffff;
}

.weather-shell.theme-dark.theme-oled.theme-caelowx .radar-map {
    background: #212121;
    border-color: #565656;
}

/* Radar keeps the clearer light Streets basemap in both themes. Dark-theme
   continuity comes from the controls layered over it, not from dimming the map. */
.weather-shell.theme-dark.theme-caelowx .radar-controls,
.weather-shell.theme-dark.theme-caelowx .radar-control-bar,
.weather-shell.theme-dark.theme-caelowx .radar-playback-controls,
.weather-shell.theme-dark.theme-caelowx .radar-time,
.weather-shell.theme-dark.theme-caelowx .radar-stale {
    background: color-mix(in srgb, var(--cx-surface) 92%, transparent);
    border-color: color-mix(in srgb, var(--cx-border) 88%, var(--cx-primary));
    box-shadow: 0 5px 18px color-mix(in srgb, var(--cx-background) 46%, transparent);
    color: var(--cx-text-primary);
}

.weather-shell.theme-dark.theme-caelowx .radar-controls button,
.weather-shell.theme-dark.theme-caelowx .radar-control-bar button,
.weather-shell.theme-dark.theme-caelowx .radar-playback-controls button {
    color: var(--cx-text-primary);
}

.weather-shell.theme-dark.theme-caelowx .radar-controls button:hover,
.weather-shell.theme-dark.theme-caelowx .radar-controls button:focus-visible,
.weather-shell.theme-dark.theme-caelowx .radar-control-bar button:hover,
.weather-shell.theme-dark.theme-caelowx .radar-control-bar button:focus-visible,
.weather-shell.theme-dark.theme-caelowx .radar-playback-controls button:hover,
.weather-shell.theme-dark.theme-caelowx .radar-playback-controls button:focus-visible {
    background: color-mix(in srgb, var(--cx-primary) 24%, transparent);
    color: var(--cx-brand-accent);
}

.weather-shell.theme-dark.theme-caelowx .leaflet-bar {
    border: 1px solid color-mix(in srgb, var(--cx-border) 88%, var(--cx-primary));
    border-radius: 7px;
    box-shadow: 0 5px 18px color-mix(in srgb, var(--cx-background) 38%, transparent);
    overflow: hidden;
}

.weather-shell.theme-dark.theme-caelowx .leaflet-bar a,
.weather-shell.theme-dark.theme-caelowx .leaflet-bar a:hover {
    background: var(--cx-surface);
    border-bottom-color: var(--cx-border);
    color: var(--cx-text-primary);
}

.weather-shell.theme-dark.theme-caelowx .leaflet-bar a:hover,
.weather-shell.theme-dark.theme-caelowx .leaflet-bar a:focus-visible {
    background: color-mix(in srgb, var(--cx-primary) 24%, var(--cx-surface));
    color: var(--cx-brand-accent);
}

/* A STATIC HEADER-ART PROOF, not the rotator described in the PRD. Dashboard
   opts into this variant through AppShell; the existing top bar remains the
   content and the landscape becomes part of the app chrome rather than a new
   card. Light mode and every other page retain their established header. */
body.banner-scenic .weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar {
    --cx-dashboard-banner: url("branding/caelowx-banner-kansas-daylight-header-standard-v1.webp");
    background-image:
        linear-gradient(90deg, rgba(203, 224, 235, .94) 0%, rgba(203, 224, 235, .72) 27%, rgba(189, 212, 226, .18) 52%, rgba(189, 212, 226, .24) 100%),
        linear-gradient(0deg, rgba(6, 23, 47, .08) 0%, transparent 64%),
        var(--cx-dashboard-banner);
    background-position: center;
    background-size: cover;
    border: 0;
    border-bottom: 1px solid color-mix(in srgb, var(--cx-border) 70%, var(--cx-primary));
    border-radius: 0;
    box-shadow: 0 10px 24px rgba(6, 23, 47, .12), inset 0 -1px 0 rgba(255, 255, 255, .7);
    box-sizing: border-box;
    margin: -14px -18px 10px;
    min-height: 108px;
    overflow: visible;
    padding: 16px 20px;
    position: relative;
    z-index: var(--cx-z-menu);
}

.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar .station-heading,
.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar .header-actions {
    position: relative;
    z-index: 1;
}

.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar .station-heading h1,
.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar .station-heading p {
    color: var(--cx-text-primary);
    text-shadow: 0 1px 10px rgba(213, 229, 238, .92);
}

.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar .station-heading .eyebrow {
    color: color-mix(in srgb, var(--cx-primary) 82%, var(--cx-text-primary));
}

.weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar .header-button {
    backdrop-filter: blur(8px);
    background: rgba(220, 234, 241, .88);
    border-color: color-mix(in srgb, var(--cx-border) 76%, var(--cx-primary));
    box-shadow: 0 4px 14px rgba(6, 23, 47, .12);
    color: var(--cx-text-primary);
}

/* Artwork is optional app chrome. The compact treatment is the default and keeps
   the station identity and account controls on one restrained 60px line. */
body.banner-compact .weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar,
body.banner-compact .weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar {
    border: 0;
    border-bottom: 1px solid var(--cx-border);
    border-radius: 0;
    box-shadow: 0 6px 18px color-mix(in srgb, var(--cx-background) 24%, transparent);
    box-sizing: border-box;
    margin: -14px -18px 10px;
    height: var(--cx-compact-chrome-size);
    min-height: var(--cx-compact-chrome-size);
    overflow: visible;
    padding: 8px 20px;
    position: relative;
    z-index: var(--cx-z-menu);
}

body.banner-compact .weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar {
    background-image: linear-gradient(100deg, #c2dbe8 0%, #d5e5ee 46%, #c9dfeb 100%);
}

body.banner-compact .weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar {
    background-image: linear-gradient(
        100deg,
        color-mix(in srgb, var(--cx-background) 82%, var(--cx-primary)) 0%,
        var(--cx-surface) 46%,
        color-mix(in srgb, var(--cx-surface) 88%, var(--cx-brand-accent)) 100%);
}

body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header .topbar .station-heading {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 44px;
}

body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header .topbar .station-heading .eyebrow {
    font-size: .5rem;
    line-height: 1.05;
}

body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header .topbar .station-heading h1 {
    font-size: 1.35rem;
    line-height: 1.05;
    margin: 2px 0 0;
}

body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header .topbar .station-heading p {
    font-size: .56rem;
    line-height: 1.05;
    margin: 2px 0 0;
}

/* Mobile: the compact chrome is only ~60px tall and stacks eyebrow + name + subtitle,
   so a 1.35rem name crowds the line vertically. A smaller name restores breathing room
   without reading as secondary. Desktop keeps 1.35rem. */
@media (max-width: 640px) {
    body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header .topbar .station-heading h1 {
        font-size: 1.12rem;
        line-height: 1.04;
        letter-spacing: -.02em;
    }

    body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header .topbar .station-heading .eyebrow {
        font-size: .47rem;
    }

    body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header .topbar .station-heading p {
        font-size: .53rem;
    }
}

@media (min-width: 641px) {
    .weather-shell.theme-caelowx.sidebar-collapsed {
        grid-template-columns: var(--cx-compact-chrome-size) minmax(0, 1fr);
    }

    .weather-shell.theme-caelowx.sidebar-collapsed .sidebar {
        padding-left: var(--cx-rail-gutter);
        padding-right: var(--cx-rail-gutter);
        padding-top: 0;
    }

    .weather-shell.theme-caelowx.sidebar-collapsed .brand {
        align-items: center;
        background-image: linear-gradient(var(--cx-border), var(--cx-border));
        background-position: center bottom;
        background-repeat: no-repeat;
        background-size: calc(100% - 20px) 1px;
        box-sizing: border-box;
        flex: 0 0 var(--cx-compact-chrome-size);
        height: var(--cx-compact-chrome-size);
        justify-content: center;
        padding: 0;
    }

    .weather-shell.theme-light.theme-caelowx.sidebar-collapsed .brand-caelowx-mark,
    .weather-shell.theme-dark.theme-caelowx.sidebar-collapsed .brand-caelowx-mark {
        transform: none;
    }

    .weather-shell.theme-caelowx.sidebar-collapsed .nav-item {
        height: var(--cx-rail-button-size);
        min-height: var(--cx-rail-button-size);
        width: var(--cx-rail-button-size);
    }

    .weather-shell.theme-caelowx.sidebar-collapsed .nav-icon,
    .weather-shell.theme-caelowx.sidebar-collapsed .nav-icon svg {
        height: 22px;
        width: 22px;
    }

    body.banner-compact .weather-shell.theme-caelowx.dashboard-hero-header.sidebar-collapsed .nav-list {
        margin-top: 10px;
    }
}

@media (min-width: 641px) and (max-width: 900px) {
    .weather-shell.theme-caelowx .sidebar {
        padding-left: var(--cx-rail-gutter);
        padding-right: var(--cx-rail-gutter);
    }
}

/* The native CaeloWX Mapbox style matches this loading surface, so the map does
   not flash white while its first tiles arrive. */
.weather-shell.theme-light.theme-caelowx .radar-map {
    background: var(--cx-card);
}

/* Map chrome stays out of the weather until the reader engages the map. Keyboard
   focus reveals it too; touch devices retain always-visible controls below. */
@media (hover: hover) and (pointer: fine) {
    .weather-shell.theme-caelowx .wpc-media .radar-controls,
    .weather-shell.theme-caelowx .wpc-media .radar-time,
    .weather-shell.theme-caelowx .wpc-media .radar-stale,
    .weather-shell.theme-caelowx .wpc-media .leaflet-control-container,
    .weather-shell.theme-caelowx .radar-map-shell .radar-controls,
    .weather-shell.theme-caelowx .radar-map-shell .radar-time,
    .weather-shell.theme-caelowx .radar-map-shell .radar-stale,
    .weather-shell.theme-caelowx .radar-map-shell .leaflet-control-container {
        opacity: 0;
        transition: opacity 160ms ease;
    }

    .weather-shell.theme-caelowx .wpc-media:hover .radar-controls,
    .weather-shell.theme-caelowx .wpc-media:hover .radar-time,
    .weather-shell.theme-caelowx .wpc-media:hover .radar-stale,
    .weather-shell.theme-caelowx .wpc-media:hover .leaflet-control-container,
    .weather-shell.theme-caelowx .wpc-media:focus-within .radar-controls,
    .weather-shell.theme-caelowx .wpc-media:focus-within .radar-time,
    .weather-shell.theme-caelowx .wpc-media:focus-within .radar-stale,
    .weather-shell.theme-caelowx .wpc-media:focus-within .leaflet-control-container,
    .weather-shell.theme-caelowx .radar-map-shell:hover .radar-controls,
    .weather-shell.theme-caelowx .radar-map-shell:hover .radar-time,
    .weather-shell.theme-caelowx .radar-map-shell:hover .radar-stale,
    .weather-shell.theme-caelowx .radar-map-shell:hover .leaflet-control-container,
    .weather-shell.theme-caelowx .radar-map-shell:focus-within .radar-controls,
    .weather-shell.theme-caelowx .radar-map-shell:focus-within .radar-time,
    .weather-shell.theme-caelowx .radar-map-shell:focus-within .radar-stale,
    .weather-shell.theme-caelowx .radar-map-shell:focus-within .leaflet-control-container {
        opacity: 1;
    }
}

/* ============================================================================
   TOUCH: EVERY RADAR CONTROL FOLLOWS ONE STATE
   ============================================================================

   Five controls - back, play/pause, forward, zoom in, zoom out - live in TWO DOM groups
   that nothing used to govern together on a touch screen:

     .radar-controls        the playback bar, authored in RadarCard.razor
     .leaflet-control-zoom  the zoom pair, built by Leaflet at runtime

   WHAT WAS ACTUALLY WRONG, measured on the phone render before this rule existed:

     - the playback bar sat at `opacity: .32` - a deliberate "hint", but still visible;
     - the zoom pair sat at `opacity: 1`, 32 x 62, governed by nothing;
     - the desktop reveal above covers BOTH groups correctly, and is unreachable here: its
       `@media (hover: hover) and (pointer: fine)` excludes touch on purpose, and its own
       comment said "touch devices retain always-visible controls below."
     - the touch rule that did exist was in `RadarCard.razor.css`, which is
       component-scoped, and Leaflet's runtime elements carry no Blazor scope attribute
       (`.wpc-media` had `b-1eh80jfbdd`; `.leaflet-control-zoom` had none) - so a scoped
       selector could not have reached the zoom buttons even if it had named them.

   SO THE RULE IS UNSCOPED AND IT NAMES BOTH GROUPS IN ONE SELECTOR LIST, driven by the one
   state `card-radar.js` already toggles: `.radar-controls-open` on the media root, set from
   Leaflet's own map click - which excludes control taps and suppresses pans, so using a
   control does not dismiss it and panning does not summon it.

   `visibility` AND `pointer-events` ALONGSIDE `opacity`, not opacity alone: an
   opacity-0 control still answers taps, so the first tap would have hit an invisible zoom
   button instead of the map that reveals it.

   THE STATE CLASS IS THE ONLY THING THAT REVEALS THESE CONTROLS. No `:focus-within`, no
   `:has(:focus-visible)`, no `:hover` - and both were tried and both broke the dismiss:

     - `.wpc-media:focus-within` matched from the FIRST tap onward, because Leaflet gives
       `.leaflet-container` a `tabindex` so arrow keys can pan, so tapping the map focuses
       it. Measured: after the dismiss tap the class was correctly gone (`open: false`)
       while both groups still computed `opacity: 1`.
     - `:has(:focus-visible)` fixed that in Chrome's emulator but is not dependable as a
       dismiss guard on real hardware: whether a tap on a button sets `:focus-visible` is a
       browser heuristic, and a tapped play button that happens to match pins every control
       open with nothing in the DOM to show why.

   A pseudo-class cannot be told "the reader is done with you". A class can, so
   `card-radar.js` owns the whole state - toggle on map tap, clear on a tap outside the
   card - and CSS only draws it. Keyboard reveal stays with the desktop block above, which
   is where a device with a keyboard reports its pointer anyway.

   NO THEME SELECTOR ANYWHERE IN HERE. Light, dark and nightshade get the same behaviour
   because nothing distinguishes them - that is the requirement, and it is also why this is
   not written under `.weather-shell.theme-caelowx` like the desktop block above.

   SCOPED TO `.wpc-media`, WHICH IS THE CARD-SYSTEM RADAR ONLY. The legacy dashboard's radar
   is `.radar-map-shell` and is driven by `dashboard.js`, which never sets
   `.radar-controls-open` - hiding its controls here would have removed them with no way
   back. Deliberately excluded.

   THE TIMESTAMP AND STALE BADGES ARE NOT CONTROLS and are left alone: they are information
   about what the map is showing, and the five controls are what was asked for. */
@media (hover: none) and (pointer: coarse) {
    .wpc-media .radar-controls,
    .wpc-media .leaflet-control-zoom {
        opacity: 0;
        pointer-events: none;
        transition: opacity .18s ease;
        visibility: hidden;
    }

    .wpc-media.radar-controls-open .radar-controls,
    .wpc-media.radar-controls-open .leaflet-control-zoom {
        opacity: 1;
        pointer-events: auto;
        visibility: visible;
    }
}

body.banner-scenic .weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar {
    --cx-dashboard-banner: url("branding/caelowx-banner-kansas-moonlit-header-standard-v4.webp");
    background-image:
        linear-gradient(
            90deg,
            color-mix(in srgb, var(--cx-background) 76%, transparent) 0%,
            color-mix(in srgb, var(--cx-background) 38%, transparent) 26%,
            color-mix(in srgb, var(--cx-background) 3%, transparent) 52%,
            color-mix(in srgb, var(--cx-background) 12%, transparent) 100%
        ),
        linear-gradient(
            0deg,
            color-mix(in srgb, var(--cx-background) 24%, transparent) 0%,
            transparent 64%
        ),
        var(--cx-dashboard-banner);
    background-position: center;
    background-size: cover;
    border: 0;
    border-bottom: 1px solid color-mix(in srgb, var(--cx-border) 66%, var(--cx-brand-accent));
    border-radius: 0;
    box-shadow:
        0 10px 28px color-mix(in srgb, var(--cx-background) 36%, transparent),
        inset 0 -1px 0 color-mix(in srgb, var(--cx-text-primary) 7%, transparent);
    box-sizing: border-box;
    isolation: isolate;
    margin: -14px -18px 10px;
    min-height: 108px;
    overflow: visible;
    padding: 16px 20px;
    position: relative;
    z-index: var(--cx-z-menu);
}

/* The account menu belongs to app chrome, above every dashboard card and map.
   Its skin follows the selected CaeloWX palette while its existing anatomy and
   interactions remain unchanged. */
.weather-shell.theme-light.theme-caelowx .user-menu-panel {
    background: rgba(228, 239, 245, .98);
    border-color: color-mix(in srgb, var(--cx-border) 74%, var(--cx-primary));
    box-shadow: 0 20px 52px rgba(6, 23, 47, .25), inset 0 1px 0 rgba(255, 255, 255, .72);
    color: var(--cx-text-primary);
}

.weather-shell.theme-dark.theme-caelowx .user-menu-panel {
    background: color-mix(in srgb, var(--cx-surface) 96%, transparent);
    border-color: color-mix(in srgb, var(--cx-border) 74%, var(--cx-brand-accent));
    box-shadow: 0 20px 52px color-mix(in srgb, var(--cx-background) 72%, transparent), inset 0 1px 0 color-mix(in srgb, var(--cx-text-primary) 7%, transparent);
    color: var(--cx-text-primary);
}

.weather-shell.theme-light.theme-caelowx .user-menu-avatar {
    background: color-mix(in srgb, var(--cx-primary) 10%, transparent);
    color: var(--cx-primary);
}

.weather-shell.theme-dark.theme-caelowx .user-menu-avatar {
    background: color-mix(in srgb, var(--cx-brand-accent) 12%, transparent);
    color: var(--cx-brand-accent);
}

.weather-shell.theme-light.theme-caelowx .user-menu-links,
.weather-shell.theme-dark.theme-caelowx .user-menu-links {
    border-color: var(--cx-border);
}

.weather-shell.theme-light.theme-caelowx .user-menu-links a:hover,
.weather-shell.theme-light.theme-caelowx .user-preference-link:hover,
.weather-shell.theme-light.theme-caelowx .user-preference-row:hover,
.weather-shell.theme-dark.theme-caelowx .user-menu-links a:hover,
.weather-shell.theme-dark.theme-caelowx .user-preference-link:hover,
.weather-shell.theme-dark.theme-caelowx .user-preference-row:hover {
    background: color-mix(in srgb, var(--cx-primary) 12%, transparent);
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar .station-heading,
.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar .header-actions {
    position: relative;
    z-index: 1;
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar .station-heading {
    text-shadow: 0 2px 16px var(--cx-background);
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar .station-heading .eyebrow {
    color: var(--cx-brand-accent);
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar .station-heading h1 {
    color: var(--cx-text-primary);
}

.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar .station-heading p {
    color: var(--cx-text-secondary);
}

/* The image can cross behind the controls, so give the two existing top-bar
   buttons a restrained glass backing without changing their shape or behavior. */
.weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar .header-button {
    backdrop-filter: blur(8px);
    background: color-mix(in srgb, var(--cx-surface) 76%, transparent);
    border-color: color-mix(in srgb, var(--cx-border) 82%, var(--cx-brand-accent));
    box-shadow: 0 4px 16px color-mix(in srgb, var(--cx-background) 42%, transparent);
}

@media (min-width: 641px) and (max-width: 900px) {
    body.banner-scenic .weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar {
        --cx-dashboard-banner: url("branding/caelowx-banner-kansas-daylight-header-tablet-v1.webp");
        min-height: 100px;
        padding: 16px 18px;
    }

    body.banner-scenic .weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar {
        --cx-dashboard-banner: url("branding/caelowx-banner-kansas-moonlit-header-tablet-v4.webp");
        background-position: center;
        margin: -14px -18px 10px;
        min-height: 100px;
        padding: 16px 18px;
    }
}

@media (min-width: 1700px) {
    body.banner-scenic .weather-shell.theme-light.theme-caelowx.dashboard-hero-header .topbar {
        --cx-dashboard-banner: url("branding/caelowx-banner-kansas-daylight-header-wide-v1.webp");
    }

    body.banner-scenic .weather-shell.theme-dark.theme-caelowx.dashboard-hero-header .topbar {
        --cx-dashboard-banner: url("branding/caelowx-banner-kansas-moonlit-header-wide-v4.webp");
    }
}

/* Account retains layout and control anatomy in its component stylesheet. The
   CaeloWX skin belongs here with the rest of the Dark theme, so pages consume a
   shared product palette instead of owning brand colors themselves. The shell
   qualifier also deliberately out-ranks Account's legacy compatibility defaults. */
.weather-shell.theme-dark.theme-caelowx.account-shell .account-page {
    --account-page-bg: var(--bg);
    --account-surface: var(--surface-2);
    --account-surface-2: var(--surface-3);
    --account-border: var(--border);
    --account-text: var(--text);
    --account-heading: var(--text);
    --account-muted: var(--text-secondary);
    --account-input-bg: var(--surface-3);
    --account-input-border: color-mix(in srgb, var(--border) 82%, var(--cx-brand-accent));
    --account-accent: var(--accent);
    --account-accent-hover: color-mix(in srgb, var(--accent) 82%, var(--cx-brand-accent));
    --account-accent-soft: color-mix(in srgb, var(--accent) 16%, transparent);
    --account-info: var(--cx-measurement-pressure);
    --account-danger: var(--danger);
    --account-shadow: 0 8px 22px color-mix(in srgb, var(--bg) 62%, transparent);
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-content {
    gap: 14px;
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section {
    box-shadow: var(--account-shadow);
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section:hover {
    box-shadow: 0 10px 26px color-mix(in srgb, var(--bg) 72%, transparent);
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section > .account-section-heading {
    padding: 15px 18px;
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section[open] > .account-section-heading {
    border-bottom-color: var(--account-border);
    margin-bottom: 14px;
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section > :not(.account-section-heading) {
    margin-left: 18px;
    margin-right: 18px;
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section > :last-child {
    margin-bottom: 18px;
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-form-grid input,
.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-form-grid select {
    background: var(--account-input-bg);
    border-color: var(--account-input-border);
}

/*  AND THE SAME RULE HAS TO ANSWER FOR FOCUS, or it silently cancels it.

    `.account-shell .account-form-grid select:focus` asks for an accent border, but the rule
    above is six classes deep and wins at every state - including focus - so the border never
    changed and only the ring showed. Measured before this rule existed: a focused select kept
    its resting border colour.

    Border only. The ring is already the design system's `--app-field-focus-ring` spread in the
    page's own accent, and `.app-select` does not change background on focus either. */
.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-form-grid input:focus,
.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-form-grid select:focus {
    border-color: var(--account-accent);
}

.weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section .account-theme-option.active {
    background: var(--account-accent-soft);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--account-accent) 42%, transparent);
    color: var(--account-text);
}

@media (max-width: 640px) {
    .weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section > .account-section-heading {
        padding: 14px 16px;
    }

    .weather-shell.theme-dark.theme-caelowx.account-shell .account-page .account-section > :not(.account-section-heading) {
        margin-left: 16px;
        margin-right: 16px;
    }
}
