﻿:root {
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body { background: var(--initial-page-background, #161616); }

/* Routes.razor uses FocusOnNavigate to move focus to the page heading on
   navigation so screen readers announce the new page. The announcement comes
   from focus moving, not from anything drawn, and the heading is not
   interactive — so no ring. */
h1:focus,
h1:focus-visible {
    outline: none;
}
button { font: inherit; }

.weather-shell {
    --bg: #161616;
    --surface: #202020;
    --surface-2: #282828;
    --surface-3: #303030;
    --border: #3b3b3b;
    --text: #f3f3f3;
    --muted: #9d9d9d;
    --accent: #49b6e8;
    --accent-soft: rgba(73,182,232,.14);

    /* What reads on top of --accent. A literal white was assumed at every call site; it is
       only correct while the accent is dark enough to carry it, which is not true in every
       theme. See caelowx-tokens.css, where Nightshade's accent is a light grey. */
    --on-accent: #ffffff;
    --success: #4ac978;
    --warning: #efc94d;
    --danger: #ef6e7f;
    /* Indicator variants exist because the text tokens above are tuned for
       contrast on the page background, which makes them muddy as small filled
       shapes. Use these for dots, bars and fills; use the text tokens for
       text. Light-theme values come from the semantic alert palette. */
    --success-indicator: #4ac978;
    --warning-indicator: #efc94d;
    --danger-indicator: #ef6e7f;
    --rain: #62a8ef;
    background: var(--bg);
    color: var(--text);
    display: grid;
    grid-template-columns: 198px minmax(0, 1fr);
    min-height: 100vh;
}

.weather-shell.theme-light {
    --bg: #ebebeb;
    --surface: #fff;
    --surface-2: #f4f4f4;
    --surface-3: #e7e7e7;
    --border: #d1d1d1;
    --text: #202020;
    --muted: #626262;
    --accent: #1677a5;
    --accent-soft: rgba(22,119,165,.10);
    --on-accent: #ffffff;
    --success: #237d43;
    --warning: #9a6700;
    --danger: #b52343;
    --success-indicator: #2f9e56;
    --warning-indicator: #d28b3e;
    --danger-indicator: #d4635a;
    --rain: #2d68c4;
}

.sidebar {
    background: var(--surface);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    padding: 16px 12px;
    position: sticky;
    top: 0;
}

.brand { align-items: center; display: flex; gap: 10px; padding: 2px 5px 17px; }
.brand-mark {
    align-items: center;
    background: var(--text);
    border-radius: 8px;
    color: var(--bg);
    display: flex;
    font-weight: 900;
    height: 31px;
    justify-content: center;
    width: 31px;
}
.brand-name { font-size: .8rem; font-weight: 800; }
.brand-subtitle { color: var(--muted); font-size: .56rem; margin-top: 2px; }

.nav-list { display: grid; gap: 4px; }
.nav-item {
    align-items: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    color: var(--muted);
    display: flex;
    gap: 10px;
    padding: 9px 10px;
    text-decoration: none;
    width: 100%;
}
.nav-item.active {
    background: var(--accent-soft);
    border-color: color-mix(in srgb,var(--accent) 25%,transparent);
    color: var(--accent);
}
.nav-item:hover,
.nav-item:focus-visible {
    background: var(--surface-2);
    color: var(--text);
    outline: none;
}

.sidebar-footer { margin-top: auto; }
.station-mini {
    align-items: center;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: flex;
    gap: 9px;
    padding: 9px;
}
.station-mini-name { font-size: .67rem; font-weight: 800; }
.station-mini-status { color: var(--muted); font-size: .54rem; margin-top: 2px; }

.status-dot {
    background: var(--muted);
    border-radius: 50%;
    display: inline-block;
    height: 8px;
    width: 8px;
}
.status-dot.online {
    background: var(--success-indicator);
    box-shadow: 0 0 0 3px color-mix(in srgb,var(--success-indicator) 16%,transparent);
}
.status-dot.warning {
    background: var(--warning-indicator);
    box-shadow: 0 0 0 3px color-mix(in srgb,var(--warning-indicator) 16%,transparent);
}
.status-dot.offline {
    background: var(--danger-indicator);
    box-shadow: 0 0 0 3px color-mix(in srgb,var(--danger-indicator) 16%,transparent);
}

.dashboard-main { min-width: 0; padding: 14px 18px 28px; }
.topbar { align-items: center; display: flex; justify-content: space-between; margin-bottom: 10px; }
.eyebrow, .card-kicker { color: var(--muted); font-size: .56rem; font-weight: 800; letter-spacing: .11em; }
.station-heading h1 { font-size: 1.55rem; letter-spacing: -.04em; margin: 3px 0 0; }
.station-heading p { color: var(--muted); font-size: .64rem; margin: 2px 0 0; }

.header-actions { align-items: center; display: flex; gap: 8px; }
.sun-time { align-items: center; display: flex; gap: 6px; padding: 0 5px; }
.sun-time div { display: flex; flex-direction: column; }
.sun-time small { color: var(--muted); font-size: .52rem; }
.sun-time strong { font-size: .65rem; margin-top: 1px; }

.mini-weather-icon { display: inline-block; height: 24px; position: relative; width: 24px; }
.sunrise-icon::before,
.sunset-icon::before {
    border: 2px solid var(--warning);
    border-bottom: 0;
    border-radius: 16px 16px 0 0;
    content: "";
    height: 8px;
    left: 5px;
    position: absolute;
    top: 8px;
    width: 14px;
}
.sunrise-icon::after,
.sunset-icon::after {
    background: var(--warning);
    box-shadow: -7px -3px 0 -1px var(--warning), 7px -3px 0 -1px var(--warning);
    content: "";
    height: 2px;
    left: 3px;
    position: absolute;
    top: 17px;
    width: 18px;
}
.sunset-icon { transform: rotate(180deg); }

.header-button {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--text);
    min-height: 33px;
    padding: 0 9px;
}
.header-button:hover,
.header-button:focus-visible {
    border-color: var(--accent);
    outline: none;
}
.edit-button.active {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

.edit-toolbar {
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  display: flex;
  justify-content: space-between;
  margin-bottom: 12px;
  min-height: 72px;
  padding: 14px 16px;
}

.edit-toolbar > div {
  display: flex;
  flex-direction: column;
}

.edit-toolbar strong {
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.2;
}

.edit-toolbar span {
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.35;
  margin-top: 5px;
}
.edit-toolbar button,
.hidden-cards button {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 7px;
    color: var(--text);
    font-size: .56rem;
    padding: 6px 8px;
}

.hidden-cards {
    align-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 9px;
    padding: 7px 9px;
}
.hidden-cards > span { color: var(--muted); font-size: .56rem; font-weight: 700; }

.dashboard-grid {
    display: grid;
    column-gap: 9px;
    grid-auto-flow: dense;
    grid-auto-rows: 1px;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    row-gap: 0;
}

.weather-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 9px;
    margin-bottom: 9px;
    min-width: 0;
    overflow: hidden;
    padding: 11px;
    position: relative;
}

.card-size-small {
    grid-column: span 3;
    grid-row: span 129;
}

.card-size-medium {
    grid-column: span 3;
    grid-row: span 249;
}

.card-size-large {
    grid-column: span 6;
    grid-row: span 489;
}

.card-temperatureHistory.card-size-large,
.card-rainfallHistory.card-size-large {
    grid-column: span 6;
    grid-row: span 489;
}

.card-hourlyForecast.card-size-large,
.card-dailyForecast.card-size-large {
    grid-column: span 6;
    grid-row: span 489;
}

.card-status.card-size-large {
    grid-column: span 6;
    grid-row: span 489;
}

.dashboard-grid.editing .weather-card {
    outline: 1px dashed color-mix(in srgb,var(--accent) 52%,transparent);
    outline-offset: -3px;
}

.sortable-ghost {
    background: var(--accent-soft) !important;
    border: 1px dashed var(--accent) !important;
    opacity: .85;
}

.sortable-chosen {
    border-color: var(--accent);
}

.sortable-drag {
    box-shadow: 0 14px 36px rgba(0,0,0,.28);
    opacity: .96;
}

.card-edit-controls {
    align-items: center;
    background: color-mix(in srgb,var(--surface) 94%,transparent);
    border: 1px solid var(--border);
    border-radius: 7px;
    display: flex;
    gap: 4px;
    padding: 3px;
    position: absolute;
    right: 6px;
    top: 6px;
    z-index: 30;
}

.drag-handle,
.hide-card,
.size-picker button {
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: 5px;
    color: var(--muted);
    display: inline-flex;
    font-size: .56rem;
    height: 22px;
    justify-content: center;
    min-width: 22px;
    padding: 0 5px;
}

.drag-handle {
    cursor: grab;
    display: grid;
    gap: 2px;
    grid-template-columns: repeat(2,3px);
    padding: 5px 7px;
    touch-action: none;
}
.drag-handle span {
    background: currentColor;
    border-radius: 50%;
    height: 3px;
    width: 3px;
}
.drag-handle:active { cursor: grabbing; }

.size-picker { display: flex; gap: 2px; }
.size-picker button.selected { background: var(--accent); color: white; }
.hide-card:hover { color: var(--danger); }
.dashboard-grid.editing .card-title-row { padding-right: 112px; }

.card-title-row { align-items: flex-start; display: flex; gap: 8px; justify-content: space-between; }
.card-title-row h2 { font-size: .74rem; margin: 2px 0 0; }
.pill {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--muted);
    font-size: .52rem;
    font-weight: 800;
    padding: 4px 7px;
}
.success-pill { color: var(--success); }

.current-conditions-content {
    align-items: center;
    bottom: 11px;
    display: grid;
    gap: 12px;
    grid-template-columns: 1fr minmax(110px,32%);
    left: 11px;
    position: absolute;
    right: 11px;
    top: 42px;
}

.condition-main {
    align-items: center;
    display: flex;
    flex-direction: column;
    text-align: center;
}

.current-condition-icon {
    align-items: center;
    display: flex;
    justify-content: center;
}

.current-condition-icon .weather-svg {
    height: 138px;
    width: 138px;
}

.value-row { align-items: flex-start; display: flex; gap: 5px; justify-content: center; }
.sensor-value { font-weight: 760; letter-spacing: -.075em; line-height: .9; }
.sensor-value.xl { font-size: clamp(3rem,4.8vw,4.4rem); }
.sensor-value.lg { font-size: clamp(2.1rem,3.2vw,3.1rem); }
.sensor-value.md { font-size: 1.9rem; }
.sensor-unit { color: var(--muted); font-size: .7rem; font-weight: 700; padding-top: 5px; }
.condition-name { font-size: .8rem; font-weight: 700; margin-top: 9px; }

/* The condition text is the one value on the current conditions card that
   has no sensor equivalent, so it carries a source tag and everything
   unlabelled on the card can be read as measured by the station. */
.condition-name-row {
    align-items: center;
    display: flex;
    gap: 6px;
    margin-top: 9px;
}

.condition-name-row .condition-name { margin-top: 0; }

.source-tag {
    border: 1px solid var(--border);
    border-radius: 3px;
    color: var(--muted);
    flex: none;
    font-size: .48rem;
    font-weight: 700;
    justify-self: start;
    letter-spacing: .04em;
    line-height: 1.5;
    padding: 0 4px;
    text-transform: uppercase;
    white-space: nowrap;
    /* Grid and flex parents stretch their children by default, which turns
       the tag into a full-width box. Size it to its text wherever it lands. */
    width: fit-content;
}
.feels-like { color: var(--muted); font-size: .6rem; margin-top: 4px; }
.feels-like strong { color: var(--accent); }

.condition-details {
    bottom: 5px;
    display: grid;
    gap: 14px;
    grid-column: 1 / -1;
    grid-template-columns: repeat(3,minmax(0,1fr));
    left: 0;
    position: absolute;
    right: 0;
}
.condition-details span { border-left: 1px solid var(--border); display: flex; flex-direction: column; }
.condition-details span:first-child { border-left: 0; }
.condition-details small { color: var(--muted); font-size: .5rem; white-space: nowrap; }
.condition-details strong { font-size: .62rem; margin-top: 3px; }

.weather-svg {
    display: block;
    height: 42px;
    overflow: visible;
    width: 42px;
}
.weather-svg .cloud {
    fill: color-mix(in srgb,var(--surface-3) 82%,#fff);
    stroke: color-mix(in srgb,var(--muted) 75%,#fff);
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2.1;
}
.weather-svg .sun-core {
    fill: color-mix(in srgb,var(--warning) 70%,transparent);
    stroke: var(--warning);
    stroke-width: 2.1;
}
.weather-svg .sun-ray {
    stroke: var(--warning);
    stroke-linecap: round;
    stroke-width: 2.2;
}
.weather-svg .rain-line {
    stroke: var(--rain);
    stroke-linecap: round;
    stroke-width: 2.6;
}
.weather-svg .snow-line,
.weather-svg .fog-line {
    stroke: var(--accent);
    stroke-linecap: round;
    stroke-width: 2;
}
.weather-svg .bolt {
    fill: var(--warning);
    stroke: var(--warning);
    stroke-linejoin: round;
}
.weather-svg .moon-fill {
    fill: color-mix(in srgb,var(--warning) 35%,var(--surface-2));
    stroke: var(--warning);
    stroke-width: 2;
}

.alerts-content {
    align-items: center;
    display: grid;
    gap: 10px;
    grid-template-columns: auto 1fr;
    inset: 42px 11px 11px;
    position: absolute;
}

.alert-symbol {
    align-items: center;
    background: color-mix(in srgb,var(--success) 14%,transparent);
    border: 1px solid color-mix(in srgb,var(--success) 35%,var(--border));
    border-radius: 50%;
    color: var(--success);
    display: flex;
    font-weight: 800;
    height: 34px;
    justify-content: center;
    width: 34px;
}

.alerts-content > div:last-child { display: flex; flex-direction: column; }
.alerts-content strong { font-size: .68rem; }
.alerts-content span { color: var(--muted); font-size: .54rem; margin-top: 3px; }

.card-alerts.card-size-small .alerts-content span,
.card-alerts.card-size-small .alert-symbol {
    display: none;
}
.card-alerts.card-size-small .alerts-content {
    display: flex;
    justify-content: center;
    text-align: center;
}
.card-alerts.card-size-small .card-kicker { display: none; }

.radar-link {
    bottom: 9px;
    left: 9px;
    position: absolute;
    right: 9px;
    top: 40px;
}
.radar-image {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 7px;
    height: 100%;
    object-fit: cover;
    width: 100%;
}

.metric-icon {
    display: inline-block;
    height: 20px;
    position: relative;
    width: 20px;
}

.humidity-icon::before {
    border: 2px solid var(--accent);
    border-radius: 50% 50% 50% 0;
    content: "";
    height: 10px;
    left: 4px;
    position: absolute;
    top: 2px;
    transform: rotate(-45deg);
    width: 10px;
}
.dew-icon::before,
.dew-icon::after,
.pressure-icon::before {
    background: var(--accent);
    border-radius: 99px;
    content: "";
    height: 2px;
    left: 2px;
    position: absolute;
    width: 16px;
}
.dew-icon::before { top: 6px; }
.dew-icon::after { top: 12px; width: 11px; }
.pressure-icon::before { top: 9px; }
.pressure-icon::after {
    border-right: 2px solid var(--accent);
    border-top: 2px solid var(--accent);
    content: "";
    height: 5px;
    position: absolute;
    right: 2px;
    top: 6px;
    transform: rotate(45deg);
    width: 5px;
}
.uv-icon::before,
.solar-icon::before {
    border: 2px solid var(--warning);
    border-radius: 50%;
    content: "";
    height: 8px;
    left: 4px;
    position: absolute;
    top: 4px;
    width: 8px;
}
.uv-icon::after,
.solar-icon::after {
    background: var(--warning);
    box-shadow: 0 -8px 0 -1px var(--warning), 0 8px 0 -1px var(--warning), -8px 0 0 -1px var(--warning), 8px 0 0 -1px var(--warning);
    content: "";
    height: 2px;
    left: 9px;
    position: absolute;
    top: 9px;
    width: 2px;
}
.lightning-icon::before {
    background: var(--warning);
    clip-path: polygon(45% 0,100% 0,65% 42%,93% 42%,25% 100%,42% 55%,15% 55%);
    content: "";
    height: 18px;
    left: 3px;
    position: absolute;
    top: 1px;
    width: 14px;
}
.rain-icon::before {
    border: 2px solid var(--rain);
    border-radius: 50% 50% 50% 0;
    content: "";
    height: 10px;
    left: 4px;
    position: absolute;
    top: 2px;
    transform: rotate(-45deg);
    width: 10px;
}
.moon-icon::before {
    background: var(--warning);
    border-radius: 50%;
    content: "";
    height: 16px;
    left: 2px;
    position: absolute;
    top: 2px;
    width: 16px;
}
.moon-icon::after {
    background: var(--surface);
    border-radius: 50%;
    content: "";
    height: 14px;
    left: 7px;
    position: absolute;
    top: 0;
    width: 14px;
}

.sensor-center {
    align-items: center;
    bottom: 8px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    left: 8px;
    position: absolute;
    right: 8px;
    text-align: center;
    top: 34px;
}
.compact-center { top: 32px; }
.sensor-note { color: var(--muted); font-size: .56rem; margin-top: 7px; }
.sensor-state { color: var(--warning); font-size: .56rem; font-weight: 800; margin-top: 7px; }

/* BEGIN Dynamic moon body graphic.
   Shared by the dashboard and the card lab. Previously this lived only in
   CardLab.razor.css, where Blazor CSS isolation scoped it to that one page. */
.moon-phase-graphic {
    background: var(--surface);
    border: 1px solid rgba(255, 211, 105, 0.35);
    border-radius: 50%;
    box-shadow: 0 0 16px rgba(255, 211, 105, 0.14);
    height: 34px;
    overflow: hidden;
    position: relative;
    width: 34px;
}

.moon-phase-graphic::before,
.moon-phase-graphic::after {
    border-radius: 50%;
    content: "";
    height: 100%;
    inset: 0;
    position: absolute;
    width: 100%;
}

.moon-phase-graphic::before {
    background: var(--warning);
}

.moon-phase-graphic::after {
    background: var(--surface);
}

/* New moon: no illuminated disc at all. */
.moon-phase-new-moon::before {
    background: var(--surface);
}

.moon-phase-new-moon::after {
    display: none;
}

/* Waxing crescent: illuminated on the right. */
.moon-phase-waxing-crescent::after {
    display: block;
    transform: translateX(-24%) scaleX(1.08);
}

/* First quarter: right half illuminated. `width: auto` is required so that
   `right` is honoured; with `left`, `right` and `width` all set the box is
   over-constrained and `right` is discarded. */
.moon-phase-first-quarter::after {
    border-radius: 0;
    display: block;
    right: 50%;
    width: auto;
}

/* Waxing gibbous: mostly illuminated, shadow on the left. */
.moon-phase-waxing-gibbous::after {
    display: block;
    transform: translateX(-72%) scaleX(0.62);
}

/* Full moon: nothing occludes the disc. */
.moon-phase-full-moon::after {
    display: none;
}

/* Waning gibbous: mostly illuminated, shadow on the right. */
.moon-phase-waning-gibbous::after {
    display: block;
    transform: translateX(72%) scaleX(0.62);
}

/* Last quarter: left half illuminated. See the note on first quarter. */
.moon-phase-last-quarter::after {
    border-radius: 0;
    display: block;
    left: 50%;
    width: auto;
}

/* Waning crescent: illuminated on the left. */
.moon-phase-waning-crescent::after {
    display: block;
    transform: translateX(24%) scaleX(1.08);
}

.moon-phase-unavailable::before {
    background: var(--muted);
    opacity: 0.35;
}

.moon-phase-unavailable::after {
    display: none;
}
/* END Dynamic moon body graphic */

.wind-compact {
    align-items: center;
    display: grid;
    gap: 12px;
    grid-template-columns: 1fr;
    inset: 42px 12px 12px;
    position: absolute;
}

.wind-primary {
    align-items: center;
    display: flex;
    gap: 10px;
    justify-content: center;
}

.wind-direction-arrow {
    border-right: 3px solid var(--accent);
    border-top: 3px solid var(--accent);
    height: 18px;
    position: relative;
    transform: rotate(113deg);
    width: 18px;
}
.wind-direction-arrow::after {
    background: var(--accent);
    content: "";
    height: 3px;
    position: absolute;
    right: -3px;
    top: -2px;
    transform: rotate(-45deg);
    transform-origin: right center;
    width: 30px;
}

.wind-details {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(2,minmax(0,1fr));
}
.wind-details span {
    align-items: center;
    border-left: 1px solid var(--border);
    display: flex;
    flex-direction: column;
}
.wind-details span:first-child { border-left: 0; }
.wind-details small { color: var(--muted); font-size: .5rem; }
.wind-details strong { font-size: .62rem; margin-top: 3px; }

.card-wind.card-size-small .wind-details { display: none; }
.card-wind.card-size-small .wind-direction-arrow {
    height: 13px;
    width: 13px;
}

/*
 * Charts get their own text floor. The card minimum - --card-label-min, 14px -
 * is right for a card and unworkable on a month chart with 31 bars. Twelve
 * pixels is legible and fits. Nothing drawn on a chart may go below it, and the
 * five-, six- and seven-pixel text this replaced was not readable at all.
 */
:root {
    --chart-text-min: .75rem;
}

.range-tabs {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: flex;
    padding: 3px;
}

/* Sized to the forecast card's Hourly/Daily pills, which were asked to be the
   reference. Both were raised to the chart floor so they match each other and
   clear it. */
.range-tabs button {
    background: transparent;
    border: 0;
    border-radius: 6px;
    color: var(--muted);
    font-size: var(--chart-text-min);
    min-height: 28px;
    padding: 0 11px;
}
.range-tabs button.active { background: var(--surface); color: var(--text); }

.chart-box {
    bottom: 9px;
    color: var(--accent);
    height: calc(100% - 48px);
    left: 9px;
    position: absolute;
    right: 9px;
}
.chart-box svg { height: 100%; width: 100%; }
.grid-lines line { stroke: var(--border); stroke-width: 1; }
.history-area { fill: color-mix(in srgb,var(--accent) 12%,transparent); }
.history-line { fill: none; stroke: currentColor; stroke-width: 2.5; }

.rain-bars {
    align-items: end;
    bottom: 39px;
    display: flex;
    gap: 5px;
    height: calc(100% - 82px);
    left: 11px;
    position: absolute;
    right: 11px;
}
.rain-bars span {
    background: var(--rain);
    border-radius: 3px 3px 0 0;
    flex: 1;
    min-height: 3px;
    opacity: .82;
}
.rain-totals { bottom: 9px; display: flex; gap: 14px; left: 11px; position: absolute; }
.rain-totals span { color: var(--muted); font-size: var(--chart-text-min); }
.rain-totals strong { color: var(--text); margin-right: 3px; }

.hourly-strip,
.daily-strip {
    display: grid;
    gap: 6px;
    grid-template-columns: repeat(8,minmax(72px,1fr));
    margin-top: 14px;
    overflow-x: auto;
}
.daily-strip { grid-template-columns: repeat(7,minmax(86px,1fr)); }

.hour-item,
.day-item {
    align-items: center;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 7px;
    display: flex;
    flex-direction: column;
    padding: 7px 5px;
    text-align: center;
}
.hour-item span,
.hour-item small,
.day-item span,
.day-item small {
    color: var(--muted);
    font-size: .5rem;
}
.hour-item .weather-svg,
.day-item .weather-svg {
    height: 34px;
    margin: 4px 0;
    width: 34px;
}
.hour-item strong,
.day-item strong { font-size: .72rem; margin-bottom: 4px; }
.forecast-loading { color: var(--muted); font-size: .6rem; padding: 16px; }

.status-left { align-items: center; display: flex; gap: 9px; }
.status-left div { display: flex; flex-direction: column; }
.status-left strong { font-size: .62rem; }
.status-left span { color: var(--muted); font-size: .52rem; margin-top: 2px; }
.status-right { display: flex; gap: 13px; }
.status-right span { color: var(--muted); font-size: .52rem; }
.status-right strong { color: var(--text); margin-left: 3px; }

@media (max-width: 1180px) {
    .weather-shell { grid-template-columns: 176px minmax(0,1fr); }
    .card-size-small {
    grid-column: span 3;
    grid-row: span 129;
}
    .card-size-medium {
    grid-column: span 3;
    grid-row: span 249;
}
    .card-size-large {
    grid-column: span 6;
    grid-row: span 489;
}
}

/* THE HORIZONTAL TOP NAVIGATION IS RETIRED.

   This block used to turn the shell into a stacked page below 900px: `.weather-shell` to
   `display: block`, the rail into a full-width strip with a bottom border, and `.nav-list`
   into a horizontally scrolling row. It applied from 900 down to 641, because below 641 the
   rail is hidden entirely and AppBottomBar takes over.

   That intermediate mode is gone. Navigation is now one of exactly two things at any width:

     641px and up    the left rail
     640px and down  AppMobileBar and AppBottomBar

   and there is no third state to fall into. The rail's own collapse behaviour was gated at
   `min-width: 901px` throughout this file, which only made sense while 900 was where the rail
   stopped existing; those gates are now 641 for the same reason.

   What survives here is the page header, which is not navigation: a narrow viewport still
   wants its title block and its actions to wrap. */
@media (max-width: 900px) {
    .topbar { align-items: flex-start; gap: 10px; }
    .header-actions { flex-wrap: wrap; justify-content: flex-end; }
}

@media (max-width: 640px) {
    .dashboard-main { padding: 12px 9px 22px; }
    .topbar { display: block; }
    .header-actions { justify-content: flex-start; margin-top: 10px; }
    .sun-time { display: none; }
    .edit-toolbar { align-items: flex-start; flex-direction: column; gap: 7px; }
    .card-size-small,
    .card-size-medium,
    .card-size-large {
    grid-column: span 6;
    grid-row: span 489;
}
    .card-status.card-size-large {
    grid-column: span 6;
    grid-row: span 489;
}
    .status-right { flex-wrap: wrap; }
    .current-condition-icon .weather-svg {
        height: 94px;
        width: 94px;
    }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition-duration: .01ms !important; }
}

/* Pass 7 modular card sizing
   Large: 6 columns x 4 units
   Medium: 3 columns x 2 units
   Small: 3 columns x 1 unit
*/
@media (min-width: 1181px) {
    .card-size-small {
        grid-column: span 3 !important;
        grid-row: span 129 !important;
    }

    .card-size-medium {
        grid-column: span 3 !important;
        grid-row: span 249 !important;
    }

    .card-size-large {
        grid-column: span 6 !important;
        grid-row: span 489 !important;
    }

    .card-hourlyForecast.card-size-large,
    .card-dailyForecast.card-size-large,
    .card-status.card-size-large {
        grid-column: 1 / -1 !important;
    }

    .card-temperatureHistory.card-size-large,
    .card-rainfallHistory.card-size-large {
        grid-column: span 6 !important;
    }
}

/* Pass 8 exact modular height math
   Visible small card: 120px
   Gap: 9px
   Grid allocation includes each card's final 9px bottom margin:
   Small  = 120 + 9 = 129
   Medium = (2 * 120) + (1 * 9) + 9 = 258
   Large  = (4 * 120) + (3 * 9) + 9 = 516
*/
@media (min-width: 1181px) {
    .dashboard-grid {
        grid-auto-rows: 1px !important;
        row-gap: 0 !important;
    }

    .weather-card {
        margin-bottom: 9px !important;
    }

    .card-size-small {
        grid-column: span 3 !important;
        grid-row: span 129 !important;
    }

    .card-size-medium {
        grid-column: span 3 !important;
        grid-row: span 258 !important;
    }

    .card-size-large {
        grid-column: span 6 !important;
        grid-row: span 516 !important;
    }

    .card-alerts.card-size-small {
        grid-column: span 3 !important;
        grid-row: span 129 !important;
    }

    .card-alerts.card-size-medium {
        grid-column: span 3 !important;
        grid-row: span 258 !important;
    }

    .card-alerts.card-size-large {
        grid-column: span 6 !important;
        grid-row: span 516 !important;
    }

    .card-temperatureHistory.card-size-large,
    .card-rainfallHistory.card-size-large {
        grid-column: span 6 !important;
        grid-row: span 516 !important;
    }

    .card-hourlyForecast.card-size-large,
    .card-dailyForecast.card-size-large {
        grid-column: 1 / -1 !important;
        grid-row: span 258 !important;
    }

    .card-status.card-size-large {
        grid-column: 1 / -1 !important;
        grid-row: span 69 !important;
    }
}

/* Consolidated radar, forecast, and compact history update */
/* THE MAP IS SEALED INTO ITS OWN STACKING CONTEXT.

   Leaflet numbers its own layers generously: panes at 400, controls and attribution at
   800. The shared chrome sits on --cx-z-chrome, which is 100 - deliberately low, so a
   menu the reader opened covers the navigation. Read against each other those numbers
   say the map paints over the bottom bar.

   It does not today, because an ancestor happens to create a stacking context. That is
   luck, not design, and it would end the first time that ancestor changed. isolation
   makes it explicit: every Leaflet layer, and every control of our own inside the map,
   is numbered relative to this element and can never compete with application chrome.
   No Leaflet value is overridden, because none needs to be. */
.radar-map-shell {
    bottom: 9px;
    isolation: isolate;
    left: 9px;
    position: absolute;
    right: 9px;
    top: 9px;
}

.radar-map {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 7px;
    height: 100%;
    overflow: hidden;
    width: 100%;
}

.radar-controls {
    align-items: center;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    border: 1px solid var(--border);
    border-radius: 7px;
    bottom: 9px;
    display: flex;
    gap: 5px;
    left: 50%;
    padding: 4px;
    position: absolute;
    transform: translateX(-50%);
    z-index: 500;
}

.radar-controls button {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--text);
    cursor: pointer;
    font-size: .55rem;
    min-height: 25px;
    padding: 0 8px;
}

.radar-controls button:hover,
.radar-controls button:focus-visible {
    border-color: var(--accent);
    outline: none;
}

.radar-time {
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--text);
    font-size: .52rem;
    padding: 5px 7px;
    position: absolute;
    right: 9px;
    top: 9px;
    z-index: 500;
}

