/* ====================================================================
   WEATHER DASHBOARD (/weather) — v4.
   All Sky Camera sits on its own, always visible, and collapses to a
   single text line when the feed is unavailable. Below it, one shared
   "wall" grid holds every other widget/image; a 預報(forecast) /
   觀測(observation) toggle in the top ticker line just reorders the
   same tiles via [data-mode] + CSS `order` — every widget/iframe
   exists exactly once in the DOM, only its position changes.

   Every tile's caption is a solid header bar that sits ABOVE its
   image/iframe (never overlaps it). Every tile is portrait
   (aspect-ratio taller than wide) to match the CWA imagery's own
   proportions, except the meteogram widget (a horizontal timeline
   chart, not a photo - needs to stay wide/landscape to read, and per
   spec keeps its double-width "2br" size in both modes) and the
   All Sky Camera (a wide fisheye feed, landscape).

   Only base.css tokens/reset and the sidebar shell are shared with the
   rest of the app.
   ==================================================================== */

.wx-page {
    max-width: 1760px;
    width: 100%;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* All Sky sits BELOW the wall now - reordered via flex `order` rather than
   moving it in the DOM (topbar stays first, then wall, then all sky, then
   the meteoblue credit line). */
.wx-topbar { order: 1; }
.wx-wall { order: 2; }
.wx-allsky { order: 3; }
.wx-fcredit { order: 4; }

/* ==================== TOPBAR (slim, text-first) ==================== */
.wx-topbar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding-bottom: 6px;
}

.wx-title-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.wx-eyebrow {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 2px;
    color: var(--secondary-purple);
}

.wx-topbar-main h1 {
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1;
}

.wx-ticker {
    margin-top: 6px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 3px 12px;
}

.wx-tick {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.78rem;
    color: var(--text-secondary);
    white-space: nowrap;
}

.wx-tick svg {
    color: var(--text-faint);
    flex-shrink: 0;
}

.wx-tick b {
    font-weight: 600;
    color: var(--text-primary);
}

.wx-tick-sep {
    width: 1px;
    height: 12px;
    background: var(--border-hairline-strong);
}

.wx-tick-muted {
    color: var(--text-muted);
}

/* ==================== MODE TOGGLE (預報 / 觀測) ==================== */
.wx-mode-toggle {
    display: inline-flex;
    align-items: center;
    padding: 2px;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--border-hairline);
    gap: 2px;
}

.wx-mode-btn {
    border: none;
    background: transparent;
    color: var(--text-muted);
    font-family: var(--font-sans);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.3px;
    padding: 3px 12px;
    border-radius: 999px;
    cursor: pointer;
    transition: var(--transition-fast);
}

.wx-mode-btn:hover {
    color: var(--text-primary);
}

.wx-mode-btn.active {
    background: var(--primary-gradient);
    color: #fff;
}

.wx-topbar-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.wx-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: 999px;
    border: 1px solid var(--border-hairline);
    background: var(--surface-2);
    color: var(--text-secondary);
    font-family: var(--font-sans);
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    transition: var(--transition-fast);
}

.wx-btn:hover {
    background: var(--surface-3);
    color: var(--text-primary);
    border-color: var(--border-hairline-strong);
}

.wx-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* ==================== ALERT PILL ==================== */
.wx-alert-pill {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    background: rgba(240, 184, 79, 0.14);
    border: 1px solid rgba(240, 184, 79, 0.4);
    color: #f0b84f;
    font-weight: 500;
    font-size: 0.78rem;
}

.wx-alert-pill.high-alert {
    background: rgba(239, 90, 90, 0.16);
    border-color: rgba(239, 90, 90, 0.45);
    color: #ff8a8a;
}

.wx-alert-icon {
    font-size: 1em;
}

/* ==================== PROGRESS ==================== */
.wx-progress {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 140px;
}

.wx-progress-track {
    flex: 1;
    height: 4px;
    background: var(--surface-3);
    border-radius: 4px;
    overflow: hidden;
}

.wx-progress-fill {
    height: 100%;
    width: 0%;
    background: var(--primary-gradient);
    border-radius: 4px;
    transition: width 0.3s ease;
}

.wx-progress-label {
    font-size: 0.72rem;
    color: var(--text-secondary);
    font-weight: 500;
    min-width: 28px;
    text-align: right;
}

/* ==================== SHARED CAPTION BAR ====================
   A solid, non-overlapping header strip - used by both the all-sky
   section and every wall tile - so the label never sits on top of
   (and never obscures) the image/iframe beneath it. */
.wx-tile-caption-meta {
    font-weight: 400;
    letter-spacing: 0.3px;
    color: var(--text-faint);
}

/* Green = fresh frame within the last capture cycle (genuinely live).
   .is-warn (amber) = no new frame in a while but the last one still shows.
   .is-down (red) = the last capture attempt actually failed. Red used to be
   the *default* here regardless of status, which read as "something's
   wrong" even when everything was fine - it's now only red when it is. */
.wx-live-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #3ddc73;
    box-shadow: 0 0 6px #3ddc73;
    animation: wx-live-pulse 2s ease-in-out infinite;
    flex-shrink: 0;
}