/* THE FEED IS BEHIND. Same chrome as the timestamp it sits under - it is the same kind of
   note about the same thing - in the shared warning colour rather than a new one. Quiet on
   purpose: this is a caveat on the reading, not an alarm, and the radar keeps rendering
   underneath it. `hidden` does the hiding; there is no state class to keep in step. */
.radar-stale {
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--warning);
    font-size: .52rem;
    padding: 5px 7px;
    position: absolute;
    right: 9px;
    top: 36px;
    z-index: 500;
}

.radar-stale[hidden] {
    display: none;
}

.radar-error {
    align-items: center;
    color: var(--muted);
    display: flex;
    font-size: .65rem;
    height: 100%;
    justify-content: center;
}

.leaflet-container {
    background: var(--surface-2);
    font-family: inherit;
}

.leaflet-control-attribution {
    font-size: 8px;
}

@media (min-width: 1181px) {
    .card-temperatureHistory.card-size-large,
    .card-rainfallHistory.card-size-large {
        grid-column: span 6 !important;
        grid-row: span 129 !important;
    }
}


/* Consolidated modular layout
   Desktop grid: 24 columns
   Compact: 3 columns x 1 unit
   Small:   6 columns x 1 unit
   Medium:  6 columns x 2 units
   Large:  12 columns x 4 units
   One unit visible height: 120px
   Gap: 9px
*/
@media (min-width: 1181px) {
    .dashboard-grid {
        grid-template-columns: repeat(24, minmax(0, 1fr)) !important;
        grid-auto-rows: 1px !important;
        column-gap: 9px !important;
        row-gap: 0 !important;
    }

    .weather-card {
        margin-bottom: 9px !important;
    }

    .card-size-compact {
        grid-column: span 3 !important;
        grid-row: span 129 !important;
    }

    .card-size-small {
        grid-column: span 6 !important;
        grid-row: span 129 !important;
    }

    .card-size-medium {
        grid-column: span 6 !important;
        grid-row: span 258 !important;
    }

    .card-size-large {
        grid-column: span 12 !important;
        grid-row: span 516 !important;
    }

    .card-alerts.card-size-compact {
        grid-column: span 3 !important;
        grid-row: span 129 !important;
    }

    .card-alerts.card-size-small {
        grid-column: span 6 !important;
        grid-row: span 129 !important;
    }

    .card-alerts.card-size-medium {
        grid-column: span 6 !important;
        grid-row: span 258 !important;
    }

    .card-alerts.card-size-large {
        grid-column: span 12 !important;
        grid-row: span 516 !important;
    }

    .card-temperatureHistory.card-size-large,
    .card-rainfallHistory.card-size-large {
        grid-column: span 12 !important;
        grid-row: span 129 !important;
    }

    .card-hourlyForecast.card-size-large,
    .card-dailyForecast.card-size-large,
    .card-status.card-size-large {
        grid-column: 1 / -1 !important;
    }

    .card-hourlyForecast.card-size-large,
    .card-dailyForecast.card-size-large {
        grid-row: span 258 !important;
    }

    .card-status.card-size-large {
        grid-row: span 69 !important;
    }
}

.station-clock {
    align-items: flex-end;
    display: flex;
    flex-direction: column;
    gap: 1px;
    text-align: right;
}

.station-clock-label {
    color: var(--muted);
    font-size: .48rem;
    font-weight: 800;
    letter-spacing: .09em;
}

.station-clock strong {
    font-size: .86rem;
    letter-spacing: -.02em;
    margin-top: 2px;
}

.station-clock > span:not(.station-clock-label),
.station-clock small {
    color: var(--muted);
    font-size: .5rem;
}

.card-size-compact {
    padding: 9px;
}

.card-size-compact .card-title-row h2 {
    font-size: .64rem;
}

.card-size-compact .card-title-row {
    align-items: center;
}

.card-size-compact .metric-icon {
    height: 16px;
    transform: scale(.82);
    transform-origin: center;
    width: 16px;
}

.card-size-compact .sensor-center {
    inset: 29px 5px 5px;
}

.card-size-compact .sensor-value.md {
    font-size: 1.45rem;
}

.card-size-compact .sensor-unit {
    font-size: .55rem;
    padding-top: 3px;
}

.card-size-compact .sensor-note {
    font-size: .46rem;
    line-height: 1.15;
    margin-top: 4px;
}

.card-size-compact .moon-phase-graphic {
    height: 27px;
    width: 27px;
}

.alerts-content {
    align-content: start;
    display: block;
    overflow-y: auto;
    padding-right: 3px;
}

.alert-list {
    display: grid;
    gap: 7px;
}

.alert-row {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-left: 3px solid var(--warning);
    border-radius: 7px;
    padding: 7px 8px;
}

.alert-row.severity-extreme {
    border-left-color: var(--danger);
}

.alert-row.severity-severe {
    border-left-color: #f08a50;
}

.alert-row.severity-moderate {
    border-left-color: var(--warning);
}

.alert-row.severity-minor {
    border-left-color: var(--accent);
}

.alert-row strong {
    display: block;
    font-size: .62rem;
}

.alert-row span {
    color: var(--muted);
    display: block;
    font-size: .5rem;
    line-height: 1.25;
    margin-top: 3px;
}

.card-alerts.card-size-small .alert-row:not(:first-child),
.card-alerts.card-size-compact .alert-row:not(:first-child) {
    display: none;
}

.card-alerts.card-size-compact .alert-row span,
.card-alerts.card-size-compact .card-kicker {
    display: none;
}

.hourly-strip,
.daily-strip {
    grid-template-columns: repeat(8, minmax(0, 1fr)) !important;
    gap: 9px !important;
}

.hour-item,
.day-item {
    aspect-ratio: 1 / 1;
    min-height: 0;
    padding: 5px 4px;
    justify-content: center;
}

.hour-item .weather-svg,
.day-item .weather-svg {
    height: 30px;
    margin: 2px 0;
    width: 30px;
}

.hour-item strong,
.day-item strong {
    font-size: .68rem;
    margin-bottom: 2px;
}

.hour-item span,
.hour-item small,
.day-item span,
.day-item small {
    font-size: .46rem;
    line-height: 1.15;
}

@media (max-width: 1180px) {
    .card-size-compact {
        grid-column: span 2;
        grid-row: span 129;
    }

    .hourly-strip,
    .daily-strip {
        grid-template-columns: repeat(4, minmax(78px, 1fr)) !important;
    }
}

@media (max-width: 640px) {
    .card-size-compact {
        grid-column: span 6;
    }

    .hourly-strip,
    .daily-strip {
        grid-template-columns: repeat(2, minmax(88px, 1fr)) !important;
    }
}

/* Dashboard refinement build */
.sr-only {
    clip: rect(0, 0, 0, 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}

.card-currentConditions .card-title-row {
    align-items: flex-start !important;
}

.card-currentConditions .card-title-row > div:first-child,
.card-currentConditions .station-clock {
    padding-top: 0 !important;
}

.card-currentConditions .card-kicker,
.card-currentConditions .station-clock-label {
    display: block;
    line-height: 1 !important;
    margin: 0 !important;
    min-height: .55rem;
}

.card-currentConditions .station-clock {
    align-self: flex-start;
    justify-content: flex-start;
}

.card-alerts .card-title-row {
    align-items: center;
    margin-bottom: 8px;
    min-height: 24px;
    position: relative;
    z-index: 2;
}

.card-alerts .alerts-content {
    inset: auto !important;
    margin-top: 0 !important;
    max-height: calc(100% - 34px);
    overflow-y: auto;
    padding-top: 0 !important;
    position: relative !important;
}

.card-alerts .alert-list {
    padding-top: 0;
}

.card-alerts .alert-row {
    cursor: pointer;
    text-align: left;
    transition: border-color .15s ease, transform .15s ease, background .15s ease;
    width: 100%;
}

.card-alerts button.alert-row {
    appearance: none;
    color: inherit;
    font: inherit;
}

.card-alerts .alert-row:hover {
    background: color-mix(in srgb, var(--surface-2) 82%, var(--accent));
    transform: translateY(-1px);
}

.card-alerts .alert-row:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.dashboard-edit-icon {
    display: inline-flex;
    height: 20px;
    width: 20px;
}

.dashboard-edit-icon svg,
.radar-control-icon svg,
.weather-alert-dialog-close svg,
.sun-event-icon {
    fill: none;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
}

.dashboard-edit-label {
    font-size: 0;
}

.dashboard-edit-label::after {
    content: "Customize dashboard";
    font-size: .72rem;
}

body.dashboard-editing .dashboard-edit-sliders svg {
    display: none;
}

body.dashboard-editing .dashboard-edit-sliders::before {
    content: "ÃƒÂ¢Ã…â€œÃ¢â‚¬Å“";
    font-size: 1rem;
    font-weight: 800;
    line-height: 20px;
    text-align: center;
    width: 20px;
}

body.dashboard-editing .dashboard-edit-label::after {
    content: "Done";
}

.radar-window,
.radar-map,
.radar-content {
    position: relative;
}

.radar-controls,
.radar-control-bar,
.radar-playback-controls {
    align-items: center;
    backdrop-filter: blur(8px);
    background: rgba(18, 24, 33, .78);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 10px;
    bottom: 10px;
    box-shadow: 0 5px 18px rgba(0, 0, 0, .25);
    display: flex;
    gap: 4px;
    padding: 5px;
    position: absolute !important;
    right: 10px;
    top: auto !important;
    z-index: 600;
}

.radar-controls button,
.radar-control-bar button,
.radar-playback-controls button {
    align-items: center;
    background: transparent;
    border: 0;
    border-radius: 7px;
    color: #fff;
    display: inline-flex;
    height: 32px;
    justify-content: center;
    padding: 0;
    width: 32px;
}

.radar-controls button:hover,
.radar-control-bar button:hover,
.radar-playback-controls button:hover {
    background: rgba(255, 255, 255, .14);
}

.radar-control-icon {
    display: inline-flex;
    height: 19px;
    width: 19px;
}

.sun-event-icon {
    height: 30px;
    width: 30px;
}

.weather-alert-dialog {
    inset: 0;
    opacity: 0;
    pointer-events: none;
    position: fixed;
    transition: opacity .18s ease;
    z-index: var(--cx-z-dialog);
}

.weather-alert-dialog.is-open {
    opacity: 1;
    pointer-events: auto;
}

.weather-alert-dialog-backdrop {
    background: rgba(6, 10, 16, .68);
    inset: 0;
    position: absolute;
}

.weather-alert-dialog-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 15px;
    box-shadow: 0 24px 70px rgba(0, 0, 0, .38);
    left: 50%;
    max-height: min(82vh, 760px);
    max-width: 760px;
    overflow-y: auto;
    padding: 24px;
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    width: calc(100% - 32px);
}

.weather-alert-dialog-close {
    align-items: center;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: inherit;
    display: flex;
    height: 34px;
    justify-content: center;
    position: absolute;
    right: 15px;
    top: 15px;
    width: 34px;
}

.weather-alert-dialog-close svg {
    height: 18px;
    width: 18px;
}

.weather-alert-dialog-heading {
    padding-right: 42px;
}

.weather-alert-dialog-heading h2 {
    font-size: 1.25rem;
    margin: 6px 0;
}

.weather-alert-dialog-heading p,
.weather-alert-dialog-body p {
    color: var(--muted);
    line-height: 1.55;
    white-space: pre-line;
}

.weather-alert-dialog-body {
    display: grid;
    gap: 17px;
    margin-top: 20px;
}

.weather-alert-dialog-body h3 {
    font-size: .7rem;
    letter-spacing: .06em;
    margin-bottom: 5px;
    text-transform: uppercase;
}

.weather-alert-dialog-severity {
    border-radius: 999px;
    display: inline-flex;
    font-size: .64rem;
    font-weight: 800;
    padding: 4px 8px;
    text-transform: uppercase;
}

.weather-alert-dialog-severity.severity-extreme {
    background: rgba(220, 53, 69, .16);
    color: #e55d6b;
}

.weather-alert-dialog-severity.severity-severe {
    background: rgba(240, 138, 80, .16);
    color: #f08a50;
}

.weather-alert-dialog-severity.severity-moderate {
    background: rgba(224, 170, 45, .16);
    color: #dca72d;
}

.weather-alert-dialog-severity.severity-minor,
.weather-alert-dialog-severity.severity-unknown {
    background: rgba(80, 140, 220, .15);
    color: #6aa4ec;
}

.weather-alert-dialog-source {
    display: inline-flex;
    font-size: .72rem;
    font-weight: 700;
    margin-top: 20px;
}

body.alert-dialog-open {
    overflow: hidden;
}

.card-hourlyForecast .card-title-row,
.card-dailyForecast .card-title-row {
    margin-bottom: 5px !important;
}

.card-hourlyForecast,
.card-dailyForecast {
    padding-bottom: 8px !important;
}

.card-hourlyForecast .card-content,
.card-dailyForecast .card-content,
.card-hourlyForecast .hourly-strip,
.card-dailyForecast .daily-strip {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

.hourly-strip,
.daily-strip {
    align-items: stretch;
    gap: 9px !important;
}

.hour-item,
.day-item {
    align-items: center;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 7px 5px !important;
}

.hour-item .weather-svg,
.day-item .weather-svg,
.hour-item img,
.day-item img {
    height: 43px !important;
    margin: 3px 0 !important;
    width: 43px !important;
}

.hour-item strong,
.day-item strong {
    font-size: .88rem !important;
    line-height: 1.05;
}

.hour-item > span:first-child,
.day-item > span:first-child {
    font-size: .62rem !important;
    font-weight: 700;
}

.hour-item span,
.hour-item small,
.day-item span,
.day-item small {
    font-size: .57rem !important;
    line-height: 1.2;
}

.card-size-compact.card-humidity .sensor-note,
.card-size-compact.card-dewPoint .sensor-note,
.card-size-compact.card-dewpoint .sensor-note,
.card-size-compact.card-wind .wind-gust,
.card-size-compact.card-wind .sensor-note {
    display: none !important;
}

.card-size-compact.card-wind .wind-direction,
.card-size-compact.card-wind .wind-arrow {
    display: inline-flex !important;
}

@media (min-width: 1181px) {
    .card-hourlyForecast.card-size-large,
    .card-dailyForecast.card-size-large {
        grid-row: span 190 !important;
    }
}

@media (max-width: 700px) {
    .weather-alert-dialog-panel {
        border-radius: 12px;
        padding: 19px;
    }

    .dashboard-edit-label {
        display: none;
    }
}

/* Dashboard polish build */

/* Forecast cards: add back approximately one standard padding width of height. */
@media (min-width: 1181px) {
    .card-hourlyForecast.card-size-large,
    .card-dailyForecast.card-size-large {
        grid-row: span 204 !important;
    }
}

.card-hourlyForecast,
.card-dailyForecast {
    padding-bottom: 14px !important;
}

.card-hourlyForecast .card-title-row,
.card-dailyForecast .card-title-row {
    margin-bottom: 7px !important;
}

/* Modern alert scrollbar with all alerts still accessible. */
.card-alerts .alerts-content {
    overflow-y: auto !important;
    scrollbar-gutter: stable;
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
}

.card-alerts .alerts-content:hover,
.card-alerts .alerts-content:focus-within {
    scrollbar-color: color-mix(in srgb, var(--muted) 60%, transparent) transparent;
}

.card-alerts .alerts-content::-webkit-scrollbar {
    width: 7px;
}

.card-alerts .alerts-content::-webkit-scrollbar-track {
    background: transparent;
}

.card-alerts .alerts-content::-webkit-scrollbar-thumb {
    background: transparent;
    border: 2px solid transparent;
    border-radius: 999px;
    background-clip: padding-box;
    min-height: 36px;
}

.card-alerts .alerts-content:hover::-webkit-scrollbar-thumb,
.card-alerts .alerts-content:focus-within::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--muted) 62%, transparent);
    background-clip: padding-box;
}

.card-alerts .alerts-content::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--text) 58%, transparent);
    background-clip: padding-box;
}

/* Keep every active alert rendered; card height controls how many are visible. */
.card-alerts .alert-row {
    display: block !important;
}

/* Readable modern alert modal: lightly frosted, not overly transparent. */
.weather-alert-dialog-panel {
    background:
        linear-gradient(
            180deg,
            color-mix(in srgb, var(--surface) 97%, transparent),
            color-mix(in srgb, var(--surface) 95%, transparent)
        ) !important;
    backdrop-filter: blur(18px) saturate(118%);
    -webkit-backdrop-filter: blur(18px) saturate(118%);
    border: 1px solid color-mix(in srgb, var(--border) 78%, var(--text) 12%) !important;
    box-shadow:
        0 28px 80px rgba(0, 0, 0, .48),
        0 0 0 1px rgba(255, 255, 255, .035) inset !important;
}

.weather-alert-dialog-backdrop {
    background: rgba(5, 9, 15, .72) !important;
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

.weather-alert-dialog-heading h2,
.weather-alert-dialog-body h3 {
    color: var(--text) !important;
}

.weather-alert-dialog-heading p,
.weather-alert-dialog-body p {
    color: color-mix(in srgb, var(--text) 84%, var(--muted)) !important;
}

/* Icon-only dashboard customization control. */
.dashboard-customize-icon {
    align-items: center;
    display: inline-flex;
    height: 21px;
    justify-content: center;
    width: 21px;
}

.dashboard-customize-icon svg {
    fill: none;
    height: 21px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
    width: 21px;
}

button:has(.dashboard-customize-icon) {
    align-items: center;
    aspect-ratio: 1;
    border-radius: 9px;
    display: inline-flex;
    justify-content: center;
    min-height: 36px;
    padding: 7px !important;
    width: 36px;
}

body.dashboard-editing .dashboard-customize-icon svg {
    display: none;
}

body.dashboard-editing .dashboard-customize-icon::before {
    content: "\2713";
    font-size: 1.05rem;
    font-weight: 800;
    line-height: 1;
}

button:has(.dashboard-customize-icon)::after {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 7px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .22);
    color: var(--text);
    content: "Customize dashboard";
    font-size: .66rem;
    font-weight: 700;
    opacity: 0;
    padding: 6px 8px;
    pointer-events: none;
    position: absolute;
    right: 0;
    top: calc(100% + 7px);
    transform: translateY(-3px);
    transition: opacity .15s ease, transform .15s ease;
    white-space: nowrap;
    z-index: var(--cx-z-menu);
}

button:has(.dashboard-customize-icon) {
    position: relative;
}

button:has(.dashboard-customize-icon):hover::after,
button:has(.dashboard-customize-icon):focus-visible::after {
    opacity: 1;
    transform: translateY(0);
}

body.dashboard-editing button:has(.dashboard-customize-icon)::after {
    content: "Finish customizing";
}

/* Wind compact and forecast padding fix */

/* The Wind card title already identifies the measurement, so hide the redundant "Current" kicker. */
.card-wind .card-kicker {
    display: none !important;
}

/* Compact Wind gets a purpose-built layout rather than a scaled-down full card. */
.card-wind.card-size-compact {
    overflow: hidden;
    padding: 8px !important;
}

.card-wind.card-size-compact .card-title-row {
    min-height: 18px;
    margin-bottom: 2px !important;
}

.card-wind.card-size-compact .card-title-row h2 {
    font-size: .62rem !important;
    line-height: 1;
}

.card-wind.card-size-compact .sensor-center,
.card-wind.card-size-compact .wind-content,
.card-wind.card-size-compact .wind-layout {
    align-items: center !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 2px !important;
    inset: 24px 5px 5px !important;
    justify-content: center !important;
    overflow: hidden;
}

.card-wind.card-size-compact .wind-main,
.card-wind.card-size-compact .wind-primary,
.card-wind.card-size-compact .sensor-value-row {
    align-items: baseline !important;
    display: flex !important;
    flex-wrap: nowrap !important;
    gap: 2px !important;
    justify-content: center !important;
    max-width: 100%;
    white-space: nowrap;
}

.card-wind.card-size-compact .sensor-value,
.card-wind.card-size-compact .wind-speed,
.card-wind.card-size-compact .sensor-value.md,
.card-wind.card-size-compact .sensor-value.lg {
    font-size: 1.18rem !important;
    line-height: .95 !important;
    max-width: 100%;
}

.card-wind.card-size-compact .sensor-unit,
.card-wind.card-size-compact .wind-unit {
    font-size: .46rem !important;
    line-height: 1 !important;
    padding-top: 0 !important;
}

.card-wind.card-size-compact .wind-direction-row,
.card-wind.card-size-compact .wind-direction {
    align-items: center !important;
    display: flex !important;
    gap: 3px !important;
    justify-content: center !important;
    line-height: 1 !important;
    max-width: 100%;
}

.card-wind.card-size-compact .wind-arrow,
.card-wind.card-size-compact .direction-arrow {
    height: 13px !important;
    min-height: 13px !important;
    min-width: 13px !important;
    width: 13px !important;
}

.card-wind.card-size-compact .wind-direction,
.card-wind.card-size-compact .direction-label,
.card-wind.card-size-compact .wind-bearing {
    font-size: .49rem !important;
}

.card-wind.card-size-compact .wind-gust,
.card-wind.card-size-compact .gust-row,
.card-wind.card-size-compact .sensor-note,
.card-wind.card-size-compact .secondary-value {
    display: none !important;
}

/* Restore balanced padding beneath the forecast tiles by making the card slightly taller. */
@media (min-width: 1181px) {
    .card-hourlyForecast.card-size-large,
    .card-dailyForecast.card-size-large {
        grid-row: span 216 !important;
    }
}

.card-hourlyForecast,
.card-dailyForecast {
    padding-bottom: 18px !important;
}

.card-hourlyForecast .hourly-strip,
.card-dailyForecast .daily-strip {
    margin-bottom: 0 !important;
}

/* Hourly precipitation probability */
.forecast-precip {
    align-items: center;
    color: #66aaf2;
    display: inline-flex;
    font-size: .58rem;
    font-weight: 750;
    gap: 3px;
    justify-content: center;
    line-height: 1;
    margin-top: 2px;
    min-height: 13px;
}

.forecast-precip svg {
    fill: none;
    height: 11px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
    width: 11px;
}

.hour-item .forecast-precip span {
    color: inherit;
    font-size: inherit !important;
    font-weight: inherit;
    line-height: inherit;
}

/* Modern sunrise and sunset icons */
.modern-sun-event-icon {
    fill: none;
    flex: 0 0 auto;
    height: 31px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
    width: 31px;
}

.modern-sunrise-icon {
    color: #f2b84b;
}

.modern-sunset-icon {
    color: #e98a62;
}

.sun-event,
.sunrise,
.sunset,
.sun-time,
.sun-item,
.sun-row {
    align-items: center;
}

.modern-sun-event-icon + * {
    margin-left: 3px;
}

/* Safe hourly rain chance rendering */
.forecast-rain-chance {
    align-items: center;
    color: #67aef2;
    display: inline-flex;
    font-size: .58rem;
    font-weight: 750;
    gap: 3px;
    justify-content: center;
    line-height: 1;
    margin: 2px 0;
    min-height: 13px;
}

.forecast-rain-chance svg {
    fill: none;
    height: 11px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
    width: 11px;
}

.hour-item .forecast-rain-chance span {
    color: inherit;
    font-size: inherit !important;
    font-weight: inherit;
    line-height: inherit;
}

/* Safe sunrise and sunset icon replacement */
.sun-event-icon-v2 {
    fill: none;
    flex: 0 0 auto;
    height: 30px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.9;
    width: 30px;
}

.sunrise-icon-v2 {
    color: #f1b84b;
}

.sunset-icon-v2 {
    color: #e58a62;
}

/* Hide legacy sunrise and sunset icons */
.sun-event:has(.sunrise-icon-v2) > svg:not(.sunrise-icon-v2),
.sun-event:has(.sunrise-icon-v2) > img,
.sun-item:has(.sunrise-icon-v2) > svg:not(.sunrise-icon-v2),
.sun-item:has(.sunrise-icon-v2) > img,
.sun-row:has(.sunrise-icon-v2) > svg:not(.sunrise-icon-v2),
.sun-row:has(.sunrise-icon-v2) > img,
.sun-time:has(.sunrise-icon-v2) > svg:not(.sunrise-icon-v2),
.sun-time:has(.sunrise-icon-v2) > img,
.sunrise:has(.sunrise-icon-v2) > svg:not(.sunrise-icon-v2),
.sunrise:has(.sunrise-icon-v2) > img,
.sun-event:has(.sunset-icon-v2) > svg:not(.sunset-icon-v2),
.sun-event:has(.sunset-icon-v2) > img,
.sun-item:has(.sunset-icon-v2) > svg:not(.sunset-icon-v2),
.sun-item:has(.sunset-icon-v2) > img,
.sun-row:has(.sunset-icon-v2) > svg:not(.sunset-icon-v2),
.sun-row:has(.sunset-icon-v2) > img,
.sun-time:has(.sunset-icon-v2) > svg:not(.sunset-icon-v2),
.sun-time:has(.sunset-icon-v2) > img,
.sunset:has(.sunset-icon-v2) > svg:not(.sunset-icon-v2),
.sunset:has(.sunset-icon-v2) > img {
    display: none !important;
}

/* Direct sunrise and sunset icons */
.modern-sunrise-icon,
.modern-sunset-icon {
    fill: none !important;
    flex: 0 0 auto;
    height: 30px !important;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.9;
    width: 30px !important;
}

.modern-sunrise-icon {
    color: #f1b84b;
}

.modern-sunset-icon {
    color: #e58a62;
}

/* Full account area */
.user-menu { position: relative; }
.user-menu-button { align-items: center; display: inline-flex; height: 38px; justify-content: center; padding: 0; width: 38px; }
.user-menu-button svg,
.user-menu-avatar svg,
.user-menu-links svg,
.user-preference-link svg,
.account-back-link svg,
.account-header-avatar svg,
.account-device-icon svg {
    fill: none;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
}
.user-menu-button svg { height: 20px; width: 20px; }
.user-menu-panel {
    background: rgba(24, 27, 32, .98);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 14px;
    box-shadow: 0 18px 48px rgba(0, 0, 0, .34);
    min-width: 265px;
    overflow: hidden;
    padding: 10px;
    position: absolute;
    right: 0;
    top: calc(100% + 9px);
    z-index: var(--cx-z-menu);
}
.user-menu-profile { align-items: center; display: flex; gap: 10px; padding: 8px 9px 12px; }
.user-menu-avatar { align-items: center; background: rgba(255,255,255,.07); border-radius: 50%; display: inline-flex; height: 34px; justify-content: center; width: 34px; }
.user-menu-avatar svg { height: 18px; width: 18px; }
.user-menu-profile > span:last-child,
.user-preference-link > span { display: grid; gap: 2px; }
.user-menu-profile strong,
.user-preference-link strong { color: var(--text-primary, #f3f5f7); font-size: .76rem; }
.user-menu-profile small,
.user-preference-link small { color: var(--text-secondary, #9199a6); font-size: .62rem; }
.user-menu-links { border-bottom: 1px solid rgba(255,255,255,.08); border-top: 1px solid rgba(255,255,255,.08); display: grid; padding: 5px 0; }
.user-menu-links a,
.user-menu-links button,
.user-preference-link,
.user-preference-row { align-items: center; border-radius: 9px; color: inherit; display: flex; gap: 9px; padding: 8px 9px; text-decoration: none; }
.user-menu-links a:hover,
.user-menu-links button:hover,
.user-preference-link:hover,
.user-preference-row:hover { background: rgba(255,255,255,.06); }
.user-menu-links button {
    background: transparent;
    border: 0;
    font: inherit;
    text-align: left;
    width: 100%;
}
.user-menu-links svg { height: 16px; width: 16px; }
.user-menu-links span { font-size: .73rem; font-weight: 650; }
.user-preferences { border-top: 0; padding-top: 5px; }
.user-preferences summary { align-items: center; cursor: pointer; display: flex; justify-content: space-between; list-style: none; padding: 8px 9px; }
.user-preferences summary::-webkit-details-marker { display: none; }
.user-preferences summary svg,
.user-preference-link > svg { height: 14px; width: 14px; }
.user-preferences[open] summary svg { transform: rotate(90deg); }
.user-preference-row { background: transparent; border: 0; justify-content: space-between; text-align: left; width: 100%; }
.user-preference-row > span:first-child { display: grid; gap: 2px; }
.theme-switch { background: rgba(255,255,255,.14); border-radius: 999px; display: inline-flex; height: 20px; padding: 2px; width: 36px; }
.theme-switch > span { background: #fff; border-radius: 50%; height: 16px; transition: transform .16s ease; width: 16px; }
.theme-switch.active { background: var(--accent-color, #4f9cf9); }
.theme-switch.active > span { transform: translateX(16px); }

.account-page { margin: 0 auto; max-width: 1180px; padding: 28px 28px 60px; }
.account-page-header { align-items: flex-end; display: flex; justify-content: space-between; margin-bottom: 26px; }
.account-page-header h1 { font-size: 1.75rem; margin: 10px 0 5px; }
.account-page-header p,
.account-section-heading p { color: var(--text-secondary, #9199a6); margin: 0; }
.account-back-link { align-items: center; color: var(--text-secondary, #9199a6); display: inline-flex; font-size: .75rem; gap: 4px; text-decoration: none; }
.account-back-link svg { height: 15px; width: 15px; }
.account-header-avatar { align-items: center; background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.1); border-radius: 50%; display: inline-flex; height: 56px; justify-content: center; width: 56px; }
.account-header-avatar svg { height: 28px; width: 28px; }
.account-layout { align-items: start; display: grid; gap: 24px; grid-template-columns: 210px minmax(0,1fr); }
.account-nav { display: grid; gap: 4px; position: sticky; top: 20px; }
.account-nav a { border-radius: 9px; color: var(--text-secondary, #a3aab5); font-size: .75rem; font-weight: 650; padding: 9px 11px; text-decoration: none; }
.account-nav a:hover { background: rgba(255,255,255,.05); color: var(--text-primary, #f3f5f7); }
.account-content { display: grid; gap: 18px; }
.account-section { background: rgba(255,255,255,.025); border: 1px solid rgba(255,255,255,.08); border-radius: 16px; padding: 22px; scroll-margin-top: 20px; }
.account-section-heading { align-items: center; display: flex; gap: 20px; justify-content: space-between; margin-bottom: 20px; }
.account-section-heading h2 { font-size: 1rem; margin: 0 0 4px; }
.account-section-heading p { font-size: .72rem; }
.account-form-grid { display: grid; gap: 15px; grid-template-columns: repeat(2,minmax(0,1fr)); }
.account-form-grid label { display: grid; gap: 6px; }
.account-form-grid label > span { color: var(--text-secondary, #a3aab5); font-size: .69rem; font-weight: 650; }
.account-form-grid input,
.account-form-grid select { background: rgba(0,0,0,.14); border: 1px solid rgba(255,255,255,.1); border-radius: 9px; color: inherit; font: inherit; font-size: .76rem; padding: 10px 11px; width: 100%; }
.account-actions { display: flex; justify-content: flex-end; margin-top: 18px; }
.account-primary-button,
.account-setting-row button,
.account-device-card button,
.account-plan-card button { background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1); border-radius: 9px; color: inherit; cursor: pointer; font: inherit; font-size: .7rem; font-weight: 700; padding: 8px 11px; }
.account-primary-button { background: var(--accent-color, #4f9cf9); border-color: transparent; color: #fff; }
.account-setting-list { display: grid; }
.account-setting-row,
.account-toggle-row { align-items: center; border-top: 1px solid rgba(255,255,255,.07); display: flex; gap: 18px; justify-content: space-between; padding: 14px 0; }
.account-setting-row:first-child,
.account-toggle-row:first-child { border-top: 0; padding-top: 0; }
.account-setting-row:last-child,
.account-toggle-row:last-child { padding-bottom: 0; }
.account-setting-row > span,
.account-toggle-row > span,
.account-device-card > span:nth-child(2),
.account-plan-card > span:first-child { display: grid; gap: 3px; }
.account-setting-row strong,
.account-toggle-row strong,
.account-device-card strong,
.account-plan-card strong { font-size: .76rem; }
.account-setting-row small,
.account-toggle-row small,
.account-device-card small,
.account-plan-card small { color: var(--text-secondary, #9199a6); font-size: .66rem; }
.account-toggle-row input { height: 17px; width: 17px; }
.account-device-card,
.account-plan-card { align-items: center; background: rgba(255,255,255,.025); border: 1px solid rgba(255,255,255,.07); border-radius: 12px; display: flex; gap: 12px; padding: 14px; }
.account-device-card > span:nth-child(2),
.account-plan-card > span:first-child { margin-right: auto; }
.account-device-icon { align-items: center; background: rgba(255,255,255,.06); border-radius: 10px; display: inline-flex; height: 38px; justify-content: center; width: 38px; }
.account-device-icon svg { height: 20px; width: 20px; }
.account-danger-row button { color: #ef8f8f; }

@media (max-width: 800px) {
    .account-page { padding: 20px 16px 50px; }
    .account-layout { grid-template-columns: 1fr; }
    .account-nav { display: flex; gap: 4px; overflow-x: auto; padding-bottom: 4px; position: static; }
    .account-nav a { flex: 0 0 auto; }
    .account-form-grid { grid-template-columns: 1fr; }
}

@media (max-width: 520px) {
    .account-page-header { align-items: flex-start; }
    .account-header-avatar { height: 44px; width: 44px; }
    .account-section { padding: 17px; }
    .account-section-heading { align-items: flex-start; flex-direction: column; }
    .account-setting-row,
    .account-toggle-row { align-items: flex-start; }
}

/* Compact wind arrow removal */
.card-wind.card-size-compact .wind-direction-arrow,
.card-wind.card-size-compact .compact-wind-arrow {
    display: none !important;
}

/* Visual consistency system */
:root {
    --type-kicker: .56rem;
    --type-card-title: .72rem;
    --type-compact-value: 1.42rem;
    --type-unit: .5rem;
    --type-secondary: .56rem;
    --type-metadata: .5rem;
    --type-pill: .52rem;
}

/* Shared section labels */
.card-kicker,
.section-kicker,
.forecast-kicker,
.radar-kicker,
.alerts-kicker {
    font-size: var(--type-kicker) !important;
    font-weight: 800 !important;
    letter-spacing: .09em !important;
    line-height: 1.1 !important;
}

/* Shared card titles */
.dashboard-card h2,
.card-title-row h2,
.card-alerts h2,
.card-wind h2,
.card-moon h2,
.card-lightning h2,
.card-uv h2,
.card-rainToday h2,
.card-solar h2,
.card-dewPoint h2,
.card-dewpoint h2 {
    font-size: var(--type-card-title) !important;
    font-weight: 750 !important;
    line-height: 1.15 !important;
}

/* Compact-card values */
.card-size-compact .sensor-value,
.card-size-small .sensor-value,
.card-size-compact .metric-value,
.card-size-small .metric-value,
.card-size-compact .wind-speed,
.card-size-small .wind-speed {
    font-size: var(--type-compact-value) !important;
    font-weight: 800 !important;
    line-height: .95 !important;
}

/* Keep Wind aligned with the other compact values */
.card-wind.card-size-compact .sensor-value,
.card-wind.card-size-small .sensor-value,
.card-wind.card-size-compact .sensor-value.lg,
.card-wind.card-size-small .sensor-value.lg {
    font-size: var(--type-compact-value) !important;
}

/* Units */
.sensor-unit,
.wind-unit,
.metric-unit,
.value-unit {
    font-size: var(--type-unit) !important;
    font-weight: 700 !important;
    line-height: 1 !important;
    margin-left: 3px !important;
}

/* Secondary labels and notes */
.sensor-note,
.sensor-state,
.wind-details small,
.condition-details small,
.alerts-content span,
/* Account's own rows used to be pinned here too, at --type-secondary. That is
   8.96px, which is below comfortable reading size for description text, and an
   !important in a global sheet meant the Account stylesheet could not raise it.
   Account now owns its own type scale - see --account-type-* in Account.razor.css.
   The dashboard selectors below keep this treatment unchanged. */
.sensor-note-legacy-anchor-does-not-exist {
    font-size: var(--type-secondary) !important;
    line-height: 1.35 !important;
}

/* Pills and source badges */
.pill,
.success-pill,
.source-pill,
.alert-count,
.card-badge,
.radar-source,
.forecast-source {
    font-size: var(--type-pill) !important;
    font-weight: 750 !important;
    line-height: 1 !important;
    min-height: 22px;
    padding: 5px 8px !important;
}

/* Wind compact card spacing */
.card-wind.card-size-compact .wind-compact {
    align-items: center !important;
    display: flex !important;
    flex-direction: column !important;
    height: calc(100% - 24px);
    justify-content: center !important;
    padding-bottom: 2px;
}

.card-wind.card-size-compact .wind-primary {
    align-items: baseline !important;
    display: flex !important;
    justify-content: center !important;
}

/* Alerts readability */
.card-alerts .alert-row strong,
.card-alerts .alerts-content strong {
    font-size: .62rem !important;
    line-height: 1.25 !important;
}

.card-alerts .alert-row span,
.card-alerts .alerts-content span {
    font-size: .54rem !important;
    line-height: 1.35 !important;
}

/* Current Conditions balance */
.card-currentConditions .current-condition-icon,
.card-currentConditions .current-condition-icon .weather-svg {
    transform: scale(.88);
    transform-origin: center;
}

/* Forecast tile typography */
.hour-item > span:first-child,
.day-item > span:first-child {
    font-size: .56rem !important;
    font-weight: 700 !important;
}

.hour-item strong,
.day-item strong {
    font-size: .78rem !important;
    font-weight: 800 !important;
    line-height: 1 !important;
}

.hour-item small,
.day-item small,
.forecast-rain-chance {
    font-size: .52rem !important;
    line-height: 1.25 !important;
}

.day-item small {
    display: -webkit-box;
    max-width: 100%;
    min-height: 2.5em;
    overflow: hidden;
    text-align: center;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
}

/* Sidebar typography */
.sidebar a,
.sidebar button,
.nav-link {
    font-size: .72rem !important;
    font-weight: 600 !important;
}

/* Consistent metadata */
.station-heading p,
.sun-time small,
.station-clock-label,
.forecast-loading,
.status-left span,
.status-right span {
    font-size: var(--type-metadata) !important;
}

/* Scrollable 12-hour and 10-day forecasts */
#hourly-forecast-strip,
#daily-forecast-strip,
.hourly-strip,
.daily-strip {
    display: grid !important;
    gap: 9px !important;
    grid-auto-flow: column !important;
    grid-auto-columns: minmax(138px, 1fr) !important;
    grid-template-columns: none !important;
    overflow-x: auto !important;
    overflow-y: hidden !important;
    padding-bottom: 10px !important;
    scrollbar-color: rgba(120, 128, 140, .75) rgba(120, 128, 140, .14);
    scrollbar-width: thin;
    scroll-behavior: smooth;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
}

#hourly-forecast-strip .hour-item,
#daily-forecast-strip .day-item,
.hourly-strip .hour-item,
.daily-strip .day-item {
    min-width: 138px;
    scroll-snap-align: start;
}

#hourly-forecast-strip::-webkit-scrollbar,
#daily-forecast-strip::-webkit-scrollbar,
.hourly-strip::-webkit-scrollbar,
.daily-strip::-webkit-scrollbar {
    height: 8px;
}

#hourly-forecast-strip::-webkit-scrollbar-track,
#daily-forecast-strip::-webkit-scrollbar-track,
.hourly-strip::-webkit-scrollbar-track,
.daily-strip::-webkit-scrollbar-track {
    background: rgba(120, 128, 140, .12);
    border-radius: 999px;
}

#hourly-forecast-strip::-webkit-scrollbar-thumb,
#daily-forecast-strip::-webkit-scrollbar-thumb,
.hourly-strip::-webkit-scrollbar-thumb,
.daily-strip::-webkit-scrollbar-thumb {
    background: rgba(120, 128, 140, .66);
    border: 2px solid transparent;
    background-clip: padding-box;
    border-radius: 999px;
}

#hourly-forecast-strip::-webkit-scrollbar-thumb:hover,
#daily-forecast-strip::-webkit-scrollbar-thumb:hover,
.hourly-strip::-webkit-scrollbar-thumb:hover,
.daily-strip::-webkit-scrollbar-thumb:hover {
    background: rgba(120, 128, 140, .9);
    background-clip: padding-box;
}

@media (max-width: 700px) {
    #hourly-forecast-strip,
    #daily-forecast-strip,
    .hourly-strip,
    .daily-strip {
        grid-auto-columns: minmax(126px, 78vw) !important;
    }

    #hourly-forecast-strip .hour-item,
    #daily-forecast-strip .day-item,
    .hourly-strip .hour-item,
    .daily-strip .day-item {
        min-width: 126px;
    }
}

/* Forecast scrolling and daily layout correction */

/* Give each forecast card its own appropriate height. */
@media (min-width: 1181px) {
    .card-hourlyForecast.card-size-large {
        grid-row: span 226 !important;
    }

    .card-dailyForecast.card-size-large {
        grid-row: span 258 !important;
    }
}

/* Keep the scrollers inside the card and reserve room for the scrollbar. */
.card-hourlyForecast,
.card-dailyForecast {
    overflow: hidden !important;
    padding-bottom: 12px !important;
}

.card-hourlyForecast .hourly-strip,
.card-dailyForecast .daily-strip {
    box-sizing: border-box;
    max-width: 100%;
    min-width: 0;
    width: 100%;
}

/* Use fixed tile widths so additional periods must scroll instead of shrinking. */
#hourly-forecast-strip,
#daily-forecast-strip {
    display: flex !important;
    gap: 9px !important;
    overflow-x: scroll !important;
    overflow-y: hidden !important;
    padding: 0 0 12px !important;
    scrollbar-gutter: stable;
    scrollbar-width: auto;
    scrollbar-color: color-mix(in srgb, var(--muted) 78%, transparent)
                     color-mix(in srgb, var(--border) 74%, transparent);
    scroll-behavior: smooth;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
}

#hourly-forecast-strip .hour-item {
    flex: 0 0 150px !important;
    min-width: 150px !important;
    scroll-snap-align: start;
}

#daily-forecast-strip .day-item {
    flex: 0 0 186px !important;
    min-width: 186px !important;
    scroll-snap-align: start;
}

/* Make the scrollbar clearly visible. */
#hourly-forecast-strip::-webkit-scrollbar,
#daily-forecast-strip::-webkit-scrollbar {
    height: 11px;
}

#hourly-forecast-strip::-webkit-scrollbar-track,
#daily-forecast-strip::-webkit-scrollbar-track {
    background: color-mix(in srgb, var(--border) 70%, transparent);
    border-radius: 999px;
}

#hourly-forecast-strip::-webkit-scrollbar-thumb,
#daily-forecast-strip::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--muted) 82%, transparent);
    border: 2px solid transparent;
    background-clip: padding-box;
    border-radius: 999px;
}

#hourly-forecast-strip::-webkit-scrollbar-thumb:hover,
#daily-forecast-strip::-webkit-scrollbar-thumb:hover {
    background: color-mix(in srgb, var(--text) 72%, transparent);
    background-clip: padding-box;
}

/* Move daily headings and icons upward, leaving the lower area for variable text. */
#daily-forecast-strip .day-item {
    aspect-ratio: auto !important;
    box-sizing: border-box;
    justify-content: flex-start !important;
    min-height: 178px;
    padding: 13px 9px 11px !important;
}

#daily-forecast-strip .day-item > span:first-child {
    margin-bottom: 5px !important;
}

#daily-forecast-strip .day-item .weather-svg,
#daily-forecast-strip .day-item img {
    flex: 0 0 auto;
    height: 42px !important;
    margin: 1px 0 5px !important;
    width: 42px !important;
}

#daily-forecast-strip .day-item strong {
    margin: 0 0 6px !important;
}

#daily-forecast-strip .day-item small {
    display: block !important;
    line-height: 1.28 !important;
    margin-top: 0;
    max-width: 100%;
    min-height: 3.84em;
    overflow: hidden;
    text-align: center;
    white-space: normal;
    -webkit-box-orient: initial;
    -webkit-line-clamp: initial;
}

/* Keep hourly tiles compact while still leaving room for their scrollbar. */
#hourly-forecast-strip .hour-item {
    aspect-ratio: auto !important;
    box-sizing: border-box;
    min-height: 150px;
    padding: 10px 7px !important;
}

@media (max-width: 700px) {
    #hourly-forecast-strip .hour-item {
        flex-basis: 132px !important;
        min-width: 132px !important;
    }

    #daily-forecast-strip .day-item {
        flex-basis: 168px !important;
        min-width: 168px !important;
    }
}

/* Matching hourly and daily forecast tile sizes */
#hourly-forecast-strip .hour-item,
#daily-forecast-strip .day-item {
    box-sizing: border-box;
    flex: 0 0 150px !important;
    height: 150px !important;
    min-height: 150px !important;
    min-width: 150px !important;
    max-height: 150px !important;
    max-width: 150px !important;
}

/* Keep the daily content higher while preserving room for forecast text. */
#daily-forecast-strip .day-item {
    justify-content: flex-start !important;
    padding: 10px 7px 7px !important;
}

#daily-forecast-strip .day-item > span:first-child {
    margin-bottom: 2px !important;
}

#daily-forecast-strip .day-item .weather-svg,
#daily-forecast-strip .day-item img {
    height: 34px !important;
    margin: 0 0 2px !important;
    width: 34px !important;
}

#daily-forecast-strip .day-item strong {
    margin: 0 0 3px !important;
}

#daily-forecast-strip .day-item small {
    display: -webkit-box !important;
    font-size: .48rem !important;
    line-height: 1.18 !important;
    margin: 0 !important;
    max-width: 100%;
    min-height: 0 !important;
    overflow: hidden;
    text-align: center;
    white-space: normal;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
}

/* Both forecast containers use matching tile widths at smaller screen sizes too. */
@media (max-width: 700px) {
    #hourly-forecast-strip .hour-item,
    #daily-forecast-strip .day-item {
        flex-basis: 132px !important;
        height: 150px !important;
        min-width: 132px !important;
        max-width: 132px !important;
    }
}

/* Matching hourly and daily forecast icon sizes */
#hourly-forecast-strip .hour-item .weather-svg,
#hourly-forecast-strip .hour-item img,
#daily-forecast-strip .day-item .weather-svg,
#daily-forecast-strip .day-item img {
    flex: 0 0 auto;
    height: 43px !important;
    width: 43px !important;
}

/* Preserve the higher daily layout while using the same icon dimensions. */
#daily-forecast-strip .day-item .weather-svg,
#daily-forecast-strip .day-item img {
    margin: 0 0 2px !important;
}

/* Radar control bar sizing and theme fixes */
.radar-controls {
    left: auto !important;
    right: 10px !important;
    transform: none !important;
    width: max-content;
    max-width: calc(100% - 20px);
}

.weather-shell.theme-dark .radar-controls {
    background: rgba(18, 24, 33, .78);
    border-color: rgba(255, 255, 255, .12);
    box-shadow: 0 5px 18px rgba(0, 0, 0, .25);
}

.weather-shell.theme-dark .radar-controls button {
    color: #fff;
}

.weather-shell.theme-dark .radar-controls button:hover,
.weather-shell.theme-dark .radar-controls button:focus-visible {
    background: rgba(255, 255, 255, .14);
}

.weather-shell.theme-light .radar-controls {
    background: rgba(255, 255, 255, .92);
    border-color: var(--border);
    box-shadow: 0 5px 18px rgba(0, 0, 0, .14);
}

.weather-shell.theme-light .radar-controls button {
    color: var(--text);
}

.weather-shell.theme-light .radar-controls button:hover,
.weather-shell.theme-light .radar-controls button:focus-visible {
    background: rgba(0, 0, 0, .08);
}

/* User menu theme fixes */
.weather-shell.theme-dark .user-menu-panel {
    background: rgba(24, 27, 32, .98);
    border-color: rgba(255, 255, 255, .10);
    box-shadow: 0 18px 48px rgba(0, 0, 0, .34);
}

.weather-shell.theme-light .user-menu-panel {
    background: rgba(255, 255, 255, .98);
    border-color: var(--border);
    box-shadow: 0 18px 48px rgba(0, 0, 0, .16);
}

.weather-shell.theme-light .user-menu-avatar {
    background: rgba(0, 0, 0, .06);
}

.weather-shell.theme-light .user-menu-profile strong,
.weather-shell.theme-light .user-preference-link strong {
    color: var(--text);
}

.weather-shell.theme-light .user-menu-profile small,
.weather-shell.theme-light .user-preference-link small {
    color: var(--muted);
}

.weather-shell.theme-light .user-menu-links {
    border-color: var(--border);
}

.weather-shell.theme-light .user-menu-links a:hover,
.weather-shell.theme-light .user-preference-link:hover,
.weather-shell.theme-light .user-preference-row:hover {
    background: rgba(0, 0, 0, .06);
}

.weather-shell.theme-light .theme-switch {
    background: rgba(0, 0, 0, .14);
}

/* Combined forecast tabs */
.card-hourlyForecast {
    overflow: hidden !important;
    padding-bottom: 12px !important;
}

@media (min-width: 1181px) {
    .card-hourlyForecast.card-size-large {
        grid-row: span 258 !important;
    }
}

.forecast-card-heading {
    align-items: flex-start;
    gap: 16px;
}

.forecast-heading-actions {
    align-items: center;
    display: flex;
    gap: 10px;
}

.forecast-tabs {
    align-items: center;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: inline-flex;
    padding: 3px;
}

.forecast-tab {
    background: transparent;
    border: 0;
    border-radius: 6px;
    color: var(--muted);
    cursor: pointer;
    font-size: var(--chart-text-min);
    font-weight: 700;
    min-height: 28px;
    padding: 0 11px;
}

.forecast-tab:hover,
.forecast-tab:focus-visible {
    color: var(--text);
    outline: none;
}

.forecast-tab.active {
    background: var(--surface);
    box-shadow: 0 1px 5px rgba(0, 0, 0, .16);
    color: var(--text);
}

.weather-shell.theme-dark .forecast-tab.active {
    background: var(--surface-3);
}

.forecast-panels {
    min-width: 0;
    width: 100%;
}

.forecast-panel {
    box-sizing: border-box;
    max-width: 100%;
    min-width: 0;
    width: 100%;
}

.forecast-panel[hidden] {
    display: none !important;
}

@media (max-width: 700px) {
    .forecast-card-heading {
        align-items: stretch;
        flex-direction: column;
    }

    .forecast-heading-actions {
        justify-content: space-between;
        width: 100%;
    }
}

/* Ensure inactive forecast tab is hidden */
#hourly-forecast-strip.forecast-panel[hidden],
#daily-forecast-strip.forecast-panel[hidden] {
    display: none !important;
}


/* Match hourly and daily forecast tile alignment */
#hourly-forecast-strip .hour-item,
#daily-forecast-strip .day-item {
    align-items: center !important;
    justify-content: center !important;
    padding: 10px 7px !important;
}

#hourly-forecast-strip .hour-item > span:first-child,
#daily-forecast-strip .day-item > span:first-child {
    margin: 0 0 3px !important;
}

#hourly-forecast-strip .hour-item .weather-svg,
#hourly-forecast-strip .hour-item img,
#daily-forecast-strip .day-item .weather-svg,
#daily-forecast-strip .day-item img {
    margin: 2px 0 !important;
}


/* Final unified forecast tile and scrollbar layout */
#hourly-forecast-strip,
#daily-forecast-strip {
    align-items: center !important;
    box-sizing: border-box !important;
    height: 218px !important;
    min-height: 218px !important;
    padding: 0 !important;
    overflow-x: scroll !important;
    overflow-y: hidden !important;
    scrollbar-gutter: stable !important;
    scrollbar-width: auto !important;
}

#hourly-forecast-strip .hour-item,
#daily-forecast-strip .day-item {
    align-items: center !important;
    box-sizing: border-box !important;
    flex: 0 0 150px !important;
    height: 150px !important;
    min-height: 150px !important;
    max-height: 150px !important;
    min-width: 150px !important;
    max-width: 150px !important;
    justify-content: flex-start !important;
    padding: 10px 7px !important;
}

#hourly-forecast-strip .hour-item > span:first-child,
#daily-forecast-strip .day-item > span:first-child {
    line-height: 1.15 !important;
    margin: 0 0 3px !important;
    min-height: 12px !important;
}

#hourly-forecast-strip .hour-item .weather-svg,
#hourly-forecast-strip .hour-item img,
#daily-forecast-strip .day-item .weather-svg,
#daily-forecast-strip .day-item img {
    flex: 0 0 auto !important;
    height: 43px !important;
    margin: 2px 0 !important;
    width: 43px !important;
}

#hourly-forecast-strip .hour-item strong,
#daily-forecast-strip .day-item strong {
    line-height: 1 !important;
    margin: 0 0 3px !important;
}

#hourly-forecast-strip .hour-item small,
#daily-forecast-strip .day-item small {
    line-height: 1.18 !important;
    margin: 0 !important;
    max-width: 100% !important;
    min-height: 0 !important;
    text-align: center !important;
}

#hourly-forecast-strip::-webkit-scrollbar,
#daily-forecast-strip::-webkit-scrollbar {
    height: 11px !important;
}

#hourly-forecast-strip::-webkit-scrollbar-track,
#daily-forecast-strip::-webkit-scrollbar-track {
    background: color-mix(in srgb, var(--border) 70%, transparent) !important;
    border-radius: 999px !important;
}

#hourly-forecast-strip::-webkit-scrollbar-thumb,
#daily-forecast-strip::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--muted) 82%, transparent) !important;
    background-clip: padding-box !important;
    border: 2px solid transparent !important;
    border-radius: 999px !important;
}

@media (max-width: 700px) {
    #hourly-forecast-strip .hour-item,
    #daily-forecast-strip .day-item {
        flex-basis: 132px !important;
        min-width: 132px !important;
        max-width: 132px !important;
    }
}

/* Prevent Forecast heading movement when switching tabs */
.card-hourlyForecast .forecast-card-heading {
    align-items: flex-start !important;
    flex: 0 0 34px !important;
    height: 34px !important;
    min-height: 34px !important;
    margin: 0 0 9px !important;
}