.wx-live-dot.is-warn {
    background: #ffb020;
    box-shadow: 0 0 6px #ffb020;
}

.wx-live-dot.is-down {
    background: #ff5a5a;
    box-shadow: 0 0 6px #ff5a5a;
}

@keyframes wx-live-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

/* ==================== ALL SKY CAMERA (own section, always visible) ==================== */
.wx-allsky {
    display: flex;
    flex-direction: column;
    aspect-ratio: 16 / 9;
    border-radius: 8px;
    overflow: hidden;
    background: var(--surface-sunken);
    border: 1px solid var(--border-hairline);
    transition: border-color 0.15s ease;
}

.wx-allsky:hover {
    border-color: var(--border-accent);
}

.wx-allsky-caption {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 12px;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 1.2px;
    color: var(--text-primary);
    background: var(--surface-2);
    border-bottom: 1px solid var(--border-hairline);
}

.wx-allsky-media {
    flex: 1;
    min-height: 0;
    overflow: hidden;
    display: flex;
}

.wx-allsky-media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    cursor: zoom-in;
}

/* Unavailable (admin-disabled, or the currently-selected source hasn't
   produced a frame in a while): collapse the image area to a single slim
   text line. The stale image is dropped - an hours-old sky is worse than
   none when the point is seeing the weather right now - but the caption
   bar (and its source toggle) stays visible so you can still switch to the
   other source without it also being hidden. */
.wx-allsky-offline {
    display: none;
}

.wx-allsky.is-offline {
    aspect-ratio: auto;
}

.wx-allsky.is-offline .wx-allsky-media {
    display: none;
}

.wx-allsky.is-offline .wx-allsky-offline {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 12px 14px;
    color: var(--text-muted);
    font-size: 0.8rem;
}

/* Source toggle (Stacked / Stream) - both are captured server-side at all
   times, this only picks which file is displayed. Each button carries its
   own small dot so both sources' live status is visible regardless of
   which one is currently selected. */
.wx-allsky-source-toggle {
    display: flex;
    gap: 4px;
    margin-left: auto;
}

.wx-allsky-source-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid var(--border-hairline);
    background: transparent;
    color: var(--text-faint);
    font-family: var(--font-mono);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.6px;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.wx-allsky-source-btn:hover {
    color: var(--text-secondary);
    border-color: var(--border-accent);
}

.wx-allsky-source-btn.active {
    background: var(--surface-sunken);
    color: var(--text-primary);
    border-color: var(--border-accent);
}

.wx-source-dot {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--text-faint);
    flex-shrink: 0;
}

.wx-source-dot.is-up {
    background: #3ddc73;
    box-shadow: 0 0 4px #3ddc73;
}

.wx-source-dot.is-down {
    background: #ff5a5a;
}

/* Which RTSP source the current frame came from (Stacked / Stream) - the
   backend silently falls back stacked -> stream, so this makes it visible
   which one is actually feeding the view. */
#allsky-source:not(:empty) {
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--surface-sunken);
    border: 1px solid var(--border-hairline);
    font-size: 0.62rem;
    letter-spacing: 0.6px;
}

/* ==================== TOGGLE WALL ====================
   Mobile-first single column - one tile per row, full width. Widens
   to a 12-column (720-1440px) then 8-column (>1440px) grid at larger
   breakpoints below. Mode only changes `order`. */
.wx-wall {
    display: grid;
    grid-template-columns: 1fr;
    gap: 8px;
}

.wx-tile {
    grid-column: span 1;
    width: 100%;
    aspect-ratio: 3 / 4;
    display: flex;
    flex-direction: column;
    border-radius: 8px;
    overflow: hidden;
    background: var(--surface-sunken);
    border: 1px solid var(--border-hairline);
    transition: border-color 0.15s ease;
}

.t-meteogram {
    grid-column: span 1;
    aspect-ratio: 3 / 2;
    min-height: 700px;
}

/* The "three" widget has no autowidth param (unlike the other meteoblue
   embeds), so it doesn't reflow to a narrower box - it needs close to
   its native ~460px design width or its 4th day/hourly row clips off
   the edge. Give it the same double-width treatment as meteogram at
   720px+; aspect-ratio is its own (~4:5, close to the widget's native
   460x564) rather than matching row-mates exactly, since forcing it
   short would clip the bottom instead of the side. */
.t-threeday {
    grid-column: span 1;
    aspect-ratio: 4 / 5;
}

.wx-tile:hover {
    border-color: var(--border-accent);
}

.wx-tile-caption {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 9px;
    font-family: var(--font-mono);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 1px;
    color: var(--text-secondary);
    background: var(--surface-2);
    border-bottom: 1px solid var(--border-hairline);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.wx-tile-media {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow: hidden;
    display: flex;
}

.wx-tile-media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    cursor: zoom-in;
    transition: transform 0.4s ease, filter 0.4s ease;
}

.wx-tile:hover .wx-tile-media img {
    transform: scale(1.035);
    filter: brightness(1.06);
}