.card-hourlyForecast .forecast-card-heading > div:first-child {
    align-self: flex-start !important;
    height: 30px !important;
    min-height: 30px !important;
    margin: 0 !important;
    padding: 0 !important;
}

.card-hourlyForecast .forecast-card-heading .card-kicker {
    display: block !important;
    line-height: 1 !important;
    margin: 0 0 3px !important;
}

.card-hourlyForecast .forecast-card-heading h2 {
    line-height: 1 !important;
    margin: 0 !important;
}

.card-hourlyForecast .forecast-panels {
    height: 218px !important;
    min-height: 218px !important;
    margin: 0 !important;
    padding: 0 !important;
}

/* Lock hourly and daily forecast content to identical rows */
#hourly-forecast-strip .hour-item,
#daily-forecast-strip .day-item {
    display: grid !important;
    grid-template-rows: 16px 43px 18px 38px !important;
    align-content: start !important;
    align-items: center !important;
    justify-items: center !important;
    justify-content: normal !important;
    gap: 3px !important;
    padding: 10px 7px !important;
}

#hourly-forecast-strip .hour-item > span:first-child,
#daily-forecast-strip .day-item > span:first-child {
    align-self: center !important;
    display: block !important;
    height: 16px !important;
    line-height: 16px !important;
    margin: 0 !important;
    overflow: hidden !important;
    white-space: nowrap !important;
}

#hourly-forecast-strip .hour-item .weather-svg,
#hourly-forecast-strip .hour-item img,
#daily-forecast-strip .day-item .weather-svg,
#daily-forecast-strip .day-item img {
    align-self: center !important;
    height: 43px !important;
    margin: 0 !important;
    width: 43px !important;
}

#hourly-forecast-strip .hour-item strong,
#daily-forecast-strip .day-item strong {
    align-self: center !important;
    height: 18px !important;
    line-height: 18px !important;
    margin: 0 !important;
}

#hourly-forecast-strip .hour-item small,
#daily-forecast-strip .day-item small {
    align-self: start !important;
    display: -webkit-box !important;
    height: 38px !important;
    line-height: 1.18 !important;
    margin: 0 !important;
    overflow: hidden !important;
    text-align: center !important;
    white-space: normal !important;
    -webkit-box-orient: vertical !important;
    -webkit-line-clamp: 3 !important;
}

/* Rainfall history chart */
.card-rainfallHistory {
    overflow: hidden;
}

.rainfall-range-tabs button {
    cursor: pointer;
}

.rain-chart {
    bottom: 34px;
    left: 8px;
    position: absolute;
    right: 8px;
    top: 42px;
}

.rain-chart-axis {
    bottom: 22px;
    color: var(--muted);
    font-size: var(--chart-text-min);
    left: 0;
    position: absolute;
    top: 3px;
    width: 34px;
}

.rain-chart-axis span {
    position: absolute;
    right: 3px;
    transform: translateY(50%);
}

.rain-chart-axis span::after {
    border-top: 1px solid color-mix(in srgb, var(--border) 75%, transparent);
    content: "";
    left: 37px;
    position: absolute;
    top: 50%;
    width: calc(100vw);
}

.rain-chart-plot {
    align-items: stretch;
    bottom: 0;
    display: flex;
    gap: 2px;
    left: 37px;
    overflow: hidden;
    position: absolute;
    right: 0;
    top: 0;
}

.rain-chart-column {
    align-items: center;
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    min-width: 0;
    position: relative;
    z-index: 1;
}

.rain-chart-value {
    color: var(--text);
    font-size: var(--chart-text-min);
    height: 16px;
    line-height: 16px;
    overflow: hidden;
    text-align: center;
    text-overflow: clip;
    white-space: nowrap;
    width: 100%;
}

.rain-chart-bar-shell {
    align-items: flex-end;
    display: flex;
    flex: 1;
    min-height: 0;
    width: 72%;
}

.rain-chart-bar {
    background: var(--rain);
    border-radius: 2px 2px 0 0;
    box-shadow: 0 0 5px color-mix(in srgb, var(--rain) 22%, transparent);
    display: block;
    min-height: 0;
    opacity: .88;
    transition: height .2s ease;
    width: 100%;
}

.rain-chart-column:hover .rain-chart-bar {
    opacity: 1;
}

.rain-chart-label {
    color: var(--muted);
    font-size: var(--chart-text-min);
    height: 17px;
    line-height: 17px;

    /* As above: thinning leaves blank neighbours, so a kept label may spill
       rather than be cut off mid-word. */
    overflow: visible;
    text-align: center;
    white-space: nowrap;
    width: 100%;
}

.rain-chart-loading {
    align-items: center;
    color: var(--muted);
    display: flex;
    font-size: var(--chart-text-min);
    height: 100%;
    justify-content: center;
    width: 100%;
}

.card-rainfallHistory .rain-totals {
    bottom: 7px;
    gap: 12px;
    left: 38px;
    right: 8px;

    /* A medium card is half the width of a large one, and these three figures
       no longer shrink to fit. Wrapping beats overflowing off the card. */
    flex-wrap: wrap;
    row-gap: 2px;
}

.card-rainfallHistory .rain-totals span {
    white-space: nowrap;
}

@media (max-width: 700px) {
    .rain-chart-value {
        display: none;
    }

    .rain-chart-plot {
        gap: 1px;
    }

    .card-rainfallHistory .rain-totals {
        gap: 7px;
        left: 30px;
    }
}

/* Match rainfall history height to the forecast card. */
@media (min-width: 1181px) {
    .card-rainfallHistory.card-size-large {
        grid-row: span 258 !important;
    }
}


/* Temperature history chart */
@media (min-width: 1181px) {
    .card-temperatureHistory.card-size-large {
        grid-row: span 258 !important;
    }
}

.card-temperatureHistory {
    overflow: hidden;
}

.temperature-range-tabs button {
    cursor: pointer;
}

.temperature-chart {
    bottom: 34px;
    left: 8px;
    position: absolute;
    right: 8px;
    top: 42px;
}

.temperature-chart-axis {
    bottom: 24px;
    color: var(--muted);
    font-size: var(--chart-text-min);
    left: 0;
    position: absolute;
    top: 8px;
    width: 34px;
}

.temperature-chart-axis span {
    position: absolute;
    right: 3px;
    transform: translateY(50%);
}

.temperature-chart-svg {
    bottom: 20px;
    height: calc(100% - 20px);
    left: 0;
    overflow: visible;
    position: absolute;
    top: 0;
    width: 100%;
}

.temperature-grid-lines line {
    stroke: color-mix(in srgb, var(--border) 75%, transparent);
    stroke-width: 1;
}

.temperature-area {
    fill: color-mix(in srgb, var(--accent) 14%, transparent);
}

.temperature-line {
    fill: none;
    stroke: var(--accent);
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2.5;
}

.temperature-points circle {
    fill: var(--surface);
    stroke: var(--accent);
    stroke-width: 2;
}

.temperature-points circle:hover,
.temperature-points circle:focus {
    fill: var(--accent);
    outline: none;
}

.temperature-chart-labels {
    bottom: 0;
    display: flex;
    font-size: var(--chart-text-min);
    justify-content: space-between;
    left: 37px;
    position: absolute;
    right: 2px;
}

.temperature-chart-labels span {
    color: var(--muted);
    flex: 1 1 0;

    /* Every point keeps its slot whether or not it is labelled, so a slot is
       one twenty-fourth of the width on a day view - narrower than "12am" at
       twelve pixels, which is why labels were rendering as "12an" and "12pr".
       The neighbouring slots are blank by construction after thinning, so a
       label may spill into them. */
    overflow: visible;
    text-align: center;
    white-space: nowrap;
}

.temperature-chart-loading {
    align-items: center;
    color: var(--muted);
    display: flex;
    font-size: var(--chart-text-min);
    inset: 0;
    justify-content: center;
    position: absolute;
}

.temperature-totals {
    bottom: 7px;
    display: flex;
    gap: 12px;
    left: 38px;
    position: absolute;
    right: 8px;
}

.temperature-totals span {
    color: var(--muted);
    font-size: var(--chart-text-min);
    white-space: nowrap;
}

.temperature-totals strong {
    color: var(--text);
    margin-right: 3px;
}

@media (max-width: 700px) {
    .temperature-totals {
        gap: 7px;
        left: 30px;
    }
}

/* Feels-like temperature history line */
.feels-like-line {
    fill: none;
    stroke: var(--warning);
    stroke-dasharray: 7 5;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2.25;
}

.feels-like-points circle {
    fill: var(--surface);
    stroke: var(--warning);
    stroke-width: 2;
}

.feels-like-points circle:hover,
.feels-like-points circle:focus {
    fill: var(--warning);
    outline: none;
}

.temperature-legend-item {
    align-items: center;
    display: inline-flex;
    gap: 4px;
}

.temperature-legend-item i {
    border-radius: 999px;
    display: inline-block;
    height: 2px;
    width: 14px;
}

.temperature-legend-item.actual i {
    background: var(--accent);
}

.temperature-legend-item.feels i {
    background: repeating-linear-gradient(
        90deg,
        var(--warning) 0 5px,
        transparent 5px 8px);
}

/* Temperature point tooltip */
.temperature-tooltip {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .22);
    color: var(--text);
    display: grid;
    font-size: var(--chart-text-min);
    gap: 2px;
    min-width: 92px;
    padding: 6px 8px;
    pointer-events: none;
    position: absolute;
    transform: translate(-50%, calc(-100% - 10px));
    white-space: nowrap;
    z-index: 5;
}

.temperature-tooltip[hidden] {
    display: none;
}

.temperature-tooltip strong {
    font-size: var(--chart-text-min);
    font-weight: 600;
}

.temperature-tooltip span {
    color: var(--muted);
}

.temperature-points circle,
.feels-like-points circle {
    cursor: pointer;
}
/* BEGIN Semantic alert dialog system */
.weather-alert-dialog {
    --alert-tone: #6fa3d6;
}

.weather-alert-dialog.alert-tone-critical {
    --alert-tone: #8b2e1f;
}

.weather-alert-dialog.alert-tone-severe {
    --alert-tone: #d4635a;
}

.weather-alert-dialog.alert-tone-warning {
    --alert-tone: #d28b3e;
}

.weather-alert-dialog.alert-tone-advisory {
    --alert-tone: #e1b23d;
}

.weather-alert-dialog.alert-tone-informational {
    --alert-tone: #7b6fa7;
}

.weather-alert-dialog.alert-tone-resolved {
    --alert-tone: #4d8e6a;
}

.weather-alert-dialog.alert-tone-statement {
    --alert-tone: #6fa3d6;
}

.weather-alert-dialog.alert-tone-inactive {
    --alert-tone: #8b9097;
}

.weather-alert-dialog-panel {
    border-radius: 20px !important;
    border-top: 3px solid var(--alert-tone) !important;
    max-width: 820px !important;
    padding: 28px !important;
}

.weather-alert-dialog-close {
    background: color-mix(in srgb, var(--surface-2) 88%, var(--alert-tone) 12%) !important;
    border-color: color-mix(in srgb, var(--border) 72%, var(--alert-tone) 28%) !important;
    border-radius: 999px !important;
    cursor: pointer;
    transition:
        background 0.15s ease,
        border-color 0.15s ease,
        color 0.15s ease,
        transform 0.15s ease;
}

.weather-alert-dialog-close:hover {
    background: color-mix(in srgb, var(--surface-2) 74%, var(--alert-tone) 26%) !important;
    border-color: color-mix(in srgb, var(--border) 48%, var(--alert-tone) 52%) !important;
    color: var(--text);
    transform: scale(1.04);
}

.weather-alert-dialog-heading {
    border-bottom: 1px solid var(--border);
    padding-bottom: 18px;
}

.weather-alert-dialog-heading h2 {
    font-size: clamp(1.35rem, 2.2vw, 1.8rem) !important;
    line-height: 1.2;
    margin: 10px 0 7px !important;
}

.weather-alert-dialog-heading p {
    font-size: 0.76rem;
    margin: 0;
}

.weather-alert-dialog-severity {
    background: color-mix(in srgb, var(--surface-2) 78%, var(--alert-tone) 22%) !important;
    border: 1px solid color-mix(in srgb, var(--border) 70%, var(--alert-tone) 30%);
    color: color-mix(in srgb, var(--text) 78%, var(--alert-tone)) !important;
}

.weather-alert-dialog-body {
    gap: 0 !important;
    margin-top: 0 !important;
}

.weather-alert-dialog-body section {
    border-bottom: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
    padding: 18px 0;
}

.weather-alert-dialog-body section:last-child {
    border-bottom: 0;
}

.weather-alert-dialog-body h3 {
    color: color-mix(in srgb, var(--text) 86%, var(--alert-tone)) !important;
    margin: 0 0 8px !important;
}

.weather-alert-dialog-body p {
    font-size: 0.88rem;
    line-height: 1.65 !important;
    margin: 0;
}

.weather-alert-dialog-source {
    align-items: center;
    border: 1px solid color-mix(in srgb, var(--border) 75%, var(--alert-tone) 25%);
    border-radius: 10px;
    color: color-mix(in srgb, var(--text) 80%, var(--alert-tone));
    justify-content: center;
    padding: 10px 14px;
    text-decoration: none;
    transition:
        background 0.15s ease,
        border-color 0.15s ease;
}

.weather-alert-dialog-source:hover {
    background: color-mix(in srgb, var(--surface-2) 84%, var(--alert-tone) 16%);
    border-color: color-mix(in srgb, var(--border) 58%, var(--alert-tone) 42%);
}

@media (max-width: 700px) {
    .weather-alert-dialog-panel {
        border-radius: 16px !important;
        padding: 21px !important;
    }
}
/* END Semantic alert dialog system */

/* BEGIN Card Lab alert final presentation */

/*
 * Narrow Tall alert rows:
 * display source, title, and expiration only.
 * Full descriptions remain in the DOM for the detail dialog.
 */
.card-lab .card-alerts.card-size-narrowTall
[data-weather-alert-index] .alert-description,
.card-lab .card-alerts.card-size-narrowTall
[data-weather-alert-index] .alert-summary {
    display: none !important;
}

.card-lab .card-alerts.card-size-narrowTall
[data-weather-alert-index] .alert-expires,
.card-lab .card-alerts.card-size-narrowTall
[data-weather-alert-index] .alert-expiration {
    color: #9299a3 !important;
    display: block !important;
    font-size: 0.68rem !important;
    line-height: 1.2 !important;
    margin-top: 7px !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}

/* Console-generated test rows use three direct child blocks. */
.card-lab .card-alerts.card-size-narrowTall
[data-weather-alert-index] > div:nth-child(2) {
    display: none !important;
}

.card-lab .card-alerts.card-size-narrowTall
[data-weather-alert-index] > div:nth-child(3) {
    color: #9299a3 !important;
    display: block !important;
    font-size: 0.68rem !important;
    margin-top: 7px !important;
}

/* Native scrolling only. */
.card-lab .card-alerts.card-size-narrowTall .alerts-content {
    cursor: default !important;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    padding-bottom: 0 !important;
    scrollbar-gutter: auto !important;
    scrollbar-width: none !important;
    touch-action: pan-y !important;
    user-select: auto !important;
}

.card-lab .card-alerts.card-size-narrowTall
.alerts-content::-webkit-scrollbar {
    display: none !important;
    height: 0 !important;
    width: 0 !important;
}

.card-lab .card-alerts .alert-scroll-navigation,
.card-lab .card-alerts .alert-scroll-arrow {
    display: none !important;
}

.card-lab .card-alerts.card-size-narrowTall.alerts-can-scroll::after {
    display: none !important;
}

/* Alert status pill remains at the upper-right. */
.card-lab .card-alerts .card-title-row {
    align-items: center !important;
    display: flex !important;
    justify-content: space-between !important;
}

.card-lab .card-alerts .card-title-row #alert-count {
    margin-left: auto !important;
}

/*
 * Explicit dialog colors because the dialog is rendered outside
 * the themed weather-shell element.
 */
.weather-alert-dialog {
    --alert-dialog-background: #181b20;
    --alert-dialog-surface: #20242a;
    --alert-dialog-border: rgba(255, 255, 255, 0.11);
    --alert-dialog-text: #f3f5f7;
    --alert-dialog-muted: #aeb4bd;
}

.weather-alert-dialog-backdrop {
    background: rgba(4, 7, 11, 0.82) !important;
    backdrop-filter: blur(5px) !important;
    -webkit-backdrop-filter: blur(5px) !important;
}

.weather-alert-dialog-panel {
    background: var(--alert-dialog-background) !important;
    border: 1px solid var(--alert-dialog-border) !important;
    border-top: 3px solid var(--alert-tone) !important;
    box-shadow:
        0 28px 90px rgba(0, 0, 0, 0.62),
        0 0 0 1px rgba(255, 255, 255, 0.025) inset !important;
    color: var(--alert-dialog-text) !important;
    max-height: min(84vh, 760px) !important;
    max-width: 780px !important;
    padding: 28px !important;
}

.weather-alert-dialog-heading {
    border-bottom: 1px solid var(--alert-dialog-border) !important;
    padding-bottom: 18px !important;
}

.weather-alert-dialog-heading h2 {
    color: var(--alert-dialog-text) !important;
    font-size: clamp(1.45rem, 2.2vw, 1.85rem) !important;
    line-height: 1.15 !important;
    margin: 10px 0 8px !important;
}

.weather-alert-dialog-heading p {
    color: var(--alert-dialog-muted) !important;
    font-size: 0.76rem !important;
    line-height: 1.45 !important;
    margin: 0 !important;
}

.weather-alert-dialog-body {
    display: grid !important;
    gap: 0 !important;
    margin-top: 0 !important;
}

.weather-alert-dialog-body section {
    border-bottom: 1px solid var(--alert-dialog-border) !important;
    padding: 18px 0 !important;
}

.weather-alert-dialog-body section:last-child {
    border-bottom: 0 !important;
}

.weather-alert-dialog-body h3 {
    color: var(--alert-dialog-text) !important;
    font-size: 0.72rem !important;
    letter-spacing: 0.08em !important;
    margin: 0 0 8px !important;
    text-transform: uppercase !important;
}

.weather-alert-dialog-body p {
    color: var(--alert-dialog-muted) !important;
    font-size: 0.9rem !important;
    line-height: 1.65 !important;
    margin: 0 !important;
    white-space: pre-line !important;
}

.weather-alert-dialog-close {
    background: var(--alert-dialog-surface) !important;
    border: 1px solid var(--alert-dialog-border) !important;
    color: var(--alert-dialog-text) !important;
}

.weather-alert-dialog-severity {
    background: color-mix(
        in srgb,
        var(--alert-dialog-surface) 76%,
        var(--alert-tone) 24%
    ) !important;
    border: 1px solid color-mix(
        in srgb,
        var(--alert-dialog-border) 65%,
        var(--alert-tone) 35%
    ) !important;
    color: color-mix(
        in srgb,
        var(--alert-dialog-text) 76%,
        var(--alert-tone) 24%
    ) !important;
}

.weather-alert-dialog-source {
    align-items: center !important;
    background: var(--alert-dialog-surface) !important;
    border: 1px solid var(--alert-dialog-border) !important;
    border-radius: 10px !important;
    color: var(--alert-dialog-text) !important;
    display: inline-flex !important;
    justify-content: center !important;
    margin-top: 20px !important;
    padding: 10px 14px !important;
    text-decoration: none !important;
}

.weather-alert-dialog-source:hover {
    background: color-mix(
        in srgb,
        var(--alert-dialog-surface) 82%,
        var(--alert-tone) 18%
    ) !important;
}

/* END Card Lab alert final presentation */

/* BEGIN Alert dialog mockup layout */

.weather-alert-dialog {
    --alert-dialog-bg: #171a1f;
    --alert-dialog-surface: #20242b;
    --alert-dialog-surface-soft: #1c2026;
    --alert-dialog-border: rgba(255, 255, 255, 0.1);
    --alert-dialog-text: #f3f5f7;
    --alert-dialog-muted: #9da5af;
}

.weather-alert-dialog-panel {
    background: var(--alert-dialog-bg) !important;
    border: 1px solid var(--alert-dialog-border) !important;
    border-radius: 14px !important;
    border-top: 0 !important;
    color: var(--alert-dialog-text) !important;
    max-height: min(88vh, 860px) !important;
    max-width: 620px !important;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    padding: 0 28px 24px 34px !important;
    width: calc(100% - 32px) !important;
}

.weather-alert-dialog-rail {
    background: var(--alert-tone);
    bottom: 0;
    left: 0;
    position: absolute;
    top: 0;
    width: 6px;
}

.weather-alert-dialog-close {
    height: 32px !important;
    right: 16px !important;
    top: 16px !important;
    width: 32px !important;
}

.weather-alert-dialog-header {
    border-bottom: 1px solid var(--alert-dialog-border);
    padding: 26px 46px 20px 0;
}

.weather-alert-dialog-title-group {
    display: grid;
    gap: 8px;
}

.weather-alert-dialog-title-group h2 {
    color: var(--alert-dialog-text) !important;
    font-size: 1.55rem !important;
    line-height: 1.18 !important;
    margin: 0 !important;
}

.weather-alert-dialog-severity {
    justify-self: start;
}

.weather-alert-dialog-source-row {
    align-items: center;
    color: var(--alert-dialog-muted);
    display: flex;
    font-size: 0.72rem;
    gap: 8px;
}

.weather-alert-dialog-source-pill {
    border: 1px solid var(--alert-dialog-border);
    border-radius: 999px;
    color: var(--alert-dialog-text);
    font-size: 0.58rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    padding: 3px 7px;
}

.weather-alert-dialog-summary {
    background: var(--alert-dialog-surface-soft);
    border: 1px solid var(--alert-dialog-border);
    border-radius: 10px;
    display: grid;
    gap: 0;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    margin: 20px 0 4px;
}

.weather-alert-dialog-summary-item {
    border-left: 1px solid var(--alert-dialog-border);
    min-width: 0;
    padding: 13px 14px;
}

.weather-alert-dialog-summary-item:first-child {
    border-left: 0;
}

.weather-alert-dialog-summary-item span {
    color: var(--alert-dialog-muted);
    display: block;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    margin-bottom: 5px;
    text-transform: uppercase;
}

.weather-alert-dialog-summary-item strong {
    color: var(--alert-dialog-text);
    display: block;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.weather-alert-dialog-body {
    display: block !important;
}

.weather-alert-dialog-section {
    border-bottom: 1px solid var(--alert-dialog-border) !important;
    padding: 18px 0 !important;
}

.weather-alert-dialog-section h3 {
    color: var(--alert-dialog-text) !important;
    font-size: 0.66rem !important;
    font-weight: 800 !important;
    letter-spacing: 0.08em !important;
    margin: 0 0 8px !important;
    text-transform: uppercase !important;
}

.weather-alert-dialog-section p {
    color: var(--alert-dialog-muted) !important;
    font-size: 0.82rem !important;
    line-height: 1.6 !important;
    margin: 0 !important;
    white-space: pre-line !important;
}

.weather-alert-dialog-message p {
    color: var(--alert-dialog-text) !important;
    font-weight: 650;
}

.weather-alert-dialog-footer {
    align-items: center;
    display: flex;
    gap: 12px;
    justify-content: space-between;
    padding-top: 20px;
}

.weather-alert-dialog-source {
    background: var(--alert-dialog-surface) !important;
    border: 1px solid var(--alert-dialog-border) !important;
    border-radius: 8px !important;
    color: var(--alert-dialog-text) !important;
    font-size: 0.7rem !important;
    margin: 0 !important;
    padding: 9px 12px !important;
}

.weather-alert-dialog-id {
    color: var(--alert-dialog-muted);
    font-size: 0.6rem;
    max-width: 55%;
    overflow: hidden;
    text-align: right;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 640px) {
    .weather-alert-dialog-panel {
        padding: 0 20px 20px 26px !important;
    }

    .weather-alert-dialog-summary {
        grid-template-columns: 1fr;
    }

    .weather-alert-dialog-summary-item {
        border-left: 0;
        border-top: 1px solid var(--alert-dialog-border);
    }

    .weather-alert-dialog-summary-item:first-child {
        border-top: 0;
    }

    .weather-alert-dialog-footer {
        align-items: stretch;
        flex-direction: column;
    }

    .weather-alert-dialog-id {
        max-width: 100%;
        text-align: left;
    }
}

/* END Alert dialog mockup layout */

/* BEGIN Approved alert dialog mockup */

.weather-alert-dialog {
    --alert-tone: #d28b3e;
    --alert-dialog-background: rgba(13, 21, 30, 0.97);
    --alert-dialog-border: rgba(150, 171, 194, 0.42);
    --alert-dialog-text: #f3f5f7;
    --alert-dialog-muted: #aeb7c2;
    --alert-dialog-rule: rgba(160, 178, 197, 0.17);
}

.weather-alert-dialog-backdrop {
    background: rgba(3, 8, 13, 0.78) !important;
    backdrop-filter: blur(5px) !important;
    -webkit-backdrop-filter: blur(5px) !important;
}

.weather-alert-dialog-panel {
    background:
        linear-gradient(
            145deg,
            rgba(18, 29, 40, 0.98),
            rgba(10, 18, 27, 0.98)
        ) !important;
    border: 1px solid var(--alert-dialog-border) !important;
    border-radius: 15px !important;
    border-top: 1px solid var(--alert-dialog-border) !important;
    box-shadow:
        0 28px 90px rgba(0, 0, 0, 0.58),
        0 0 0 1px rgba(255, 255, 255, 0.02) inset !important;
    color: var(--alert-dialog-text) !important;
    max-height: min(86vh, 790px) !important;
    max-width: 665px !important;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    padding: 28px 30px 20px !important;
    width: calc(100% - 36px) !important;
}

.weather-alert-dialog-accent {
    background: var(--alert-tone);
    border-radius: 999px;
    height: 87px;
    left: 25px;
    position: absolute;
    top: 18px;
    width: 5px;
}

.weather-alert-dialog-close {
    background: rgba(255, 255, 255, 0.055) !important;
    border: 1px solid rgba(255, 255, 255, 0.13) !important;
    border-radius: 999px !important;
    color: var(--alert-dialog-text) !important;
    height: 42px !important;
    right: 18px !important;
    top: 17px !important;
    width: 42px !important;
}

.weather-alert-dialog-close svg {
    height: 18px !important;
    width: 18px !important;
}

.weather-alert-dialog-header {
    border-bottom: 1px solid var(--alert-dialog-rule);
    margin-left: 22px;
    padding: 0 54px 20px 0;
}

.weather-alert-dialog-header h2 {
    color: var(--alert-dialog-text) !important;
    font-size: 1.9rem !important;
    font-weight: 750 !important;
    letter-spacing: -0.025em !important;
    line-height: 1.1 !important;
    margin: 0 0 9px !important;
}

.weather-alert-dialog-subtitle {
    align-items: center;
    color: var(--alert-dialog-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: 0.82rem;
    gap: 5px;
}

.weather-alert-dialog-subtitle strong {
    color: var(--alert-tone);
    font-weight: 700;
}

.weather-alert-dialog-dot {
    color: var(--alert-dialog-muted);
    margin: 0 7px;
}

.weather-alert-dialog-summary {
    border-bottom: 1px solid var(--alert-dialog-rule);
    display: grid;
    grid-template-columns: 1fr 1fr 1.2fr;
    margin: 0;
    padding: 20px 0 17px;
}

.weather-alert-dialog-summary-item {
    border-left: 1px solid var(--alert-dialog-rule);
    min-width: 0;
    padding: 0 18px;
}

.weather-alert-dialog-summary-item:first-child {
    border-left: 0;
    padding-left: 0;
}

.weather-alert-dialog-summary-item:last-child {
    padding-right: 0;
}

.weather-alert-dialog-summary-item span {
    color: var(--alert-dialog-muted);
    display: block;
    font-size: 0.61rem;
    font-weight: 750;
    letter-spacing: 0.12em;
    margin-bottom: 7px;
    text-transform: uppercase;
}

.weather-alert-dialog-summary-item strong {
    color: var(--alert-dialog-text);
    display: block;
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.weather-alert-dialog-body {
    display: block !important;
    margin: 0 !important;
}

.weather-alert-dialog-body section {
    border-bottom: 0 !important;
    padding: 13px 0 3px !important;
}

.weather-alert-dialog-body h3 {
    color: #d6dbe1 !important;
    font-size: 0.61rem !important;
    font-weight: 750 !important;
    letter-spacing: 0.12em !important;
    margin: 0 0 6px !important;
    text-transform: uppercase !important;
}

.weather-alert-dialog-body p {
    color: var(--alert-dialog-muted) !important;
    font-size: 0.81rem !important;
    line-height: 1.5 !important;
    margin: 0 !important;
    white-space: pre-line !important;
}

#weather-alert-dialog-headline {
    color: var(--alert-dialog-text) !important;
}

.weather-alert-dialog-footer {
    align-items: center;
    border-top: 1px solid var(--alert-dialog-rule);
    color: var(--alert-dialog-muted);
    display: flex;
    font-size: 0.68rem;
    gap: 14px;
    justify-content: space-between;
    margin-top: 17px;
    padding-top: 14px;
}

.weather-alert-dialog-footer a {
    color: #54a7f4 !important;
    font-weight: 650;
    text-decoration: none;
}

.weather-alert-dialog-footer a:hover {
    text-decoration: underline;
}

#weather-alert-dialog-id {
    color: rgba(174, 183, 194, 0.72);
    max-width: 48%;
    overflow: hidden;
    text-align: right;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.weather-alert-dialog.alert-tone-critical {
    --alert-tone: #ff2b22;
}

.weather-alert-dialog.alert-tone-severe {
    --alert-tone: #ff7b22;
}

.weather-alert-dialog.alert-tone-warning {
    --alert-tone: #f59f17;
}

.weather-alert-dialog.alert-tone-advisory {
    --alert-tone: #a66df2;
}

/* PURPLE IS INFORMATIONAL, in the platform's semantic mapping:

     blue    primary / action / active
     green   success
     amber   warning / caution
     red     destructive / severe
     purple  informational / help / guidance

   This variant was blue, which made "informational" indistinguishable from
   "statement" below it and left guidance popups reading as primary actions.
   #a66df2 is the platform's existing purple, not a new colour. */
.weather-alert-dialog.alert-tone-informational {
    --alert-tone: #a66df2;
}

.weather-alert-dialog.alert-tone-resolved {
    --alert-tone: #55b27c;
}

.weather-alert-dialog.alert-tone-statement {
    --alert-tone: #5ba9f3;
}

.weather-alert-dialog.alert-tone-inactive {
    --alert-tone: #858d97;
}

@media (max-width: 700px) {
    .weather-alert-dialog-panel {
        padding: 24px 22px 18px !important;
    }

    .weather-alert-dialog-accent {
        left: 17px;
    }

    .weather-alert-dialog-header {
        margin-left: 16px;
    }

    .weather-alert-dialog-summary {
        grid-template-columns: 1fr;
    }

    .weather-alert-dialog-summary-item {
        border-left: 0;
        border-top: 1px solid var(--alert-dialog-rule);
        padding: 12px 0;
    }

    .weather-alert-dialog-summary-item:first-child {
        border-top: 0;
    }

    .weather-alert-dialog-footer {
        align-items: flex-start;
        flex-direction: column;
    }

    #weather-alert-dialog-id {
        max-width: 100%;
        text-align: left;
    }
}

/* END Approved alert dialog mockup */

/* BEGIN Exact alert mockup correction */

.weather-alert-dialog-panel {
    background:
        linear-gradient(
            145deg,
            rgba(13, 24, 35, 0.985),
            rgba(8, 17, 27, 0.985)
        ) !important;
    border: 1px solid rgba(129, 151, 174, 0.5) !important;
    border-radius: 15px !important;
    box-shadow:
        0 30px 90px rgba(0, 0, 0, 0.62),
        0 0 0 1px rgba(255, 255, 255, 0.018) inset !important;
    max-height: min(88vh, 790px) !important;
    max-width: 665px !important;
    padding: 26px 29px 18px !important;
    width: calc(100% - 42px) !important;
}

.weather-alert-dialog-accent {
    height: 86px !important;
    left: 25px !important;
    top: 18px !important;
    width: 5px !important;
}

.weather-alert-dialog-header {
    margin-left: 22px !important;
    padding: 0 55px 18px 0 !important;
}

.weather-alert-dialog-header h2 {
    font-size: 1.82rem !important;
    font-weight: 720 !important;
    letter-spacing: -0.025em !important;
    line-height: 1.08 !important;
    margin: 0 0 9px !important;
}

.weather-alert-dialog-subtitle {
    font-size: 0.79rem !important;
    gap: 5px !important;
}

.weather-alert-dialog-close {
    height: 42px !important;
    right: 17px !important;
    top: 17px !important;
    width: 42px !important;
}

/* Remove the rounded summary box left over from earlier styling. */
.weather-alert-dialog-summary {
    background: transparent !important;
    border: 0 !important;
    border-bottom: 1px solid rgba(160, 178, 197, 0.17) !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    display: grid !important;
    grid-template-columns: 1fr 1fr 1.18fr !important;
    margin: 0 !important;
    padding: 18px 0 15px !important;
}

.weather-alert-dialog-summary-item {
    background: transparent !important;
    border: 0 !important;
    border-left: 1px solid rgba(160, 178, 197, 0.17) !important;
    border-radius: 0 !important;
    padding: 0 18px !important;
}

.weather-alert-dialog-summary-item:first-child {
    border-left: 0 !important;
    padding-left: 0 !important;
}

.weather-alert-dialog-summary-item:last-child {
    padding-right: 0 !important;
}

.weather-alert-dialog-summary-item span {
    font-size: 0.59rem !important;
    letter-spacing: 0.11em !important;
    margin-bottom: 6px !important;
}

.weather-alert-dialog-summary-item strong {
    font-size: 0.76rem !important;
    font-weight: 450 !important;
    line-height: 1.35 !important;
}

.weather-alert-dialog-body section {
    border: 0 !important;
    padding: 12px 0 2px !important;
}

.weather-alert-dialog-body h3 {
    font-size: 0.6rem !important;
    font-weight: 740 !important;
    letter-spacing: 0.11em !important;
    margin: 0 0 6px !important;
}

.weather-alert-dialog-body p {
    color: #aab3be !important;
    font-size: 0.79rem !important;
    font-weight: 400 !important;
    line-height: 1.5 !important;
}

#weather-alert-dialog-headline {
    color: #dce2e8 !important;
    font-weight: 400 !important;
}

.weather-alert-dialog-footer {
    border-top: 1px solid rgba(160, 178, 197, 0.17) !important;
    font-size: 0.66rem !important;
    margin-top: 15px !important;
    padding-top: 13px !important;
}

#weather-alert-dialog-id {
    font-size: 0.64rem !important;
}

/* END Exact alert mockup correction */

/* BEGIN Theme-aware alert dialog */

.weather-alert-dialog {
    --alert-dialog-rule:
        color-mix(in srgb, var(--border) 78%, transparent);
}

.weather-alert-dialog-panel {
    background:
        linear-gradient(
            145deg,
            color-mix(in srgb, var(--surface) 98%, var(--surface-2) 2%),
            color-mix(in srgb, var(--surface) 94%, var(--surface-2) 6%)
        ) !important;
    border-color:
        color-mix(in srgb, var(--border) 84%, var(--text) 16%) !important;
    color: var(--text) !important;
}

.weather-alert-dialog-header {
    border-bottom-color: var(--alert-dialog-rule) !important;
}

.weather-alert-dialog-header h2,
.weather-alert-dialog-summary-item strong,
#weather-alert-dialog-headline {
    color: var(--text) !important;
}

.weather-alert-dialog-subtitle,
.weather-alert-dialog-summary-item span,
.weather-alert-dialog-body p,
.weather-alert-dialog-footer,
#weather-alert-dialog-id {
    color: var(--muted) !important;
}

.weather-alert-dialog-summary {
    background: transparent !important;
    border-bottom-color: var(--alert-dialog-rule) !important;
}

.weather-alert-dialog-summary-item {
    border-left-color: var(--alert-dialog-rule) !important;
}

.weather-alert-dialog-body h3 {
    color:
        color-mix(in srgb, var(--text) 84%, var(--muted) 16%) !important;
}

.weather-alert-dialog-close {
    background: var(--surface-2) !important;
    border-color:
        color-mix(in srgb, var(--border) 84%, var(--text) 16%) !important;
    color: var(--text) !important;
}

.weather-alert-dialog-close:hover {
    background:
        color-mix(
            in srgb,
            var(--surface-2) 82%,
            var(--alert-tone) 18%
        ) !important;
}

.weather-alert-dialog-footer {
    border-top-color: var(--alert-dialog-rule) !important;
}

.weather-alert-dialog-footer a {
    color: var(--accent) !important;
}

.weather-alert-dialog-backdrop {
    background:
        color-mix(in srgb, var(--bg) 78%, transparent) !important;
}

/* END Theme-aware alert dialog */

/* BEGIN Popup colors matching event list */
.weather-alert-dialog.alert-tone-critical {
    --alert-tone: #ef6e7f !important;
}

.weather-alert-dialog.alert-tone-severe {
    --alert-tone: #f08a50 !important;
}

.weather-alert-dialog.alert-tone-warning {
    --alert-tone: var(--warning) !important;
}

.weather-alert-dialog.alert-tone-advisory {
    --alert-tone: var(--accent) !important;
}
/* END Popup colors matching event list */

/* BEGIN Modern forecast heading and hidden scrolling */

/* Match the shared dashboard card-label treatment. */
.card-hourlyForecast .forecast-title {
    color: var(--muted) !important;
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
    font-size: 0.7rem !important;
    font-weight: 600 !important;
    letter-spacing: 0.08em !important;
    line-height: 1.05 !important;
    margin: 0 !important;
    text-transform: uppercase !important;
}

/* The forecast heading now contains one label instead of two stacked labels. */
.card-hourlyForecast .forecast-card-heading > div:first-child {
    align-items: center !important;
    display: flex !important;
    height: 30px !important;
    min-height: 30px !important;
}

/* Preserve horizontal scrolling without displaying a traditional scrollbar. */
#hourly-forecast-strip,
#daily-forecast-strip {
    cursor: grab;
    overflow-x: auto !important;
    padding-bottom: 0 !important;
    scrollbar-color: transparent transparent !important;
    scrollbar-width: none !important;
    touch-action: pan-x pan-y;
    -webkit-overflow-scrolling: touch;
}

#hourly-forecast-strip:active,
#daily-forecast-strip:active {
    cursor: grabbing;
}

#hourly-forecast-strip::-webkit-scrollbar,
#daily-forecast-strip::-webkit-scrollbar {
    display: none !important;
    height: 0 !important;
    width: 0 !important;
}

/* END Modern forecast heading and hidden scrolling */

/* BEGIN Global collapsible sidebar */

.weather-shell,
.sidebar {
    transition:
        grid-template-columns 200ms ease,
        width 200ms ease;
}

.brand {
    min-width: 0;
    position: relative;
}

.brand-copy {
    min-width: 0;
    overflow: hidden;
    transition:
        opacity 130ms ease,
        width 180ms ease;
    white-space: nowrap;
}

.sidebar-collapse-toggle {
    align-items: center;
    appearance: none;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--muted);
    cursor: pointer;
    display: inline-flex;
    flex: 0 0 36px;
    height: 36px;
    justify-content: center;
    margin-left: auto;
    padding: 0;
    transition:
        background 150ms ease,
        border-color 150ms ease,
        color 150ms ease,
        transform 200ms ease;
    width: 36px;
}

.sidebar-collapse-toggle:hover,
.sidebar-collapse-toggle:focus-visible {
    background: var(--surface-3);
    border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
    color: var(--text);
    outline: none;
}

.sidebar-collapse-toggle svg {
    fill: none;
    height: 18px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2;
    width: 18px;
}

.nav-icon {
    align-items: center;
    display: inline-flex;
    flex: 0 0 20px;
    font-size: 1rem;
    justify-content: center;
    line-height: 1;
    width: 20px;
}

.nav-label {
    overflow: hidden;
    white-space: nowrap;
}

@media (min-width: 641px) {
    .weather-shell.sidebar-collapsed {
        grid-template-columns: 68px minmax(0, 1fr);
    }

    .weather-shell.sidebar-collapsed .sidebar {
        padding-left: 8px;
        padding-right: 8px;
    }

    .weather-shell.sidebar-collapsed .brand {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
    }

    .weather-shell.sidebar-collapsed .brand-mark {
        display: none;
    }

    .weather-shell.sidebar-collapsed .brand-copy,
    .weather-shell.sidebar-collapsed .nav-label,
    .weather-shell.sidebar-collapsed .station-mini > div {
        display: none;
    }

    .weather-shell.sidebar-collapsed .sidebar-collapse-toggle {
        margin-left: 0;
        transform: rotate(180deg);
    }

    .weather-shell.sidebar-collapsed .nav-item {
        gap: 0;
        justify-content: center;
        min-height: 42px;
        padding-left: 0;
        padding-right: 0;
    }

    .weather-shell.sidebar-collapsed .station-mini {
        justify-content: center;
        min-height: 42px;
        padding-left: 0;
        padding-right: 0;
    }

    .weather-shell.sidebar-collapsed .status-dot {
        flex: 0 0 auto;
    }
}

/* The collapse control follows the rail: hidden only where the rail itself is, which is now
   the phone breakpoint rather than 900. */
@media (max-width: 640px) {
    .sidebar-collapse-toggle {
        display: none;
    }

    .brand-copy {
        opacity: 1;
        width: auto;
    }
}

/* END Global collapsible sidebar */

/* BEGIN Sidebar edge toggle */

/*
 * Keep the product mark at the top while positioning the collapse control
 * halfway down the sidebar's outside edge.
 */
@media (min-width: 641px) {
    .sidebar {
        overflow: visible;
        position: sticky;
    }

    /*
     * The brand previously established a positioning context for the button.
     * Reset it so the absolute control is positioned against the sidebar.
     */
    .sidebar .brand {
        position: static;
    }

    .sidebar .sidebar-collapse-toggle {
        border-radius: 9px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, .18);
        margin: 0;
        position: absolute;
        right: -18px;
        top: 50%;
        transform: translateY(-50%);
        z-index: 30;
    }

    .sidebar .sidebar-collapse-toggle:hover,
    .sidebar .sidebar-collapse-toggle:focus-visible {
        transform: translateY(-50%) scale(1.04);
    }

    /*
     * Preserve the W logo in the collapsed rail.
     */
    .weather-shell.sidebar-collapsed .brand-mark {
        display: flex;
    }

    .weather-shell.sidebar-collapsed .brand {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
    }

    .weather-shell.sidebar-collapsed .sidebar-collapse-toggle {
        margin: 0;
        transform: translateY(-50%) rotate(180deg);
    }

    .weather-shell.sidebar-collapsed .sidebar-collapse-toggle:hover,
    .weather-shell.sidebar-collapsed .sidebar-collapse-toggle:focus-visible {
        transform: translateY(-50%) rotate(180deg) scale(1.04);
    }
}

/* END Sidebar edge toggle */

/* BEGIN Unified sidebar outline icons */

.nav-icon svg {
    fill: none;
    height: 20px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
    width: 20px;
}

.nav-item {
    cursor: pointer;
}

.weather-shell.sidebar-collapsed .nav-icon {
    height: 22px;
    width: 22px;
}

.weather-shell.sidebar-collapsed .nav-icon svg {
    height: 20px;
    width: 20px;
}

/* END Unified sidebar outline icons */

/* BEGIN Sidebar spacing and hover match */

/* Give each navigation control clearer separation. */
.nav-list {
    gap: 9px;
}

/*
 * Match the top-right header controls:
 * bordered surface at rest, accent border on hover/focus.
 */
.nav-item {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    min-height: 38px;
    transition:
        border-color 150ms ease,
        color 150ms ease,
        background 150ms ease;
}

.nav-item:hover,
.nav-item:focus-visible {
    background: var(--surface);
    border-color: var(--accent);
    color: var(--text);
    outline: none;
}

/* Preserve the selected Dashboard treatment. */
.nav-item.active {
    background: var(--accent-soft);
    border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
    color: var(--accent);
}

.nav-item.active:hover,
.nav-item.active:focus-visible {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

@media (min-width: 641px) {
    .weather-shell.sidebar-collapsed .nav-list {
        gap: 10px;
    }

    .weather-shell.sidebar-collapsed .nav-item {
        height: 40px;
        min-height: 40px;
    }
}

/* END Sidebar spacing and hover match */

/* BEGIN Sidebar chevron alignment correction */

@media (min-width: 641px) {
    /*
     * Position the control over the horizontal break between the adjacent
     * dashboard card rows instead of the exact center of the full sidebar.
     */
    .sidebar .sidebar-collapse-toggle {
        right: -9px;
        top: calc(50% + 30px);
    }
}

/* END Sidebar chevron alignment correction */

/* BEGIN Sidebar navigation card alignment */

@media (min-width: 641px) {
    /*
     * Align the top of the Home navigation tile with the top of the
     * first dashboard card while preserving the W logo position.
     */
    .sidebar .nav-list {
        margin-top: 20px;
    }
}

/* END Sidebar navigation card alignment */

/* BEGIN Collapsed sidebar square buttons */

@media (min-width: 641px) {
    .weather-shell.sidebar-collapsed .nav-list {
        justify-items: center;
        width: 100%;
    }

    .weather-shell.sidebar-collapsed .nav-item {
        height: 40px;
        min-height: 40px;
        padding: 0;
        width: 40px;
    }
}

/* END Collapsed sidebar square buttons */
/* History card responsive typography and spacing */
.card-temperatureHistory,
.card-rainfallHistory,
.card-temperatureHistory button,
.card-rainfallHistory button {
    font-family: inherit !important;
}

.card-temperatureHistory .card-kicker,
.card-rainfallHistory .card-kicker {
    font-family: inherit !important;
    font-size: var(--type-kicker) !important;
    font-weight: 800 !important;
    letter-spacing: .09em !important;
    line-height: 1.1 !important;
}

.card-temperatureHistory .card-title-row h2,
.card-rainfallHistory .card-title-row h2 {
    font-family: inherit !important;
    font-size: var(--type-card-title) !important;
    font-weight: 700 !important;
    line-height: 1.05 !important;
    margin: 2px 0 0 !important;
}

.card-temperatureHistory .card-title-row,
.card-rainfallHistory .card-title-row {
    min-height: 34px;
}

.card-temperatureHistory .temperature-chart,
.card-rainfallHistory .rain-chart {
    top: 50px;
}

.card-temperatureHistory .temperature-chart-axis,
.card-temperatureHistory .temperature-chart-labels,
.card-temperatureHistory .temperature-totals,
.card-rainfallHistory .rain-chart-axis,
.card-rainfallHistory .rain-chart-label,
.card-rainfallHistory .rain-chart-value,
.card-rainfallHistory .rain-totals {
    font-family: inherit !important;
}

.card-temperatureHistory.card-size-medium .temperature-totals,
.card-rainfallHistory.card-size-medium .rain-totals {
    align-items: center;
    gap: 8px;
    overflow: hidden;
}

.card-rainfallHistory .rain-chart-value:empty {
    visibility: hidden;
}

@media (max-width: 700px) {
    .card-temperatureHistory .card-title-row,
    .card-rainfallHistory .card-title-row {
        min-height: 31px;
    }

    .card-temperatureHistory .temperature-chart,
    .card-rainfallHistory .rain-chart {
        top: 47px;
    }
}
/* Rainfall hover and keyboard-focus tooltip */
.rain-chart-column[data-rainfall-column] {
    cursor: default;
}

.rain-chart-column[data-rainfall-column]::after {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .22);
    color: var(--text);
    content: attr(data-rainfall-label) "  " attr(data-rainfall-value);
    font-family: inherit;
    font-size: var(--chart-text-min);
    font-weight: 600;
    left: 50%;
    min-width: max-content;
    opacity: 0;
    padding: 6px 8px;
    pointer-events: none;
    position: absolute;
    top: 8px;
    transform: translate(-50%, -6px);
    transition: opacity .12s ease, transform .12s ease;
    white-space: nowrap;
    z-index: 5;
}

.rain-chart-column[data-rainfall-column]:hover::after,
.rain-chart-column[data-rainfall-column]:focus-visible::after {
    opacity: 1;
    transform: translate(-50%, 0);
}

.rain-chart-column[data-rainfall-column]:focus-visible {
    outline: none;
}

.rain-chart-value {
    display: none !important;
}

/* BEGIN Forecast mobile heading cleanup */
@media (max-width: 640px) {
    .card-hourlyForecast .forecast-card-heading {
        align-items: center !important;
        display: flex !important;
        flex: 0 0 34px !important;
        flex-direction: row !important;
        gap: 8px !important;
        height: 34px !important;
        justify-content: space-between !important;
        min-height: 34px !important;
        margin: 0 0 6px !important;
    }

    .card-hourlyForecast .forecast-card-heading > div:first-child {
        align-items: center !important;
        display: flex !important;
        flex: 0 1 auto !important;
        height: auto !important;
        min-height: 0 !important;
        min-width: 0 !important;
    }

    .card-hourlyForecast .forecast-title {
        font-size: 0.78rem !important;
        line-height: 1 !important;
        margin: 0 !important;
        white-space: nowrap !important;
    }

    .card-hourlyForecast .forecast-heading-actions {
        align-items: center !important;
        display: flex !important;
        flex: 0 0 auto !important;
        gap: 6px !important;
        justify-content: flex-end !important;
        min-height: 30px !important;
        width: auto !important;
    }

    .card-hourlyForecast .forecast-tabs {
        display: inline-flex !important;
        flex: 0 0 auto !important;
        padding: 2px !important;
    }

    .card-hourlyForecast .forecast-tab {
        min-height: 26px !important;
        padding: 0 9px !important;
    }

    .card-hourlyForecast .forecast-heading-actions > .pill {
        display: inline-flex !important;
        flex: 0 0 auto !important;
    }

    .card-hourlyForecast .forecast-panels {
        margin-top: 0 !important;
    }
}
/* END Forecast mobile heading cleanup */
/* The desktop bar is not the phone's bar.

   What was here forced .topbar to display:block !important at this width, gave it a
   68px min-height and absolutely positioned .header-actions into its corner - the old
   approach of reshaping the desktop header until it fitted a phone.

   AppMobileBar is the phone's chrome now, so this bar simply hides. The !important was
   also actively in the way: it beat AppTopBar's own scoped rule, so the desktop header
   was still rendering at phone width on Reports - only the Dashboard escaped it, and
   only because a second !important rule qualified with .card-lab hid it there. */



/* Phone shell clearance.

   What was here: a .card-lab-qualified copy of the mobile header's styling - ten rules,
   nine of them declaration-for-declaration identical to AppTopBar's own scoped styling,
   the tenth restating stroke properties CaelowxIcon already sets and inherits. All of it
   is gone rather than moved, because the classes it styled no longer exist: the phone's
   header is AppMobileBar and the phone's navigation is AppBottomBar.

   Also gone: the rules that hid the sidebar's brand and collapsed its footprint at this
   width, and the one that hid the desktop topbar with !important. The sidebar stands down
   entirely below 640 now and AppTopBar hides itself, so all three were describing work
   already done elsewhere.

   What remains is the one thing that is genuinely the shell's: reserving room so a fixed
   bar does not sit on top of the last thing on the page. Unqualified, because every
   surface has the bar - and derived from the bar's own height token, so the two cannot
   drift apart. */
@media (max-width: 640px) {
    .weather-shell {
        /* ONE COLUMN, BECAUSE THE RAIL IS GONE.

           The shell keeps its 176px first column from the desktop rule, and below this
           breakpoint `.sidebar` is `display: none` - so it stops being a grid item and `main`
           inherits the narrow column instead. Retiring the old top-nav block removed the
           `display: block` that used to hide this, so the collapse to a single column has to be
           stated here, where the rail actually stops. */
        grid-template-columns: minmax(0, 1fr);

        padding-bottom: var(--cx-bottom-bar-clearance);
    }

    /* No top gutter above the phone's own bar, on every surface. The Dashboard had this
       as a .card-lab-qualified !important; unqualified is what makes the bar start at the
       same y on Stations, Reports and Account too, which is the parity that was missing. */
    .dashboard-main {
        padding-top: 0;
    }

    /* Dashboard-specific and deliberately untouched: its own bottom rhythm inside the
       reserved space. */
    .card-lab .dashboard-main {
        padding-bottom: 12px !important;
        padding-top: 0 !important;
    }
}




/* Forecast tile size test - 200px */
#hourly-forecast-strip .hour-item,
#daily-forecast-strip .day-item {
    flex: 0 0 180px !important;
    height: 180px !important;
    min-height: 180px !important;
    max-height: 180px !important;
    min-width: 180px !important;
    max-width: 180px !important;
    padding: 14px !important;
}


/* Forecast tile vertical alignment */
#hourly-forecast-strip,
#daily-forecast-strip {
    transform: translateY(-18px);
}

/* Forecast tile redesign */
#hourly-forecast-strip .hour-item {
    display: flex !important;
    flex-direction: column !important;
    justify-content: space-between !important;
    align-items: center !important;
    padding: 12px !important;
}

#hourly-forecast-strip .forecast-time {
    align-self: flex-start !important;
    font-size: 13px !important;
}

#hourly-forecast-strip .forecast-icon .weather-svg {
    width: 55px !important;
    height: 55px !important;
}

#hourly-forecast-strip .forecast-temperature {
    font-size: 28px !important;
    line-height: 1 !important;
}

#hourly-forecast-strip .forecast-details {
    display: flex !important;
    justify-content: space-between !important;
    width: 100% !important;
    font-size: 13px !important;
}

#hourly-forecast-strip .forecast-wind,
#hourly-forecast-strip .forecast-rain-chance {
    font-size: 13px !important;
}

/* Forecast tile alignment refinement */
#hourly-forecast-strip .hour-item .forecast-time {
    align-self: flex-start !important;
    text-align: left !important;
}

#hourly-forecast-strip .forecast-details {
    margin-top: auto !important;
}

/* Forecast tile polish */
#hourly-forecast-strip .forecast-icon .weather-svg {
    width: 60px !important;
    height: 60px !important;
}

#hourly-forecast-strip .forecast-details {
    margin-bottom: 8px !important;
}

/* Forecast bottom row refinement */
#hourly-forecast-strip .forecast-details {
    height: 28px !important;
    align-items: center !important;
    margin-bottom: 12px !important;
}

/* Forecast footer alignment */
#hourly-forecast-strip .forecast-details {
    display: flex !important;
    align-items: center !important;
    justify-content: space-between !important;
    height: 24px !important;
    margin-bottom: 12px !important;
}

#hourly-forecast-strip .forecast-wind,
#hourly-forecast-strip .forecast-rain-chance {
    display: flex !important;
    align-items: center !important;
    height: 24px !important;
}

/* Forecast time alignment */
#hourly-forecast-strip .forecast-time {
    align-self: flex-start !important;
    width: 100% !important;
    text-align: left !important;
}

/* Daily forecast matching hourly tile layout */
#daily-forecast-strip .day-item {
    display: flex !important;
    flex-direction: column !important;
    justify-content: space-between !important;
    align-items: center !important;
    padding: 12px !important;
}

#daily-forecast-strip .forecast-time {
    align-self: flex-start !important;
    width: 100% !important;
    text-align: left !important;
    font-size: 13px !important;
}

#daily-forecast-strip .forecast-icon .weather-svg {
    width: 60px !important;
    height: 60px !important;
}

#daily-forecast-strip .forecast-temperature {
    font-size: 28px !important;
    line-height: 1 !important;
}

#daily-forecast-strip .forecast-summary {
    font-size: 13px !important;
    line-height: 1.2 !important;
    text-align: center !important;
}

/* Daily forecast summary readability */
#daily-forecast-strip .forecast-summary {
    font-size: 12px !important;
    line-height: 1.25 !important;
    text-align: center !important;
    max-height: 32px;
    overflow: hidden;
}

/* Daily forecast footer alignment */
#daily-forecast-strip .forecast-details {
    display: flex !important;
    justify-content: space-between !important;
    align-items: center !important;
    width: 100% !important;
}