.wx-tile-iframe {
    width: 100%;
    height: 100%;
    flex: 1;
    border: 0;
}

.wx-seeing-wrap {
    background: #fff;
}

.wx-seeing-wrap iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

/* Mode ordering - every tile is the SAME size in both modes now (see
   the >=720px block below): one uniform width for map/seeing/all CWA
   tiles, double that for meteogram. Giving map/seeing the same width
   as everything else (instead of a narrower "featured" width) is what
   makes forecast mode's tiles tall enough - a narrower tile means a
   shorter tile once the portrait aspect-ratio is applied.
   Only `order` differs per mode, chosen so every row's spans add up to
   exactly 12 with no leftover gaps (see the math in the media query
   comment below). Because map+meteogram alone already sum to 12,
   "seeing" starts the next row rather than sharing a row with them -
   that's the trade-off for keeping every tile uniformly wide/tall. */
[data-mode="forecast"] .t-map { order: 1; }
[data-mode="forecast"] .t-meteogram { order: 2; }
[data-mode="forecast"] .t-seeing { order: 3; }
[data-mode="forecast"] .t-threeday { order: 4; }
[data-mode="forecast"] .t-rainradar { order: 5; }
[data-mode="forecast"] .t-radar { order: 6; }
[data-mode="forecast"] .t-cloudreal { order: 7; }
[data-mode="forecast"] .t-cloudir { order: 8; }
[data-mode="forecast"] .t-rainprec { order: 9; }
[data-mode="forecast"] .t-rainpred1 { order: 10; }
[data-mode="forecast"] .t-rainpred2 { order: 11; }

[data-mode="observation"] .t-rainradar { order: 1; }
[data-mode="observation"] .t-radar { order: 2; }
[data-mode="observation"] .t-cloudreal { order: 3; }
[data-mode="observation"] .t-cloudir { order: 4; }
[data-mode="observation"] .t-map { order: 5; }
[data-mode="observation"] .t-seeing { order: 6; }
[data-mode="observation"] .t-meteogram { order: 7; }
[data-mode="observation"] .t-rainprec { order: 8; }
[data-mode="observation"] .t-threeday { order: 9; }
[data-mode="observation"] .t-rainpred1 { order: 10; }
[data-mode="observation"] .t-rainpred2 { order: 11; }

/* 720px-1440px only: 12-column base, capped at 3 tiles per row, with
   a taller portrait ratio + a 500px height floor (narrow/medium
   screens need the extra help to not look cramped). Every tile spans
   4/12 (33%); meteogram and the three-day widget always span 8/12
   (double) in both modes - both need the extra width to render
   without clipping.
   With the order above, spans pack into clean rows with no gaps:
     forecast:    [map4 meteogram8] [seeing4 threeday8]
                  [rainradar4 radar4 cloudreal4] [cloudir4 rainprec4 rainpred1_4]
                  [rainpred2_4]
     observation: [rainradar4 radar4 cloudreal4] [cloudir4 map4 seeing4]
                  [meteogram8 rainprec4] [threeday8 rainpred1_4] [rainpred2_4] */
@media (min-width: 720px) and (max-width: 1440px) {
    .wx-wall {
        grid-template-columns: repeat(12, 1fr);
    }

    .wx-tile {
        grid-column: span 4;
        aspect-ratio: 3 / 5;
        min-height: 500px;
    }

    .t-meteogram {
        grid-column: span 8;
        aspect-ratio: 3 / 2;
        min-height: 500px;
    }

    .t-threeday {
        grid-column: span 8;
        aspect-ratio: 4 / 5;
    }
}

/* >1440px: back to the original 8-column base, 4 tiles per row, the
   default 3/4 portrait ratio and no height floor (plenty of width at
   this size to keep things tall without forcing it). Every tile spans
   2/8 (25%); meteogram and the three-day widget span 4/8 (double).
   Same order as above packs naturally with no gaps:
     forecast:    [map2 meteogram4 seeing2] [threeday4 rainradar2 radar2]
                  [cloudreal2 cloudir2 rainprec2 rainpred1_2] [rainpred2_2]
     observation: [rainradar2 radar2 cloudreal2 cloudir2] [map2 seeing2 meteogram4]
                  [rainprec2 threeday4 rainpred1_2] [rainpred2_2] */
@media (min-width: 1441px) {
    .wx-wall {
        grid-template-columns: repeat(8, 1fr);
    }

    .wx-tile {
        grid-column: span 2;
    }

    .t-meteogram {
        grid-column: span 4;
        min-height: 0;
    }

    .t-threeday {
        grid-column: span 4;
    }
}

/* ==================== SHARED CREDIT LINE ==================== */
.wx-fcredit {
    text-align: center;
}

.wx-fcredit a {
    color: var(--text-faint);
    text-decoration: none;
    font-size: 0.7em;
}

/* ==================== RESPONSIVE ==================== */
@media (max-width: 900px) {
    .wx-topbar {
        padding-left: 46px;
    }

    .wx-topbar-actions {
        width: 100%;
        justify-content: space-between;
    }
}