#daily-forecast-strip .forecast-wind,
#daily-forecast-strip .forecast-rain-chance {
    display: flex !important;
    align-items: center !important;
    height: 24px !important;
}


/*
 * Rainfall series states.
 *
 * A bar of zero height and a day with no data look identical, so a source that
 * publishes on a lag would silently claim it did not rain on the days it has not
 * published yet. Pending and missing days get a visible placeholder track
 * instead of a bar, and no numeric value.
 */
.rain-chart-column.is-pending .rain-chart-bar-shell,
.rain-chart-column.is-missing .rain-chart-bar-shell {
    background-image:
        repeating-linear-gradient(
            135deg,
            color-mix(in srgb, currentColor 12%, transparent) 0 4px,
            transparent 4px 8px);
    border-radius: 4px;
    opacity: 0.65;
}

.rain-chart-column.is-pending .rain-chart-label,
.rain-chart-column.is-missing .rain-chart-label {
    opacity: 0.55;
}

/*
 * Sits between the plot and the totals row. Positioned rather than flowed,
 * because .rain-chart is absolute and a normal-flow sibling renders over it.
 */
.rain-chart-source {
    bottom: 34px;
    font-size: 0.75rem;
    left: 8px;
    line-height: 1.3;
    margin: 0;
    opacity: 0.7;
    position: absolute;
    right: 8px;
}

/* The note is one line on a large card and two on a medium one, so the room it
   needs is measured at render and handed over in --rain-source-height. */
.rain-chart.has-source-note {
    bottom: calc(40px + var(--rain-source-height, 20px));
}

/* ==========================================================================
   Page-owned rules for elements AppShell authors

   A page passes PageClass and MainClass to AppShell, which puts them on the shell
   root and the main region - elements the page no longer authors. A scoped rule
   naming one of them as its last compound therefore stops matching the moment the
   page moves onto the shell, silently: the selector is still there, the attribute
   it wants is not.

   These four came out of Account.razor.css and Stations.razor.css unchanged when
   those pages were converted. Global, because the elements are.
   ========================================================================== */

/* Keep account content inside the same visual shell as the dashboard. */
.account-shell {
    --account-page-bg: var(--bg);
    --account-surface: var(--surface);
    --account-surface-2: var(--surface-2);
    --account-border: var(--border);
    --account-text: var(--text);
    --account-heading: var(--text);
    --account-muted: var(--muted);
    --account-input-bg: var(--surface-2);
    --account-input-border: var(--border);
    --account-accent: var(--accent);
    --account-accent-hover: color-mix(in srgb, var(--accent) 84%, var(--text));
    --account-accent-soft: var(--accent-soft);
    --account-danger: var(--danger);
    --account-shadow: 0 1px 2px rgba(0, 0, 0, .08);
}

.account-dashboard-main {
    padding-bottom: 32px;
}

.admin-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);
}

.admin-dashboard-main {
    padding-bottom: 32px;
}

.stations-main {
    display: flex;
    flex-direction: column;
    gap: 16px;

    /* Top gutter from the shared token rather than its own 22px: this padding is above
       AppTopBar, so a page-specific value moved the shared header down by 8px on this
       surface alone. The horizontal and bottom rhythm are Stations' own and unchanged. */
    padding: var(--cx-page-pad-top) 26px 34px;
}

@media (max-width: 640px) {
    /* The 90px bottom this used to carry predated the shell reserving room for
       AppBottomBar, so the two were additive - about 166px of nothing under the last
       station. The shell's clearance is the only one needed now, and the horizontal
       inset is Stations' own. */
    .stations-main {
        padding: 0 14px var(--cx-page-pad-bottom-phone);
    }
}

/* On a phone the controls stay attached to the map, not to the viewport's fixed bottom
   navigation. Lifting them by the full navigation clearance put the bar near the map's
   vertical centre whenever the radar appeared higher on the page. The attribution and
   control bar fit beside each other, so both can occupy the true bottom edge. */
@media (max-width: 640px) {
    .radar-controls {
        bottom: 8px;
    }
}

/* The brand is a link now, in both bars. The sidebar laid its two halves out itself;
   the anchor sits between .brand and them, so it carries that layout instead. */
.brand-home {
    align-items: center;
    color: inherit;
    display: flex;
    gap: 10px;
    min-width: 0;
    text-decoration: none;
}

.brand-home:hover .brand-name {
    color: var(--accent);
}

.brand-home:focus-visible {
    border-radius: 8px;
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* =========================================================================================
   THE PREDICTIVE WATERING EXPLANATION DIALOG.

   CONTENT ONLY. The shell - backdrop, panel, header, close control, open/close animation and
   the light/dark tokens - is the `.weather-alert-dialog*` block above, shared with the alert
   detail dialog and not duplicated here. Everything below styles what sits INSIDE that shell,
   and every rule is scoped to this one dialog so the alert detail is untouched.
   ========================================================================================= */

/*
   The alert body sets `white-space: pre-line`, because an NWS alert arrives as preformatted
   text whose line breaks carry meaning. This dialog's copy is authored as markup, so the source
   indentation would otherwise be drawn as literal line breaks - and it was, until this rule.

   `!important` IS REQUIRED HERE, and it is not a shortcut. Three separate blocks above declare
   `.weather-alert-dialog-body p { white-space: pre-line !important; ... }`, so an id-scoped
   selector still loses to them: importance beats specificity. The same three blocks also force
   `margin` and `font-size` on any `p` in this shell, which is why the two rules below have to
   restate theirs the same way. Every one is scoped to this dialog, so the alert detail keeps the
   preformatted behaviour it needs.
*/
#predictive-watering-dialog .weather-alert-dialog-body p {
    white-space: normal !important;
}

/* Shorter sections want less air between them than the alert's long prose blocks do. */
#predictive-watering-dialog .weather-alert-dialog-body {
    gap: 13px;
}

#predictive-watering-dialog .pwd-list {
    color: var(--muted);
    display: grid;
    gap: 6px;
    line-height: 1.5;
    margin: 8px 0 0;
    padding-left: 18px;
}

/* Every caveat is one line of a single list, so each is stated once and reads at a glance. */
#predictive-watering-dialog .pwd-caveats strong {
    color: var(--text);
}

/* The three recommendations: the term is the reading, the definition explains it. */
#predictive-watering-dialog .pwd-definitions {
    display: grid;
    gap: 4px 14px;
    grid-template-columns: auto 1fr;
    margin: 8px 0 14px;
}

#predictive-watering-dialog .pwd-definitions dt {
    color: var(--text);
    font-weight: 700;
    letter-spacing: .04em;
}

#predictive-watering-dialog .pwd-definitions dd {
    color: var(--muted);
    line-height: 1.5;
    margin: 0;
}

/*
   THE TWO PRIORITIES, SIDE BY SIDE. Both are described rather than only the active one: a
   reader deciding whether to change the setting needs to see what the alternative does, and
   showing only the current choice makes the dialog useless for that question.
*/
#predictive-watering-dialog .pwd-priorities {
    display: grid;
    gap: 10px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin: 10px 0 14px;
}

#predictive-watering-dialog .pwd-priority {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    opacity: .62;
    padding: 12px;
}

/* The active one is lifted; the other stays legible but plainly secondary. */
#predictive-watering-dialog .pwd-priority.is-active {
    border-color: var(--accent);
    opacity: 1;
}

#predictive-watering-dialog .pwd-priority h4 {
    align-items: center;
    color: var(--text);
    display: flex;
    font-size: .82rem;
    gap: 8px;
    margin: 0;
}

#predictive-watering-dialog .pwd-priority p {
    margin-top: 6px !important;
}

/* The tag is authored on both and revealed on the active one, so the script sets one class. */
#predictive-watering-dialog .pwd-active-tag {
    background: var(--accent);
    border-radius: 999px;
    color: var(--bg);
    display: none;
    font-size: .58rem;
    font-weight: 800;
    letter-spacing: .06em;
    padding: 3px 7px;
    text-transform: uppercase;
}

#predictive-watering-dialog .pwd-priority.is-active .pwd-active-tag {
    display: inline-flex;
}

#predictive-watering-dialog .pwd-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 22px;
}

#predictive-watering-dialog .pwd-action {
    align-items: center;
    color: var(--accent);
    display: inline-flex;
    font-weight: 600;
    gap: 6px;
    text-decoration: none;
}

#predictive-watering-dialog .pwd-action:hover,
#predictive-watering-dialog .pwd-action:focus-visible {
    text-decoration: underline;
}

/* The effective thresholds, read from the response. */
#predictive-watering-dialog .pwd-settings {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin: 10px 0;
}

#predictive-watering-dialog .pwd-setting {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
}

#predictive-watering-dialog .pwd-setting span {
    color: var(--muted);
    font-size: .68rem;
    letter-spacing: .04em;
    text-transform: uppercase;
}

#predictive-watering-dialog .pwd-setting strong {
    color: var(--text);
    font-size: 1rem;
}

#predictive-watering-dialog .pwd-note {
    font-size: .78rem !important;
}

/* On a phone the two-across pairs stack rather than shrinking to unreadable columns. */
@media (max-width: 560px) {
    #predictive-watering-dialog .pwd-priorities,
    #predictive-watering-dialog .pwd-settings {
        grid-template-columns: minmax(0, 1fr);
    }

    #predictive-watering-dialog .pwd-definitions {
        grid-template-columns: minmax(0, 1fr);
    }

    #predictive-watering-dialog .pwd-definitions dd {
        margin-bottom: 6px;
    }
}

/* ===========================================================================
   THE COMPACT RAIL BAND: 641-900px

   THE SAME RAIL AS `.sidebar-collapsed`, NOT A SECOND ONE. Every value below is
   the value that block already uses - 68px column, 8px side padding, hidden
   labels, 40px square nav tiles. If one of them changes, both move together;
   they are one design with two triggers.

   WHY A SECOND TRIGGER AT ALL. `.sidebar-collapsed` is a persisted READER
   PREFERENCE, written to storage by the sidebar's own toggle. Driving it from
   the viewport would overwrite a choice the reader made, and it would have to
   be driven from component state because Blazor owns the shell's class
   attribute and rewrites it on every render. A width band is not a preference,
   so it belongs in a media query.

   WHY THE BAND EXISTS. Retiring the horizontal top navigation restored the full
   176px rail between 641 and 900, which took that much width off the content
   box - measured, the dashboard's cards lost about a quarter of their size
   there. The compact rail gives 108px of it back and keeps the left-rail
   navigation model the product asks for.
   =========================================================================== */

@media (min-width: 641px) and (max-width: 900px) {
    .weather-shell {
        grid-template-columns: 68px minmax(0, 1fr);
    }

    .sidebar {
        padding-left: 8px;
        padding-right: 8px;
    }

    .brand {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
    }

    .brand-copy,
    .nav-label,
    .station-mini > div {
        display: none;
    }

    .nav-list {
        gap: 10px;
        justify-items: center;
        width: 100%;
    }

    .nav-item {
        gap: 0;
        height: 40px;
        justify-content: center;
        min-height: 40px;
        padding: 0;
        width: 40px;
    }

    .station-mini {
        justify-content: center;
        min-height: 42px;
        padding-left: 0;
        padding-right: 0;
    }

    .status-dot {
        flex: 0 0 auto;
    }

    /* THE TOGGLE GOES, BECAUSE IT WOULD LIE. The rail is compact here by width,
       not by choice, so expanding it would do nothing visible - a control that
       appears to be broken is worse than no control. The reader's stored
       preference is untouched and returns above 900. */
    .sidebar-collapse-toggle {
        display: none;
    }
}

/* ===========================================================================
   ADMINISTRATION WORKSPACE
   ===========================================================================

   PROMOTED FROM Admin.razor.css ON 2026-09-10, AND THAT IS THE WHOLE POINT.

   Administration used to be one page, so every admin pattern - the cards, the
   card headings, the icon chips, the two-column grid, the tables, the empty
   states - lived in that page's scoped stylesheet. Scoped CSS does not reach
   another component, so the moment Administration became ten routes, each new
   page either got a copy or looked wrong. /admin/cases hit exactly that: it
   borrowed `admin-topbar` and `admin-page`, got the markup and none of the
   layout, and its header collapsed.

   So these are global. Every /admin/* page uses them and none of them owns a
   stylesheet of its own. Rules that the design system already provided were
   dropped rather than moved: `.admin-field` was `.app-field` exactly, and
   `.admin-page` / `.admin-page-width` / `.admin-title-row` are now
   `.app-page-header` / `.app-page-width`.

   The admin SHELL itself adds almost nothing here: it reuses .weather-shell,
   .sidebar, .nav-item and .dashboard-main, which already carry the collapse
   behaviour and the responsive breakpoints. Administration is a different nav
   list in the same frame, not a second frame.
   ========================================================================== */

.admin-eyebrow {
    margin: 0 0 5px;
}

.admin-description {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    line-height: 1.45;
    margin: var(--cx-space-2) 0 0;
}

.admin-primary-grid {
    align-items: stretch;
    display: grid;
    gap: var(--cx-space-6);
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.admin-card {
    padding: 18px;
}

.admin-card-heading {
    align-items: flex-start;
    display: flex;
    gap: 11px;
    margin-bottom: var(--cx-space-6);
}

.admin-card-heading h2 {
    font-size: var(--cx-text-xl);
    margin: 0;
}

.admin-card-heading p {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    line-height: 1.4;
    margin: var(--cx-space-1) 0 0;
}

.admin-card-icon,
.admin-tool-icon {
    align-items: center;
    background: var(--app-accent-soft);
    border: 1px solid color-mix(in srgb, var(--app-accent) 24%, var(--app-border));
    border-radius: var(--cx-radius-sm);
    color: var(--app-accent);
    display: inline-flex;
    flex: 0 0 auto;
    height: 32px;
    justify-content: center;
    width: 32px;
}

.admin-card-icon svg,
.admin-tool-icon svg,
.admin-create-button svg,
.admin-copy-button svg,
.admin-empty-icon svg {
    fill: none;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
}

.admin-card-icon svg {
    height: 17px;
    width: 17px;
}

.admin-form {
    display: grid;
    gap: 14px;
}

/* ONE CONTROL GEOMETRY FOR THE WHOLE WORKSPACE.

   There were four. `.app-input` from the design system is 42px tall with a 10px radius; this
   block used to be a 46px/12px re-implementation; Mail scoped a 43px/11px one through ::deep;
   and Beta's toolbar select was 40px. Three of them also disagreed about the text size and two
   about the background, so a field on Beta, a field on Mail and a field on Integrations were
   three different controls doing the same job.

   THE FIX IS TO STOP RE-IMPLEMENTING IT. `.app-input`, `.app-select` and `.app-textarea`
   already carry the height, radius, padding, border, background and focus ring, and they are
   what the markup already asks for - the local rules were only ever overriding them. What
   Administration genuinely needs on top is ONE declaration: the design system leaves control
   text at `font: inherit`, which is 16px, and that is off the scale this workspace was moved
   onto in the typography pass. So the size is named here and nothing else is. */
.admin-shell .app-input,
.admin-shell .app-select,
.admin-shell .app-textarea {
    font-size: var(--cx-text-md);
}

/* Helper text under a field. `.app-field` supplies the stack and the label; the line beneath
   it had no rule at all, so it fell back to 16px in full contrast and read louder than the
   label above it. */
.admin-shell .app-field small {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    line-height: var(--cx-leading);
}

/* ONE BOOLEAN, DRAWN ONE WAY.

   Administration had five checkbox treatments: the case gate's bare native box, Mail's with an
   accent-color, the AI enable toggle without one, four unclassed escalation boxes, and the
   queue's draft flag. The platform's own switch - `.theme-switch` - is a span pair driven by a
   button rather than an input, so adopting it here would mean rewriting five working controls
   and their bindings to gain appearance. The native control keeps the keyboard behaviour, the
   checked state and the theme's own rendering; what it needed was to be the same size and the
   same colour everywhere, and to have the focus ring the rest of the workspace uses. */
.admin-shell input[type="checkbox"] {
    accent-color: var(--app-accent);
    block-size: 16px;
    cursor: pointer;
    flex: 0 0 auto;
    inline-size: 16px;
    margin: 0;
}

.admin-shell input[type="checkbox"]:focus-visible {
    outline: var(--cx-focus-width) solid var(--app-accent);
    outline-offset: var(--cx-focus-offset);
}

.admin-shell input[type="checkbox"]:disabled {
    cursor: not-allowed;
    opacity: .55;
}

/* The status line at the top of an integration card - "Not configured", "Connected" - which
   carried a class no stylesheet defined, so it rendered as a 16px full-contrast paragraph with
   the user agent's own margin: the largest text in the card, for a line that is context. */
.admin-ambient-status {
    color: var(--app-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--cx-text-md);
    gap: var(--cx-space-2);
    line-height: var(--cx-leading);
    margin: var(--cx-space-5) 0;
}

.admin-ambient-status strong {
    color: var(--app-text);
    font-weight: var(--cx-weight-bold);
}

.admin-form-actions {
    align-items: end;
    display: grid;
    gap: 14px;
    grid-template-columns: minmax(150px, 190px) minmax(190px, 1fr);
}

/* NOT A SECOND BUTTON. `.app-button` owns the height, the radius, the padding and the gap;
   this used to restate all four at different values, so the create action on /admin/beta was
   46px and 12px beside a 40px, 10px Refresh. What is left is only where the button SITS - the
   right-hand cell of the form-actions grid, capped so it does not stretch across it. */
.admin-create-button {
    justify-self: end;
    max-width: 230px;
    min-width: 205px;
    width: 100%;
}

.admin-create-button svg,
.admin-copy-button svg {
    height: 15px;
    width: 15px;
}

.admin-message {
    font-size: var(--cx-text-sm);
    line-height: 1.4;
}

.admin-result-card {
    display: flex;
    flex-direction: column;
}

.admin-empty-state {
    flex: 1;
    gap: var(--cx-space-5);
    min-height: 132px;
    padding: var(--cx-space-7);
}

.admin-empty-icon {
    align-items: center;
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: 50%;
    color: var(--app-muted);
    display: inline-flex;
    flex: 0 0 auto;
    height: 38px;
    justify-content: center;
    width: 38px;
}

.admin-empty-icon svg {
    height: 18px;
    width: 18px;
}

.admin-empty-state > div {
    display: grid;
    gap: var(--cx-space-1);
    text-align: left;
}

.admin-empty-state strong {
    color: var(--app-text);
    font-size: var(--cx-text-md);
}

.admin-empty-state span:last-child {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    line-height: 1.4;
}

.admin-invitation-result {
    display: grid;
    gap: 14px;
}

.admin-result-summary {
    background: color-mix(in srgb, var(--app-success) 8%, var(--app-surface-2));
    border: 1px solid color-mix(in srgb, var(--app-success) 28%, var(--app-border));
    border-radius: var(--cx-radius-sm);
    display: grid;
    gap: 3px;
    padding: 11px 12px;
}

.admin-result-status {
    color: var(--app-success);
    font-size: var(--cx-text-2xs);
    font-weight: 800;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.admin-result-summary strong {
    font-size: var(--cx-text-md);
}

.admin-result-summary small {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
}

.admin-link-row {
    display: grid;
    gap: var(--cx-space-3);
    grid-template-columns: minmax(0, 1fr) auto;
}

.admin-copy-button {
    gap: var(--cx-space-2);
    min-width: 82px;
}

.admin-create-another {
    background: transparent;
    border: 0;
    color: var(--app-accent);
    cursor: pointer;
    font: inherit;
    font-size: var(--cx-text-xs);
    font-weight: 700;
    justify-self: start;
    padding: 0;
}

.admin-create-another:hover {
    text-decoration: underline;
}

.admin-tool-grid {
    display: grid;
    gap: 9px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.admin-tool-card {
    align-items: center;
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--cx-radius-md);
    display: flex;
    gap: var(--cx-space-4);
    min-width: 0;
    padding: 11px;
}

.admin-tool-icon {
    background: var(--app-surface-3);
    border-color: var(--app-border);
    color: var(--app-muted);
    height: 31px;
    width: 31px;
}

.admin-tool-icon svg {
    height: 16px;
    width: 16px;
}

.admin-tool-text {
    display: grid;
    flex: 1;
    gap: 3px;
    min-width: 0;
}

.admin-tool-text strong {
    font-size: var(--cx-text-md);
}

.admin-tool-text small {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    line-height: 1.35;
}

.validation-message {
    color: var(--app-danger);
    display: block;
    font-size: var(--cx-text-xs);
}

@media (max-width: 560px) {
    /* THE ACTION ROW STACKS RATHER THAN OVERFLOWS.

       `admin-form-actions` asks for minmax(150px, 190px) and minmax(190px, 1fr), so its tracks
       need 354px before the gap. A card on a 390px phone offers about 336px of inner width, and
       the second button simply ran past the card's right edge - measured 60px past it on
       /admin/integrations, with the card scrolling sideways to reach it.

       There WAS a rule here that looked like it fixed this and did not: it restated the same
       two tracks verbatim, so it changed nothing at any width. This is what it was reaching for.
       One column, and minmax(0, 1fr) rather than 1fr so a long label shrinks its track instead
       of establishing a floor under it. */
    .admin-form-actions {
        grid-template-columns: minmax(0, 1fr);
    }

    .admin-link-row {
        grid-template-columns: 1fr;
    }

    .admin-copy-button {
        width: 100%;
    }

    .admin-tool-card {
        align-items: flex-start;
    }
}



/* BACKFILL RUN HISTORY.

   Plain and readable rather than styled: the table has thirteen columns because an operator
   watching a controlled import needs all of them at once, and the horizontal scroll is what
   keeps that from widening the page. No new colour, no new type scale. */
/* The generic names were added when a second table (user accounts) needed the
   same treatment. The run-history names are kept alongside them rather than
   renamed, so the markup above is untouched and this stays a pure addition. */
.admin-runs-scroll,
.admin-table-scroll {
    overflow-x: auto;
    width: 100%;
}

.admin-runs-table,
.admin-table {
    border-collapse: collapse;
    font-size: var(--cx-text-lg);
    width: 100%;
}

.admin-runs-table th,
.admin-runs-table td,
.admin-table th,
.admin-table td {
    border-bottom: 1px solid var(--border);
    padding: .35rem .5rem;
    text-align: left;
    white-space: nowrap;
}

.admin-runs-table th,
.admin-table th {
    color: var(--muted);
    font-weight: 700;
}

/* USER PASSWORD RECOVERY.

   Layout only. The search row puts the field and its button on one line and
   wraps them on a narrow viewport; the rest is spacing. The temporary password
   itself is the one thing given its own treatment - it is read off this screen
   and typed on another, so it is set large, monospaced and letter-spaced, which
   is what makes 0/O and 1/I distinguishable at a glance. */
/* Not a grid. The section wrapper exists only so the card sits in the page's
   normal vertical rhythm; admin-primary-grid would halve its width. */
.admin-user-section {
    display: block;
}

.admin-user-search {
    align-items: flex-end;
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-bottom: 1rem;
}

/* Capped. The card is full width for the results table's sake, and a search
   field that grows with it would be a 1,200px box holding an email address. */
.admin-user-search .app-field {
    flex: 1 1 16rem;
    margin-bottom: 0;
    max-width: 28rem;
}

.admin-user-confirm-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .75rem;
}

.admin-user-issued .app-button {
    margin-top: .75rem;
}

.admin-temporary-password {
    font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", Menlo, Consolas, monospace;
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: .28em;
    margin: .35rem 0 .6rem;
    user-select: all;
    word-break: break-all;
}

/* STATION SETUP - the same problem as the temporary password above.

   The station's upload ID and its one-time station key are read off this screen and
   typed into a weather console's keypad, so they get the same monospace stack for the
   same reason: 0/O and 1/I have to stay apart. Here rather than in Stations.razor.css
   because that stylesheet declares no font family by rule - the page must not grow a
   second type scale - and because sharing the stack with .admin-temporary-password is
   what keeps the two one-time secrets in the product looking like one idea.

   Sizing and spacing stay in the page's own stylesheet. */
.stations-setup-key-id,
.stations-setup-secret {
    font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", Menlo, Consolas, monospace;
}

/* --- The Administration frame's own additions -----------------------------------------

   Deliberately short. The shell reuses .weather-shell, .sidebar, .nav-item, .nav-icon,
   .nav-label, .brand, .sidebar-footer and .dashboard-main unchanged; these are only the
   parts Administration has and the weather shell does not - an exit link, a phone nav, and
   the header spacing. Anything longer than this would mean the frame was being rebuilt
   rather than reused. */

/* A LONE CARD IN THE TWO-COLUMN GRID TAKES THE WHOLE ROW.

   `admin-primary-grid` was written when Administration was one page and its cards came in
   pairs. Splitting the page up left several areas with a single card in a two-column grid,
   which pinned it to the left half: on /admin/data the backfill table - the widest thing in
   the workspace - was squeezed into about a third of the page and scrolled sideways inside
   it, with the right half of the screen empty.

   Stated here rather than by unwrapping the markup on each page, because the grid is right
   whenever a second card arrives; what was wrong was its behaviour with one. */
.admin-primary-grid > :only-child {
    grid-column: 1 / -1;
}

.admin-nav-exit {
    border-top: 1px solid var(--app-border);
    margin-top: var(--cx-space-4);
    padding-top: var(--cx-space-4);
}

.admin-nav-back .nav-icon svg {
    fill: none;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.8;
}

.admin-head {
    margin-bottom: var(--cx-space-6);
}

/* The title block inside `admin-head`.

   Replaces the borrowed `station-heading`, which supplied no layout at all - only Dashboard
   typography, including an !important that rendered every admin subtitle at 8px. The one
   declaration here is what a flex item needs to wrap a long title rather than push the header
   actions off the row; `station-heading` did not have it, so this is the only behavioural
   difference and it is the safe direction. */
.admin-head-titles {
    min-width: 0;
}

.admin-head h1 {
    font-size: 1.32rem;
    margin: 2px 0 0;
}

.admin-head-description {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    margin: var(--cx-space-1) 0 0;
}

.admin-body {
    display: grid;
    gap: var(--cx-space-6);
}

/* THE WIDE ADMINISTRATION WORKSPACE.

   `app-page-width` caps content at 1180px and centres it, which is right for the form- and
   prose-shaped areas - Mail, Users, Beta, Demo - where a long measure is harder to read and a
   stretched input looks broken.

   It is wrong for an operational surface. Measured on /admin/cases at a 2752px viewport: the
   rail took 198, `dashboard-main` offered 2518 usable, and the queue was drawn 1180 wide
   starting at x=885 - 669px of dead canvas either side, with the detail drawer then overlaying
   a list that was already squeezed into the middle.

   So this is the opt-in for surfaces whose job is to show as much as the screen allows. It
   removes the cap and the auto-centring only; the outer padding on `dashboard-main` still
   supplies the workspace's own gutters, so content stays anchored to the left of the content
   area rather than against the rail.

   A PAGE OPTS IN - it is not the default. Making every admin area full width would stretch a
   sender-identity form across an ultrawide monitor, which is the opposite mistake. */
.admin-body-wide {
    margin-inline: 0;
    max-width: none;
    width: 100%;
}

.admin-gate {
    color: var(--app-muted);
    font-size: var(--cx-text-lg);
    padding: 28px;
}

/* THE PHONE'S WAY INTO THE ADMIN NAV.

   Gated at 640, not 900, and that number is not arbitrary. The shared shell hides the left
   rail below 641 and hands over to AppMobileBar/AppBottomBar - and app.css states as a rule
   that navigation is one of exactly two things at any width, with no third intermediate
   state. Administration cannot use AppBottomBar (that is the weather nav), so this is its
   equivalent, and it appears at precisely the width where the rail stops existing.

   Gating it at 900 - the first attempt - put the rail and this bar on screen together
   between 641 and 900, which is the third state that block exists to prevent. */
.admin-mobile-bar,
.admin-mobile-nav {
    display: none;
}

/* BOTH OVERVIEW GRIDS STACK AT THE SAME WIDTH.

   They did not. `admin-primary-grid` was collapsed twice - once here at 900 and once at 820
   alongside `admin-tool-grid` - and because this block is the later of the two, the 820
   declaration for it never applied. The visible result was that the area tiles went to one
   column at 820 while the cards above them held two until 900, so between those widths the
   page read as two different layouts stacked on each other.

   900 rather than 820 because 900 is the width this workspace already turns on: the rail's
   compact band, the drawer's near-full-screen mode and the Mail settings groups all change
   there, and 820 was used by nothing else. */
@media (max-width: 900px) {
    .admin-primary-grid,
    .admin-tool-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 640px) {
    /* THE ADMIN RAIL HIDES WHERE THE WEATHER RAIL DOES.

       `AppSidebar.razor.css` hides `.sidebar` at this width - but that rule is SCOPED to that
       component, and AdminShell renders its own `<aside class="sidebar">` rather than using
       it. So the admin rail never received it: measured at narrow width, the rail and the
       mobile bar were both on screen at once, which is the third navigation state app.css
       says must not exist.

       Stated here rather than by adopting AppSidebar, because AppSidebar renders the weather
       nav - Administration needs the same frame with a different list, which is the whole
       arrangement. */
    .admin-shell .sidebar {
        display: none;
    }

    .admin-mobile-bar {
        display: flex;
        gap: var(--cx-space-3);
        margin-bottom: var(--cx-space-5);
    }

    .admin-mobile-bar .app-button {
        flex: 1 1 auto;
        justify-content: center;
    }

    .admin-mobile-nav {
        background: var(--app-surface);
        border: 1px solid var(--app-border);
        border-radius: var(--app-radius-md);
        display: grid;
        gap: 2px;
        grid-template-columns: 1fr 1fr;
        margin-bottom: var(--cx-space-5);
        padding: var(--cx-space-2);
    }

    /* Ten destinations in one column is a scroll before the page even begins; two columns
       fit them without one. Below 380 the labels want the full width back. */
    @media (max-width: 380px) {
        .admin-mobile-nav {
            grid-template-columns: 1fr;
        }
    }
}

/* A labelled status list. Administration reports state far more than it edits it - Overview,
   Demo and System all needed the same "field name, value" pairs, so this is one pattern
   rather than three. Same shape as the Cases detail panel's context list, which is where it
   was first written before three more callers wanted it. */
/* ONE LABEL/VALUE LIST, TWO NAMES.

   `cases-context-list` was written first, in the case drawer; `admin-status-list` was written
   when Overview, Demo and System all wanted the same thing, and the comment above it already
   admitted the relationship. They then drifted - 140 against 150, one gap against two, uppercase
   on one set of terms and not the other, and two different value sizes - which is what a
   duplicated pattern does when nobody owns it.

   The cases name is kept rather than renamed: it is in markup and in tests, and renaming it buys
   a reader nothing. It simply shares the rules now, so the two cannot drift again. */
.admin-status-list,
.cases-context-list {
    display: grid;
    gap: var(--cx-space-4) var(--cx-space-6);
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    margin: 0;
}

.admin-status-list dt,
.cases-context-list dt {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    font-weight: 700;
    text-transform: uppercase;
}

.admin-status-list dd,
.cases-context-list dd {
    color: var(--app-text);
    font-size: var(--cx-text-lg);
    margin: 2px 0 0;
    overflow-wrap: anywhere;
}

/* Links inside admin panels take the product's accent rather than the browser's default
   blue-then-purple. Without this an Overview full of navigation reads as an unstyled
   document, and a visited link changes colour for a reason that means nothing here. */
.admin-card a,
.admin-status-list a,
.admin-description a {
    color: var(--app-accent);
    text-decoration: none;
}

.admin-card a:hover,
.admin-status-list a:hover,
.admin-description a:hover {
    text-decoration: underline;
}

.admin-card a:focus-visible,
.admin-status-list a:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
}

/* The area cards on Overview are links, not static tiles. The tool-card pattern was built for
   a static roadmap grid, so it needs the affordances a link has. */
a.admin-tool-card {
    text-decoration: none;
    transition: border-color .12s ease, background .12s ease;
}

a.admin-tool-card:hover {
    background: var(--app-surface-2);
    border-color: color-mix(in srgb, var(--app-accent) 40%, var(--app-border));
}

a.admin-tool-card .admin-tool-text strong {
    color: var(--app-text);
}

/* --- The case queue -------------------------------------------------------------------

   PROMOTED FROM AdminCases.razor.css ON 2026-09-10, for the reason the block above
   states: Administration relies on global CSS, and a page-scoped stylesheet is how an
   admin surface stops matching the rest of the workspace.

   The names stay 'cases-*' rather than becoming 'admin-queue-*'. They describe what this
   is - an operational queue with a rank, a state and a detail drawer - and renaming them
   would be churn across the markup and its tests to no reader's benefit. If a second
   admin queue ever wants these, that is the moment to generalise the name.        */

/* THE CASE QUEUE.

   Layout only. Cards, fields, selects, buttons, badges and messages are the design system's
   (`app-card`, `app-field`, `app-select`, `app-button`, `app-badge`, `app-message`), and the
   page frame - `admin-shell`, `admin-topbar`, `admin-page`, `admin-page-width` - is the one
   /admin already established. This file adds the toolbar, the table and the detail panel,
   which nothing else draws. */

.cases-card {
    padding: var(--cx-space-6);
}

/* THE TOOLBAR WRAPS RATHER THAN SCROLLS.
   Five filters and two buttons do not fit a phone in one row, and a horizontally scrolling
   toolbar hides controls behind a gesture nobody looks for. */
.cases-toolbar {
    align-items: end;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-4);
    margin-bottom: var(--cx-space-3);
}

/* THE FILTERS ARE TOOLING, NOT CONTENT.

   The queue is what this page is for, and the toolbar was taking 67% of the height above the
   first row: five fields at the 42px form height, each with a 6px gap under its label, and a
   14px rule of space before the table.

   `--cx-control-height` is the answer the design system already wrote down - "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". These are that. The form height stays the form height
   everywhere a field is being filled in; a filter is being flicked through. */
.cases-toolbar .app-select,
.cases-toolbar .app-input {
    min-height: var(--cx-control-height);
}

/* The actions join the row rather than sitting beside it: same height as the controls they act
   on, so Refresh and New internal case read as the end of the toolbar and not as a second row. */
.cases-toolbar .app-button {
    min-height: var(--cx-control-height);
}

.cases-filter {
    flex: 1 1 140px;
    gap: var(--cx-space-1);
    min-width: 0;
}

.cases-toolbar-actions {
    align-items: flex-end;
    display: flex;
    flex: 0 0 auto;
    gap: var(--cx-space-3);
    margin-left: auto;
}

/* ----------------------------------------------------------------- bounded loading

   THE RESULT CONTEXT ROW sits between the filters and the queue: what came back on the left,
   how much to fetch at a time on the right. It is not a second toolbar - the toolbar narrows
   what is asked for and this describes the answer - and it is sized to be ignorable, because a
   reader scanning the queue should not have to read it. */
.cases-result-context {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-4);
    justify-content: space-between;
    margin-bottom: var(--cx-space-3);
}

.cases-result-count {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    margin: 0;
}

.cases-result-size {
    align-items: center;
    display: flex;
    gap: var(--cx-space-3);
}

/* The label names the combobox through aria-labelledby - a <label for> cannot reach one. */
.cases-result-size-label {
    color: var(--app-muted);
    font-size: var(--cx-text-2xs);
    font-weight: var(--cx-weight-semibold);
    letter-spacing: var(--cx-tracking-eyebrow);
    text-transform: uppercase;
}

/* Narrow, because the widest thing it ever shows is three digits. */
.cases-rows-select {
    min-width: 84px;
}

/* LOAD MORE sits under the queue, centred, and is absent rather than disabled once the queue is
   exhausted - a permanently greyed control is a question the reader keeps re-asking. */
.cases-load-more {
    display: flex;
    justify-content: center;
    margin-top: var(--cx-space-5);
}

/* INTERNAL CASE CREATION. */
.cases-create {
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-md);
    display: grid;
    gap: var(--cx-space-5);
    margin-bottom: var(--cx-space-6);
    padding: 14px;
}

.cases-create h3 {
    font-size: var(--cx-text-lg);
    margin: 0;
}

.cases-create-grid {
    display: grid;
    gap: var(--cx-space-4);
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.cases-create-eligible {
    align-content: start;
}

.cases-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
}

/* THE QUEUE DOES NOT SCROLL SIDEWAYS ANY MORE.

   It did, at ordinary desktop widths, because ten columns needed 900px - and the two that fell
   off the right were Status and Priority, the two a triager scans for. Dropping Type (the
   reference prefix already says it) and Reporter (a bare account id answers nothing at a
   glance), and folding the build and the timestamp to short forms, brought the natural width
   under 700px.

   `overflow-x` stays as a safety net for a very long title or an unusual zoom level; it is no
   longer load-bearing, and below 760px the table stops being a table entirely - see the narrow
   block at the end of this file. */
.cases-table-scroll {
    overflow-x: auto;
}

.cases-table {
    border-collapse: collapse;
    font-size: var(--cx-text-md);
    min-width: 680px;
    table-layout: fixed;

    /* THE TABLE FILLS THE WORKSPACE. NO CEILING.

       There was a `max-width: 1500px` here, added so the surplus stayed as empty canvas on the
       right for the drawer to overlay. That was the wrong trade and it is gone: the queue is
       the primary workspace, and reserving a third of an ultrawide monitor for a panel that is
       usually closed is a permanent cost paid for an occasional convenience. The list is
       allowed to sit under the drawer.

       Title is the only column without a fixed width, so it takes the surplus and the token
       columns stay tight together on the right. That is the readable arrangement: the tokens
       keep their scanning band rather than each one growing a wide empty cell. */
    width: 100%;
}

/* Fixed widths for everything that is a token, so the title takes the remainder rather than
   every column negotiating for space each render. */
.cases-table th:nth-child(1) { width: 92px; }   /* Reference */
.cases-table th:nth-child(3) { width: 72px; }   /* Origin */
.cases-table th:nth-child(4) { width: 48px; }   /* Priority */
.cases-table th:nth-child(5) { width: 104px; }  /* Status */
.cases-table th:nth-child(6) { width: 56px; }   /* Agent */
.cases-table th:nth-child(7) { width: 82px; }   /* Created */
.cases-table th:nth-child(8) { width: 78px; }   /* Build */

.cases-table th {
    border-bottom: 1px solid var(--app-border);
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    font-weight: 700;
    letter-spacing: .02em;
    padding: var(--cx-space-3) var(--cx-space-4);
    text-align: left;
    text-transform: uppercase;
    white-space: nowrap;
}

.cases-table td {
    border-bottom: 1px solid var(--app-border);
    color: var(--app-text);
    padding: 9px 10px;
    vertical-align: middle;
}

.cases-col-centre {
    text-align: center;
}

.cases-table tbody tr:hover {
    background: var(--app-surface-2);
}

.cases-table tr.is-selected {
    background: var(--app-accent-soft);
}

/* RANK AND STATE ARE COLOUR AND WEIGHT, NOT PILLS.

   Following docs/CaeloWX_Semantic_Color_Matrix.md, which is explicit that horizontal space
   should not be spent on a badge when typography says the same thing - and that red is severe,
   amber is elevated concern, blue is active/current, green is resolved, neutral is ordinary. */
.cases-pri {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
}

.cases-pri-critical {
    color: var(--app-danger);
}

.cases-pri-high {
    color: var(--app-warning);
}

/* P2 is the unranked default, so it stays ordinary text: colouring it would make every
   untriaged case look like a decision somebody made. P3 recedes instead. */
.cases-pri-low {
    color: var(--app-muted);
    font-weight: 600;
}

.cases-state {
    font-size: var(--cx-text-md);
    white-space: nowrap;
}

.cases-state-active {
    color: var(--app-accent);
    font-weight: 700;
}

.cases-state-attention {
    color: var(--app-warning);
    font-weight: 700;
}

.cases-state-done {
    color: var(--app-success);
    font-weight: 600;
}

.cases-state-quiet {
    color: var(--app-muted);
}

.cases-origin {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    white-space: nowrap;
}

.cases-when,
.cases-build {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    white-space: nowrap;
}

.cases-build {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
}

/* A boolean drawn as one mark. Present is yes, absent is no; `aria-label` on the element
   carries that for a reader who cannot see it. */
.cases-agent-dot {
    background: var(--app-accent);
    border-radius: 50%;
    display: inline-block;
    height: 9px;
    width: 9px;
}

/* The reference is the control that opens the case - a real button, not a clickable row, so
   it is reachable by keyboard and announced as what it does. */
.cases-reference {
    background: none;
    border: 0;
    color: var(--app-accent);
    cursor: pointer;
    font: inherit;
    font-weight: 700;
    padding: 0;
    white-space: nowrap;
}

.cases-reference:hover {
    text-decoration: underline;
}

.cases-reference:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
}

/* Titles are derived from a submitter's first sentence and can be long. The column takes
   whatever the fixed ones leave and truncates; the full text is on the `title` and in the
   detail panel. */
.cases-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cases-title span {
    color: var(--app-text);
}

/* SUBMITTED TEXT KEEPS THE SHAPE IT WAS WRITTEN IN.
   `pre-wrap` so paragraphs and line breaks survive, and `overflow-wrap` so a pasted URL or an
   unbroken 400-character string cannot push the panel wider than the page. */
.cases-text {
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    color: var(--app-text);
    font-size: var(--cx-text-md);
    line-height: 1.55;
    margin: 0;
    overflow-wrap: anywhere;
    padding: var(--cx-space-4) var(--cx-space-5);
    white-space: pre-wrap;
}

.cases-context-json {
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    font-size: var(--cx-text-sm);
    margin: var(--cx-space-5) 0 0;
    overflow-x: auto;
    padding: var(--cx-space-4) var(--cx-space-5);
    white-space: pre-wrap;
    word-break: break-all;
}

.cases-attachments ul {
    display: grid;
    gap: var(--cx-space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.cases-attachments li {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
}

.cases-attachment-meta {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
}

/* The three editing sections share a rule above them, so the panel reads as
   Submission -> Context -> Triage -> Development direction -> Automation rather than as one
   long form. */
.cases-triage,
.cases-direction,
.cases-automation {
    border-top: 1px solid var(--app-border);
    display: grid;
    gap: var(--cx-space-5);
    padding-top: 14px;
}

.cases-triage-grid {
    display: grid;
    gap: var(--cx-space-4);
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.cases-section-note {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    line-height: 1.55;
    margin: -4px 0 0;
}

.cases-direction-input {
    min-height: 150px;
}

/* THE AGENT GATE, COMPACT.

   It was a tinted box with a paragraph in it, which is a lot of room for one boolean. It is
   now a single row - switch, label, one line of rule, and a state word that says whether the
   two halves currently agree. The rule stays visible because this is the one control here
   whose consequence is that something starts work on its own. */
.cases-gate {
    align-items: center;
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-md);
    cursor: pointer;
    display: flex;
    gap: var(--cx-space-4);
    padding: var(--cx-space-4) var(--cx-space-5);
}

.cases-gate-text {
    display: grid;
    gap: 1px;
    min-width: 0;
}

.cases-gate-text strong {
    font-size: var(--cx-text-lg);
}

.cases-gate-text small {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    line-height: 1.4;
}

/* Reads "Open" only when status is Ready *and* the switch is on - the gate as a fact rather
   than as a control the reader has to combine in their head. */
.cases-gate-state {
    border: 1px solid var(--app-border);
    border-radius: 999px;
    color: var(--app-muted);
    flex: 0 0 auto;
    font-size: .66rem;
    font-weight: 700;
    margin-left: auto;
    padding: 3px 9px;
    text-transform: uppercase;
}

.cases-gate-state.is-open {
    background: var(--app-accent-soft);
    border-color: color-mix(in srgb, var(--app-accent) 38%, var(--app-border));
    color: var(--app-accent);
}

.cases-gate-warning {
    color: var(--app-warning);
    font-size: var(--cx-text-md);
    font-weight: 600;
    line-height: 1.5;
    margin: 0;
}

/* Disclosures for the fields that are read once, not every day. */
.cases-advanced > summary {
    color: var(--app-muted);
    cursor: pointer;
    font-size: var(--cx-text-md);
    font-weight: 700;
    padding: var(--cx-space-1) 0;
}

.cases-advanced > summary:focus-visible {
    outline: 2px solid var(--app-accent);
    outline-offset: 2px;
}

.cases-advanced[open] > summary {
    margin-bottom: var(--cx-space-3);
}

/* NARROW: THE TABLE STOPS BEING A TABLE.

   Eight columns cannot be read on a phone at any size worth having, and a sideways-scrolling
   table hides the columns that matter behind a gesture. Each row becomes a card instead, with
   the reference and the title leading and the tokens beneath them - the same information, in
   the order it is scanned. `data-label` on each cell supplies the field name that the removed
   header row was carrying. */
@media (max-width: 760px) {
    .cases-table-scroll {
        overflow-x: visible;
    }

    .cases-table,
    .cases-table tbody,
    .cases-table tr,
    .cases-table td {
        display: block;
        width: auto;
    }

    .cases-table {
        min-width: 0;
    }

    .cases-table thead {
        /* Hidden from sight, not from the accessibility tree. */
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
        height: 1px;
        overflow: hidden;
        position: absolute;
        white-space: nowrap;
        width: 1px;
    }

    .cases-table tr {
        border: 1px solid var(--app-border);
        border-radius: var(--app-radius-md);
        display: grid;
        gap: var(--cx-space-1) var(--cx-space-4);
        grid-template-columns: auto 1fr;
        margin-bottom: var(--cx-space-4);
        padding: var(--cx-space-4) var(--cx-space-5);
    }

    .cases-table td {
        border: 0;
        padding: 0;
    }

    /* Reference and title lead, side by side, like the row they replace. */
    .cases-table td[data-label="Reference"] {
        grid-column: 1;
    }

    .cases-table td[data-label="Title"] {
        grid-column: 2;
        white-space: normal;
    }

    /* The rest run as a labelled wrap beneath them. */
    .cases-table td[data-label="Origin"],
    .cases-table td[data-label="Priority"],
    .cases-table td[data-label="Status"],
    .cases-table td[data-label="Agent eligible"],
    .cases-table td[data-label="Created"],
    .cases-table td[data-label="Build"] {
        align-items: baseline;
        display: flex;
        gap: var(--cx-space-2);
        grid-column: 1 / -1;
        text-align: left;
    }

    .cases-table td[data-label="Origin"]::before,
    .cases-table td[data-label="Priority"]::before,
    .cases-table td[data-label="Status"]::before,
    .cases-table td[data-label="Agent eligible"]::before,
    .cases-table td[data-label="Created"]::before,
    .cases-table td[data-label="Build"]::before {
        color: var(--app-muted);
        content: attr(data-label);
        flex: 0 0 92px;
        font-size: var(--cx-text-xs);
        font-weight: 700;
        text-transform: uppercase;
    }

    /* An empty agent cell would otherwise render a label with nothing after it. */
    .cases-table td[data-label="Agent eligible"]:empty {
        display: none;
    }
}

@media (max-width: 640px) {
    .cases-toolbar-actions {
        margin-left: 0;
        width: 100%;
    }

    .cases-toolbar-actions .app-button {
        flex: 1 1 auto;
    }

    .cases-create-grid,
    .cases-triage-grid {
        grid-template-columns: 1fr;
    }

    /* The state word drops under the label rather than squeezing the rule to two words. */
    .cases-gate {
        align-items: flex-start;
        flex-wrap: wrap;
    }

    .cases-gate-state {
        margin-left: 0;
    }
}

/* --- AI TRIAGE, IN THE QUEUE AND IN THE DRAWER ------------------------------------------

   TWO SURFACES, ONE RULE: A RECOMMENDATION MUST NEVER LOOK LIKE A VALUE.

   Everything in this block is visibly a suggestion sitting beside the owner's own field, and
   that separation is carried by structure rather than by a label: recommendations live in
   their own bordered section, in their own tint, with their own accept and reject controls,
   below the Triage controls that hold the real values. A suggestion rendered into the same
   input as the stored value would be indistinguishable from it the moment somebody glanced
   at the screen.

   NO NEW COLUMN IN THE QUEUE. The table is eight columns and got there by dropping two: at
   ordinary desktop widths a ninth pushes Status and Priority off the right, which is the
   regression `AdminCaseSurfaceTests.TheTableDoesNotForceADesktopScrollbar` exists to prevent.
   The triage marks ride inside the Reference cell as small dots, so the signal costs no
   horizontal space at all. */

/* THE QUEUE'S MARKS.

   A row of up to three dots beside the reference. Present means yes, absent means no, and the
   accessible name carries it for a reader who cannot see the dot - the same convention the
   agent-eligibility mark already established two columns over. */
.cases-marks {
    display: inline-flex;
    gap: 3px;
    margin-left: var(--cx-space-2);
    vertical-align: middle;
}

.cases-mark {
    border-radius: 999px;
    display: inline-block;
    height: 6px;
    width: 6px;
}

/* Accent: a recommendation is waiting to be read. */
.cases-mark-triage {
    background: var(--app-accent);
}

/* Warning: the machine thinks this one needs the product owner, not a developer. */
.cases-mark-decision {
    background: var(--app-warning);
}

/* Hollow: this case is proposed into a group, or already in one. Hollow rather than filled
   because grouping is a relationship between rows and not a property of this one. */
.cases-mark-group {
    border: 1.5px solid var(--app-accent);
    height: 7px;
    width: 7px;
}

.cases-mark-group.is-confirmed {
    background: var(--app-accent);
}

/* THE DRAWER'S AI TRIAGE SECTION.

   Shares the rule-above-it treatment with Triage, Development direction and Automation, so the
   panel keeps reading top to bottom as one document. It sits AFTER Triage and BEFORE
   Development direction: a recommendation is read while the classification is being decided,
   and it has nothing to say about the instructions that follow. */
.cases-ai {
    border-top: 1px solid var(--app-border);
    display: grid;
    gap: var(--cx-space-5);
    padding-top: 14px;
}

.cases-ai-head {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
}

.cases-ai-head h3 {
    margin: 0;
}

/* WHAT PRODUCED THIS, STATED EVERY TIME.

   CaeloWX has no inference provider. What fills these fields today is a rule engine, and this
   chip says so in the operator's own eyeline rather than in a tooltip or a footnote - because
   an operator who believes a keyword rule is a language model will over-trust it exactly once
   and then stop trusting the feature at all. */
.cases-ai-engine {
    border: 1px solid var(--app-border);
    border-radius: 999px;
    color: var(--app-muted);
    font-size: .66rem;
    font-weight: 600;
    padding: 2px 8px;
    white-space: nowrap;
}

.cases-ai-engine.is-model {
    background: var(--app-accent-soft);
    border-color: color-mix(in srgb, var(--app-accent) 38%, var(--app-border));
    color: var(--app-accent);
}

/* ESCALATED, SAID OUT LOUD.

   A case promoted to the expensive model is the one fact about a run that changes both what it
   cost and how much weight the answer deserves. Warning weight rather than accent, because it is
   a "read this more carefully" signal and not a success. */
.cases-ai-escalated {
    border: 1px solid color-mix(in srgb, var(--app-warning) 40%, var(--app-border));
    border-radius: 999px;
    color: var(--app-warning);
    font-size: .64rem;
    font-weight: 700;
    letter-spacing: .02em;
    padding: 2px 8px;
    text-transform: uppercase;
    white-space: nowrap;
}

/* WHAT THE RUN USED. A tuning aid, deliberately quiet - it is read when somebody is asking about
   cost and ignored the rest of the time, so it must not compete with the recommendation above
   it. */
.cases-ai-usage {
    border-top: 1px solid var(--app-border);
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    line-height: 1.5;
    margin: 0;
    overflow-wrap: anywhere;
    padding-top: 9px;
}

.cases-ai-confidence {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    margin-left: auto;
}

/* THE PANEL THE SUGGESTIONS LIVE IN.
   Tinted and bordered, so the boundary between "what the machine said" and "what the case is"
   is a thing you can see from across the room. */
.cases-ai-body {
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-md);
    display: grid;
    gap: var(--cx-space-5);
    padding: var(--cx-space-5);
}

.cases-ai-summary {
    font-size: var(--cx-text-md);
    line-height: 1.55;
    margin: 0;
}

.cases-ai-reasoning {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    line-height: 1.55;
    margin: 0;
}

/* ONE RECOMMENDATION, ONE ROW, ONE ACCEPT.

   The field name, then the current value, then the arrow, then the proposal, then a button
   that takes only that field. Reading a row answers "what would change" before the operator
   presses anything, which is the whole difference between this and an Accept-everything flow. */
.cases-ai-field {
    align-items: center;
    border-top: 1px solid var(--app-border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
    padding-top: var(--cx-space-4);
}

.cases-ai-field:first-child {
    border-top: 0;
    padding-top: 0;
}

.cases-ai-field-label {
    color: var(--app-muted);
    flex: 0 0 68px;
    font-size: var(--cx-text-sm);
    font-weight: 700;
    text-transform: uppercase;
}

.cases-ai-field-values {
    display: flex;
    flex: 1 1 200px;
    flex-wrap: wrap;
    gap: var(--cx-space-2);
    min-width: 0;
}

.cases-ai-current {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    text-decoration: line-through;
}

.cases-ai-arrow {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
}

.cases-ai-proposed {
    font-size: var(--cx-text-md);
    font-weight: 600;
    overflow-wrap: anywhere;
}

.cases-ai-field .app-button {
    flex: 0 0 auto;
}

/* Applied is a fact, not a button. Once a field has been taken the control is replaced by a
   word, because an Accept that has already happened is the one thing on this panel that must
   not be pressable twice. */
.cases-ai-applied {
    color: var(--app-success);
    flex: 0 0 auto;
    font-size: var(--cx-text-sm);
    font-weight: 700;
    text-transform: uppercase;
}

/* THE OWNER-DECISION FLAG.
   Warning weight, because it is the one output on this panel that says "do not hand this to a
   developer yet" - which is the mistake that costs a whole implementation. */
.cases-ai-decision {
    background: color-mix(in srgb, var(--app-warning) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--app-warning) 35%, var(--app-border));
    border-radius: var(--app-radius-sm);
    display: grid;
    gap: 3px;
    padding: 9px 11px;
}

.cases-ai-decision strong {
    color: var(--app-warning);
    font-size: var(--cx-text-md);
}

.cases-ai-decision span {
    font-size: var(--cx-text-md);
    line-height: 1.5;
}

.cases-ai-note {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    line-height: 1.5;
    margin: 0;
}

.cases-ai-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
}

/* TRIAGE UNAVAILABLE.
   Phase P wants the failure visible and the manual workflow untouched, so this is a quiet
   line rather than an error banner: nothing is broken, a convenience is simply absent. */
.cases-ai-unavailable {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    line-height: 1.55;
    margin: 0;
}

/* --- GROUPS, IN THE DRAWER --------------------------------------------------------------

   THE SIMPLEST SURFACE THAT ANSWERS THE OPERATOR'S QUESTION, which is "how many people have
   told me this and on what builds". A drawer section rather than a route: a group is read
   while looking at one of its cases, and a separate page would mean leaving the queue to
   learn something about the case that is already open. */
.cases-groups {
    border-top: 1px solid var(--app-border);
    display: grid;
    gap: var(--cx-space-5);
    padding-top: 14px;
}

.cases-group {
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-md);
    display: grid;
    gap: var(--cx-space-4);
    padding: var(--cx-space-5);
}

.cases-group-head {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
}

.cases-group-reference {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    font-weight: 700;
    letter-spacing: .02em;
}

.cases-group-title {
    font-size: var(--cx-text-lg);
    font-weight: 600;
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
}

.cases-group-state {
    border: 1px solid var(--app-border);
    border-radius: 999px;
    color: var(--app-muted);
    font-size: .64rem;
    font-weight: 700;
    margin-left: auto;
    padding: 2px 8px;
    text-transform: uppercase;
}

.cases-group-state.is-confirmed {
    background: var(--app-accent-soft);
    border-color: color-mix(in srgb, var(--app-accent) 38%, var(--app-border));
    color: var(--app-accent);
}

/* THE DERIVED FACTS. Every one of these is computed from the confirmed members on read, so a
   figure on this screen cannot disagree with the rows behind it. */
.cases-group-facts {
    display: grid;
    gap: var(--cx-space-3);
    grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
    margin: 0;
}

.cases-group-facts > div {
    display: grid;
    gap: 1px;
}

.cases-group-facts dt {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    font-weight: 700;
    text-transform: uppercase;
}

.cases-group-facts dd {
    font-size: var(--cx-text-md);
    font-weight: 600;
    margin: 0;
    overflow-wrap: anywhere;
}

.cases-group-members {
    display: grid;
    gap: var(--cx-space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.cases-group-member {
    background: var(--app-surface);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    display: grid;
    gap: var(--cx-space-2);
    padding: 9px 11px;
}

.cases-group-member.is-proposed {
    border-color: color-mix(in srgb, var(--app-accent) 32%, var(--app-border));
}

.cases-group-member.is-rejected {
    opacity: .55;
}

.cases-group-member-head {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
}

.cases-group-member-reference {
    font-size: var(--cx-text-md);
    font-weight: 700;
}

.cases-group-member-title {
    flex: 1 1 160px;
    font-size: var(--cx-text-md);
    min-width: 0;
    overflow-wrap: anywhere;
}

.cases-group-member-relation {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    font-weight: 700;
    text-transform: uppercase;
}

/* WHY, IN THE TERMS THE SELECTOR ACTUALLY USED.
   "same route and same area" can be checked in seconds; a bare confidence number cannot be
   checked at all, and the operator is the one who has to be right. */
.cases-group-member-reason {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    line-height: 1.5;
    margin: 0;
    overflow-wrap: anywhere;
}

.cases-group-member-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-2);
}

.cases-group-member-decided {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    font-weight: 700;
    text-transform: uppercase;
}

.cases-group-member-decided.is-confirmed {
    color: var(--app-success);
}

/* --- REMOTE AGENT CREDENTIALS ------------------------------------------------------------

   The issue row and the one-time secret panel, following the temporary-password panel on
   /admin/users: the same shape, because the property is the same - a value the server will
   never produce again, shown once, in a panel that says so. */
.admin-agent-issue {
    align-items: flex-end;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-4);
}

.admin-agent-issue .app-field {
    flex: 1 1 200px;
    min-width: 0;
}

/* AGENTS - the capability chooser.

   Global, in the ADMINISTRATION WORKSPACE block, like every other admin style. Each row is a
   checkbox beside a name and the sentence that says what granting it actually permits: an
   operator choosing machine authority should not have to already know what the key means. */
.admin-agent-capabilities {
    border: 1px solid var(--app-border);
    border-radius: var(--cx-radius-md, 8px);
    display: grid;
    gap: var(--cx-space-3);
    margin: var(--cx-space-4) 0 0;
    padding: var(--cx-space-4);
}

.admin-agent-capabilities legend {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    font-weight: 700;
    padding: 0 var(--cx-space-2);
    text-transform: uppercase;
}

.admin-agent-capability {
    align-items: start;
    display: flex;
    gap: var(--cx-space-3);
}

.admin-agent-capability > span {
    display: grid;
    gap: 2px;
}

/* RELEASES - the published release, read as an announcement.

   THE BODY GETS THE WHOLE WIDTH, and that is the whole point of this block. The published view
   used `cases-context-list`, whose `repeat(auto-fit, minmax(150px, 1fr))` put long-form release
   notes into a column about 150px wide. Prose does not survive that. Here the article is a plain
   block inside the drawer's single-column grid, so it measures the full content width - about
   670px at the drawer's 720px cap, which is already a sensible reading measure. NO width is set:
   hardcoding one is what would break the responsive behaviour the drawer already has. */
.release-notes h3 {
    margin: 0 0 var(--cx-space-3);
}

/* Plain text with its line breaks preserved, which is all this slice needs and all it should
   have: a blank line in the stored body becomes a paragraph gap, and a hand-typed bullet keeps
   its own line. NO Markdown parser and no rich text - that is a later decision, not a side effect
   of a spacing fix.

   `break-word` rather than `anywhere`: a long URL wraps, and ordinary prose is never broken
   mid-word. The label/value pattern uses `anywhere` because it is sizing short values into narrow
   cells, which is the opposite problem. */
.release-notes-body {
    color: var(--app-text);
    font-size: var(--cx-text-lg);
    line-height: 1.65;
    overflow-wrap: break-word;
    white-space: pre-wrap;
}

/* RELEASES - the suggested-notes panel.

   ITS WHOLE JOB IS TO NOT LOOK LIKE THE RELEASE. A drafted suggestion sits beside the fields it
   might become, and a reader glancing at the drawer must never take it for saved content. So it
   is inset, bordered and tinted - visibly a panel on the page rather than part of the form - and
   it says "Not saved" in words as well. The body inside it reuses `.release-notes-body`, because
   a suggestion should be read the way the published note will be read. */
.release-suggestion {
    background: var(--app-surface-muted, transparent);
    border: 1px dashed var(--app-border);
    border-radius: var(--cx-radius-md, 8px);
    display: grid;
    gap: var(--cx-space-3);
    padding: var(--cx-space-4);
}

.release-suggestion-head {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
    justify-content: space-between;
}

.release-suggestion-head h3 {
    margin: 0;
}

.release-suggestion-body {
    display: grid;
    gap: var(--cx-space-3);
}

.release-suggestion-body h4 {
    margin: 0;
}

.release-suggestion-note {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    margin: 0;
}

/* RELEASES - the composer's action row, and the helper text under it.

   `cases-actions` is the shared button row and deliberately carries no vertical margin: on
   /admin/cases it sits inside a toolbar that already provides its own rhythm. Here it follows a
   textarea directly, so the separation has to be stated - and stated HERE rather than by adding a
   margin to the shared class, which would move buttons on a page this change has nothing to do
   with.

   space-6 is the same 16px `.admin-card-heading` uses to separate a heading from the content
   below it, so the composer's internal rhythm matches the card it sits in rather than inventing a
   second one. */
.admin-release-actions {
    margin-top: var(--cx-space-6);
}

/* The helper text belongs to the button without crowding it. `.admin-description`'s own top
   margin is space-2, which suits following a paragraph; following a control it wants a little
   more. */
.admin-release-actions + .admin-description {
    margin-top: var(--cx-space-4);
}

/* RELEASES - the draft composer's field row.

   Global, in the ADMINISTRATION WORKSPACE block, like every other admin style. Scoped CSS does
   not reach another component, so while these patterns lived on one page every new admin route
   either copied them or rendered wrong - which is exactly how /admin/cases's header collapsed.
   No admin page has a stylesheet of its own, and that rule has no carve-out. */
.admin-release-draft {
    align-items: flex-end;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-4);
    margin-bottom: var(--cx-space-4);
}

.admin-release-draft .app-field {
    flex: 1 1 220px;
    min-width: 0;
}

.admin-agent-note {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    line-height: 1.5;
    margin: 0;
}

.admin-agent-issued p {
    margin: 0 0 var(--cx-space-3);
}

/* Monospace and breakable: this is a header line somebody has to select and copy exactly, and
   it is longer than the panel. */
.admin-agent-secret {
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    font-family: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Consolas", monospace;
    font-size: .74rem;
    overflow-wrap: anywhere;
    padding: 9px 11px;
    user-select: all;
}

/* THE KB REFERENCE IN AN ADMIN TABLE. Monospace so a column of them lines up and 0/O stay
   distinguishable, at the same stack and size .admin-agent-key uses - this is the same kind of
   thing, an identifier somebody reads off the screen and quotes. */
.admin-knowledge-reference {
    font-family: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Consolas", monospace;
    font-size: var(--cx-text-md);
    white-space: nowrap;
}

.admin-agent-key {
    font-family: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Consolas", monospace;
    font-size: var(--cx-text-md);
    overflow-wrap: anywhere;
}

/* --- PROVENANCE, AT THE TOP OF AN AGENT-FILED CASE --------------------------------------

   "Discovered while investigating BUG-0021". One line, above the submitted report, because it
   is context for everything below it rather than a fact about the triage decisions.

   It is NOT grouping and must not be styled like the group cards further down: those answer
   "are these the same issue", and a configuration defect found while reading a rain report is
   not the same issue by any reading. A quiet line is the correct weight. */
.cases-provenance {
    align-items: baseline;
    color: var(--app-muted);
    display: flex;
    flex-wrap: wrap;
    font-size: var(--cx-text-md);
    gap: var(--cx-space-2);
    line-height: 1.5;
    margin: 0 0 var(--cx-space-1);
}

.cases-provenance-label {
    text-transform: none;
}

/* A button rather than an anchor: the drawer opens cases in place, so there is no href that
   would mean anything, and an anchor with no href is not focusable. */
.cases-provenance-link {
    background: none;
    border: 0;
    color: var(--app-accent);
    cursor: pointer;
    font: inherit;
    font-weight: 700;
    letter-spacing: .02em;
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.cases-provenance-link:disabled {
    color: var(--app-muted);
    cursor: not-allowed;
    text-decoration: none;
}

.cases-provenance-title {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* --- THE CASE THREAD -------------------------------------------------------------------

   ONE CHRONOLOGY. A remote agent's findings and results, the platform's record of what
   changed, and a person's replies are the same kind of thing - what happened to this case,
   in order - so they are one list. A separate "AI findings" panel would mean interleaving
   two half-threads by timestamp to answer any question about the work.

   THE AUTHOR KIND CARRIES A LEFT BORDER AS WELL AS A CHIP. Colour alone is not a signal
   everybody receives, and the one thing a reader must never get wrong is whether a machine
   or a colleague wrote what they are about to act on. */
.cases-thread {
    border-top: 1px solid var(--app-border);
    display: grid;
    gap: var(--cx-space-5);
    padding-top: 14px;
}

.cases-thread-list {
    display: grid;
    gap: var(--cx-space-3);
    list-style: none;
    margin: 0;
    padding: 0;
}

.cases-thread-entry {
    background: var(--app-surface-2);
    border: 1px solid var(--app-border);
    border-left: 3px solid var(--app-border);
    border-radius: var(--app-radius-sm);
    display: grid;
    gap: 5px;
    padding: 9px 11px;
}

.cases-thread-entry-agent {
    border-left-color: var(--app-accent);
}

/* Recorded field changes sit back: they are the audit trail, read when somebody asks what
   happened rather than as part of the conversation. */
.cases-thread-entry-system {
    background: none;
    border-left-color: color-mix(in srgb, var(--app-muted) 45%, transparent);
}

.cases-thread-head {
    align-items: baseline;
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
}

.cases-thread-chip {
    background: color-mix(in srgb, var(--app-muted) 16%, transparent);
    border-radius: 999px;
    color: var(--app-muted);
    font-size: .62rem;
    font-weight: 700;
    letter-spacing: .04em;
    padding: 1px 7px;
    text-transform: uppercase;
}

.cases-thread-chip-agent {
    background: color-mix(in srgb, var(--app-accent) 16%, transparent);
    color: var(--app-accent);
}

.cases-thread-author {
    font-size: var(--cx-text-md);
    font-weight: 600;
    min-width: 0;
    overflow-wrap: anywhere;
}

.cases-thread-category {
    color: var(--app-muted);
    font-size: var(--cx-text-xs);
    font-weight: 700;
    text-transform: uppercase;
}

/* Pushed right so the eye can scan times down one edge. */
.cases-thread-when {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    margin-left: auto;
}

.cases-thread-body {
    margin: 0;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
}

.cases-thread-compose {
    display: grid;
    gap: var(--cx-space-3);
}

.cases-thread-compose-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
    justify-content: flex-end;
}

/* The category is a short word; letting it take the row's width would make it look like the
   primary control, which is the note itself. */
.cases-thread-compose-actions .app-select {
    flex: 0 0 auto;
    width: auto;
}

@media (max-width: 640px) {
    /* The label stops holding its own column and the row stacks, so a long proposed title is
       not squeezed into forty pixels beside a button. */
    .cases-ai-field {
        align-items: flex-start;
        flex-direction: column;
    }

    .cases-ai-field-label {
        flex: 0 0 auto;
    }

    .cases-ai-confidence {
        margin-left: 0;
    }

    .cases-group-state {
        margin-left: 0;
    }

    .cases-thread-when {
        margin-left: 0;
    }
}

/* --- The Administration detail drawer --------------------------------------------------

   A RIGHT-SIDE SLIDE-OVER, WHICH THE PRODUCT DID NOT HAVE.

   `weather-alert-dialog` was checked first and is the wrong shape: it is a centred modal with
   an opaque backdrop, built to interrupt. This is the opposite - an operations console where
   the queue stays in view and in reach while a case is worked:

       QUEUE -> OPEN CASE -> WORK CASE -> SAVE/CLOSE -> CONTINUE QUEUE

   What it does reuse from that dialog is everything that is not geometry: the `--cx-z-dialog`
   layer, the shadow weight, and the app-* form and button primitives inside it.

   NO BACKDROP ON DESKTOP, AND THAT IS THE CENTRAL DECISION.

   A backdrop would do two things this workflow cannot afford: dim the queue that is supposed
   to stay readable, and swallow the clicks that let somebody open the next case straight from
   the list. So on desktop the drawer is simply a fixed panel on the right - the queue keeps
   full width underneath, stays legible on the left, and stays clickable. Closing is the X or
   Escape, which is what an ops console wants anyway: the panel stays until you are done with
   it.

   The backdrop returns below 900, where the queue is not usable behind a near-full-width
   panel and tapping outside to dismiss is the expected gesture.

   IT DOES NOT REFLOW THE PAGE. `position: fixed` means the queue is never resized, and no
   column is compressed because a case was opened. */

.admin-drawer {
    inset: 0;
    pointer-events: none;
    position: fixed;
    z-index: var(--cx-z-dialog);
}

/* Desktop: no dimming, no click capture - see above. The element still exists so the narrow
   rules have something to turn on. */
.admin-drawer-backdrop {
    display: none;
}

.admin-drawer-panel {
    background: var(--app-surface);
    border-left: 1px solid var(--app-border);
    bottom: 0;
    box-shadow: -18px 0 60px rgba(0, 0, 0, .38);
    display: flex;
    flex-direction: column;
    pointer-events: auto;
    position: absolute;
    right: 0;
    top: 0;

    /* ~40% of the viewport, bounded so it stays workable on a laptop and does not become a
       second page on an ultrawide. */
    width: clamp(420px, 40vw, 720px);
}

/* Header and footer hold their size; only the body scrolls. Sticky positioning would work
   too, but a flex column cannot scroll past its own header, which is the simpler guarantee. */
.admin-drawer-header {
    align-items: flex-start;
    border-bottom: 1px solid var(--app-border);
    display: flex;
    flex: 0 0 auto;
    gap: var(--cx-space-5);
    justify-content: space-between;
    padding: 14px 16px;
}

.admin-drawer-heading {
    display: grid;
    gap: 2px;
    min-width: 0;
}

.admin-drawer-reference {
    color: var(--app-accent);
    font-size: var(--cx-text-sm);
    font-weight: 700;
    letter-spacing: .04em;
}

.admin-drawer-heading h2 {
    font-size: var(--cx-text-xl);
    margin: 0;
    overflow-wrap: anywhere;
}

.admin-drawer-meta {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
}

.admin-drawer-body {
    display: grid;
    flex: 1 1 auto;
    gap: var(--cx-space-6);
    overflow-y: auto;
    padding: var(--cx-space-6);
}

.admin-drawer-footer {
    align-items: center;
    border-top: 1px solid var(--app-border);
    display: flex;
    flex: 0 0 auto;
    gap: var(--cx-space-4);
    justify-content: flex-end;
    padding: var(--cx-space-5) var(--cx-space-6);
}

.admin-drawer-footer .admin-drawer-footer-note {
    color: var(--app-muted);
    font-size: var(--cx-text-md);
    margin-right: auto;
}

/* The unsaved-changes prompt. Inline in the footer rather than a browser confirm: a native
   confirm blocks the whole page and is not something this product uses anywhere. */
.admin-drawer-footer-warn {
    color: var(--app-warning);
    font-size: var(--cx-text-md);
    font-weight: 700;
    margin-right: auto;
}

@media (max-width: 900px) {
    /* Near-full-screen, with the backdrop back. Administration on a phone is low priority -
       this needs to remain usable, not polished. */
    .admin-drawer-backdrop {
        background: rgba(6, 10, 16, .55);
        display: block;
        inset: 0;
        pointer-events: auto;
        position: absolute;
    }

    .admin-drawer-panel {
        width: min(100%, 560px);
    }
}

/* ============================================================================
   HELP & FEEDBACK MENU, AND THE CAPTURE PREVIEW
   ============================================================================

   GLOBAL BECAUSE TWO BARS RENDER THE SAME COMPONENT AND THE PANEL IS SHARED. The panel
   itself is not styled here at all - it wears `.user-menu-panel`, `.user-menu-profile`,
   `.user-menu-links` and `.user-preference-row`, which are already global above and already
   carry both themes. What is here is only what the account menu keeps in its own scoped
   stylesheet, which a scoped rule cannot lend to another component: the root's positioning
   context, the dismissal backdrop, and the phone's sheet placement.

   Copying those three from UserMenu.razor.css into a HelpFeedbackMenu.razor.css would have
   been the obvious move and the wrong one - two scoped copies of one behaviour, drifting the
   first time either is touched. They are stated once, here, next to the rules they mirror.

   NO NEW COLOUR, SPACING OR TYPE VALUE IS INVENTED BELOW. Every one is a token or a value
   already used by the dialog and card patterns this borrows from. */

.help-menu { position: relative; }

.help-menu-backdrop {
    /* Covers the viewport whenever the panel is open, sits under it and over everything else,
       and closes on any click that reaches it. One element rather than a document listener, so
       the menu works on pages that never load dashboard.js. */
    inset: 0;
    position: fixed;
    z-index: calc(var(--cx-z-menu) - 1);
}

/* The capture item is separated from the three intents, because it is a different kind of
   thing: the three open a form, this one takes a photograph and stays where it is. */
/* Capture is separated from the three intents because it is a different kind of thing: the
   three open a form, this one takes a photograph and stays where it is. It is still a
   `.user-menu-links` row, so it wears the same geometry rather than a second definition of it -
   `border-top: 0` because the nav above it already drew that line. */
.help-menu-capture {
    border-bottom: 0;
    border-top: 0;
}

.help-menu-note {
    color: var(--muted);
    font-size: .68rem;
    line-height: 1.35;
    margin: 2px 9px 6px;
}

/* ------------------------------------------------------------ phone placement

   Mirrors what UserMenu.razor.css does for `.user-menu-sheet`, and for the same reason: the
   panel is position:absolute inside a 38px button wrapper, which is right for a desktop
   dropdown and wrong for a phone - whether it fits depends on an ancestor's width, and the
   panel's min-width means it cannot shrink to fit either. Fixed positioning takes the ancestor
   out of it and the insets are viewport units, so containment is a property of the rule. */
@media (max-width: 640px) {
    .help-menu-sheet .user-menu-panel {
        left: auto;
        max-height: calc(
            100vh - var(--cx-mobile-bar-height) - var(--cx-bottom-bar-height)
            - env(safe-area-inset-top) - env(safe-area-inset-bottom) - var(--cx-space-5));
        max-width: calc(100vw - (var(--cx-page-pad-x-phone) * 2));
        min-width: 0;
        overflow-y: auto;
        position: fixed;
        right: var(--cx-page-pad-x-phone);
        top: calc(var(--cx-mobile-bar-height) + env(safe-area-inset-top));
        width: min(320px, calc(100vw - (var(--cx-page-pad-x-phone) * 2)));
    }
}

/* ------------------------------------------------------------ capture preview

   The overlay a screenshot lands in before anyone decides what to do with it. It is a dialog
   in the same sense the admin drawer is - `--cx-z-dialog`, a dimmed backdrop, Escape closes -
   and it reuses `app-card` for the surface and `app-button` for every control, so it carries
   no colour of its own. */
.help-capture-overlay {
    align-items: center;
    background: rgba(2, 6, 23, .62);
    display: flex;
    inset: 0;
    justify-content: center;
    padding: var(--cx-space-4, 16px);
    position: fixed;
    z-index: var(--cx-z-dialog);
}

.help-capture-dialog {
    display: grid;
    gap: var(--cx-space-5);
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    width: min(100%, 640px);
}

.help-capture-dialog-narrow { width: min(100%, 420px); }

.help-capture-title {
    font-size: 1rem;
    font-weight: 700;
    margin: 0;
}

.help-capture-subtitle,
.help-capture-origin {
    color: var(--muted);
    font-size: .75rem;
    margin: 0;
}

/* THE VIEWPORT IS THE CROP.

   The picture is the point of the dialog, so it gets the room - capped by height as well as
   width, because a tall portrait capture would otherwise push the three choices off screen,
   and those are the one thing this dialog exists to show.

   `overflow: hidden` is what makes this a crop rather than a zoom: the image is transformed
   inside the box and whatever leaves the box is gone. The submitted PNG is rendered from this
   box's own numbers, so there is no way for the preview and the attachment to disagree.

   `touch-action: none` so a drag on a touch screen pans the picture instead of scrolling the
   dialog underneath it. */
.help-capture-viewport {
    background: var(--surface-2, rgba(255, 255, 255, .04));
    border: 1px solid var(--border);
    border-radius: var(--cx-radius-md, 10px);
    cursor: grab;
    height: 46vh;
    overflow: hidden;
    position: relative;
    touch-action: none;
    width: 100%;
}

.help-capture-viewport:active { cursor: grabbing; }

.help-capture-image {
    display: block;
    height: 100%;
    object-fit: contain;
    /* The transform origin has to be the centre, because the crop maths assumes the visible
       window is centred before the pan is applied. */
    transform-origin: center center;
    user-select: none;
    width: 100%;
}

.help-capture-choose { display: grid; gap: var(--cx-space-2); }

/* Wraps rather than scrolls. Three labels at phone width do not fit on one row, and a row of
   primary actions that runs off the side is a row with a hidden third option. */
.help-capture-intents {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
}

.help-capture-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
    justify-content: flex-end;
}

/* ==========================================================================
   DEPLOYMENT LEDGER - /admin/deployments

   Global, like every other Administration style. The workspace has one stylesheet on purpose:
   a page that brings its own is a page whose spacing drifts from the rest the first time
   anybody adjusts either.
   ========================================================================== */

/* THE PRIVATE/PUBLIC NOTE, AT THE WEIGHT AN EXPLANATION DESERVES.

   It was an `admin-card` with an icon and an h2, which made the caption the largest thing on a
   page whose subject is the history below it. A standing note reads once and then stays out of
   the way - which is what a permanent explanation should do. */
.admin-private-note {
    align-items: baseline;
    background: var(--app-surface-2);
    border-left: 3px solid var(--app-muted);
    border-radius: var(--app-radius-sm);
    display: flex;
    flex-wrap: wrap;
    gap: var(--cx-space-3);
    margin-bottom: var(--cx-space-6);
    padding: var(--cx-space-4) var(--cx-space-5);
}

.admin-private-note p {
    color: var(--app-muted);
    flex: 1 1 320px;
    font-size: var(--cx-text-md);
    line-height: 1.5;
    margin: 0;
}

.admin-private-note-tag {
    color: var(--app-muted);
    font-size: var(--cx-text-sm);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* THE GAP BETWEEN DEPLOYMENTS, WHICH IS WHAT MAKES THEM READ AS SEPARATE EVENTS.

   Set once here rather than as a margin on the card, so it is identical whether the cards
   either side are open or shut - a margin that collapses differently against an expanded body
   is exactly how a list starts looking uneven. */
.admin-deployment-list {
    display: grid;
    gap: var(--cx-space-6);
}

.admin-deployment {
    background: var(--app-surface-2);
    border: 1px solid var(--border);
    border-radius: var(--app-radius-sm);

    /* `overflow: hidden` is what lets the status band below run to the card's corners and be
       cut by its radius rather than squaring them off; `position: relative` is what the band
       is positioned against. */
    overflow: hidden;
    position: relative;
}

/* THE OUTCOME, AS A BAND DOWN THE LEFT EDGE.

   THE PATTERN IS THE ALERT TILE'S, NOT A NEW ONE. `.wpdn-alerts .alert-row::before` in
   card-alerts.css is this product's wide status band: an absolutely positioned ::before at
   7px, pinned top to bottom, clipped by the card's own radius. A `border-left` - the 3px
   variant `.alert-row`, `.cases-thread-entry` and `.admin-private-note` use - reads as a
   border at that width rather than as a deliberate status accent, which is the whole point
   of drawing it on a deployment.

   IT SPANS THE WHOLE CARD, so it is identical whether the card is open or shut: the band is
   on the article, not on the header, and it paints over the body's top rule because it is
   positioned and the rule is not.

   THE COLOURS ARE THE PLATFORM'S SEMANTIC RAMP, deliberately - `--app-success` /
   `--app-warning` / `--app-danger` / `--app-accent`, the same tokens the outcome pill beside
   the version uses. The alert card's ramp is a different, local one; card-alerts.css says so
   itself, and borrowing it here would put two meanings on one colour.

   A ROLLBACK IS WARNING, NOT DANGER. It is the deployment machinery working: the build was
   withdrawn and the environment is back where it was. A FAILED deployment is danger, because
   it stopped somewhere nobody chose. Drawing both red would say the safe outcome and the
   unsafe one are the same thing. */
.admin-deployment::before {
    background: var(--deployment-tone, var(--app-border));
    bottom: 0;
    content: "";
    left: 0;
    position: absolute;
    top: 0;
    width: 7px;
}

.admin-deployment.is-succeeded   { --deployment-tone: var(--app-success); }
.admin-deployment.is-rolled-back { --deployment-tone: var(--app-warning); }
.admin-deployment.is-failed      { --deployment-tone: var(--app-danger); }
.admin-deployment.is-running     { --deployment-tone: var(--app-accent); }

/* The whole header is the toggle, so it is a real button - keyboard, focus ring and
   aria-expanded come with that and do not have to be rebuilt. */
.admin-deployment-header {
    align-items: center;
    background: none;
    border: 0;
    color: inherit;
    cursor: pointer;
    display: flex;
    flex-wrap: wrap;
    font: inherit;
    gap: var(--cx-space-2) var(--cx-space-5);
    padding: var(--cx-space-5) var(--cx-space-6);
    text-align: left;
    width: 100%;
}

.admin-deployment-header:hover {
    background: var(--app-surface-3);
}

.admin-deployment-chevron {
    align-items: center;
    color: var(--app-muted);
    display: inline-flex;
    flex: 0 0 auto;
}

.admin-deployment-chevron svg {
    fill: none;
    height: 16px;
    stroke: currentColor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2;
    transition: transform .15s ease;
    width: 16px;
}

.admin-deployment.is-open .admin-deployment-chevron svg {
    transform: rotate(90deg);
}

.admin-deployment-identity {
    align-items: baseline;
    display: flex;
    flex: 0 0 auto;
    gap: var(--cx-space-3);
}

.admin-deployment-version {
    font-size: var(--cx-text-lg);
    font-weight: 700;
}

/* The collapsed summary: environment, when, the commit range, migrations, how much it carried.
   Wraps as a whole rather than per word, so a narrow window stacks facts instead of breaking
   one across two lines. */
.admin-deployment-facts {
    align-items: baseline;
    color: var(--app-muted);
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    font-size: var(--cx-text-md);
    gap: var(--cx-space-2);
}

/* The one group that must never break: `049c2b9 -> a1b2c3d` is a range, and a range split
   across two lines is two commits nobody reads as related. */
.admin-deployment-range {
    align-items: baseline;
    display: inline-flex;
    gap: var(--cx-space-2);
    white-space: nowrap;
}

.admin-deployment-body {
    border-top: 1px solid var(--border);
    padding: var(--cx-space-5) var(--cx-space-6) var(--cx-space-6);
}

.admin-deployment-body > * + * {
    margin-top: var(--cx-space-5);
}

/* A case number that opens the case. A button rather than a link, because it opens a drawer on
   this page and does not navigate - and it is styled to look like what it does. */
.admin-case-open {
    background: none;
    border: 0;
    color: var(--app-accent);
    cursor: pointer;
    font: inherit;
    padding: 0;
    text-align: left;
}

.admin-case-open:hover {
    text-decoration: underline;
}

/* Titles are the long column and the only one worth wrapping; everything else in an
   `admin-table` stays on one line. */
.admin-table td .admin-case-open {
    white-space: normal;
}
