/* Viewer layout (§17, Phase 4C design pass) — self-hosted, no CDN.
   Camera tiles stay visually dominant: understated chrome, maximal video area,
   dark neutral tile backgrounds from the theme tokens. */

.viewer {
    padding: 0.9rem 1rem 1.25rem;
    min-height: calc(100vh - 3.5rem);
    box-sizing: border-box;
}

.viewer__header {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    margin: 0 0 0.75rem;
}

.viewer__header h1 {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 650;
}

.viewer__header p {
    margin: 0;
    color: var(--text-secondary);
    font-size: 0.85rem;
}

.viewer__empty {
    margin: 2rem auto 0;
    max-width: 30rem;
    padding: 1.1rem 1.3rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text-secondary);
    line-height: 1.55;
}

.viewer__empty strong {
    display: block;
    color: var(--text-primary);
    margin-bottom: 0.25rem;
}

.viewer__empty span { display: block; }

.viewer__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.6rem;
    /* Stacking context + positioning anchor for the absolutely-positioned
       Fisk/min overlay and the focused-camera layer. */
    position: relative;
}

/* --- Adaptive sparse-view presentation (§6). A view with only ONE or TWO
   playable cameras uses a full-area layout instead of the configured grid, so a
   lone feed is not stranded in the upper-left quarter and two feeds are not
   pillarboxed on a widescreen display. Scoped to the .viewer--adaptive main + the
   --single/--dual grid so the selector, empty, denial and 3+-camera configured
   views are never affected. */
.viewer--adaptive {
    display: flex;
    flex-direction: column;
    /* Fill the viewport below the app header (2.5rem min-height + 2×0.5rem
       padding ≈ 3.5rem, the same allocation the non-adaptive .viewer reserves).
       The viewer header then takes its natural height and the grid flexes into
       the rest. dvh tracks the mobile dynamic viewport. */
    height: calc(100dvh - 3.5rem);
    min-height: 0;
    /* Size container so the Dual layout can flip orientation from the ACTUAL
       available viewer-area shape (never a generic pixel breakpoint). In browser
       fullscreen the grid child fills the screen while dvh/width track the
       fullscreen viewport, so this container resizes with it and the aspect-ratio
       decision stays correct. */
    container-type: size;
    container-name: viewer-area;
}

.viewer--adaptive .viewer__grid {
    /* Take the remaining height after the viewer header; min-height:0 lets the
       grid shrink so its rows resolve against the flex height, not the videos'
       intrinsic 16:9 size. */
    flex: 1 1 auto;
    min-height: 0;
}

.viewer__grid--single {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr);
}

/* Single tile fills the whole viewer area; the video is letter-/pillar-boxed. */
.viewer__grid--single .tile {
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    min-width: 0;
    min-height: 0;
}

.viewer__grid--single .tile__video {
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    aspect-ratio: auto;
    object-fit: contain;
}

/* Two cameras, WIDE/landscape default: side by side, each tile sized like ONE
   tile of the four-camera 2×2 grid (a 16:9 video at half width) instead of the
   full viewer height, so a Dual tile is no longer unusually tall. The single
   content-height row is CENTERED vertically (align-content) in the taller
   adaptive area. */
.viewer__grid--dual {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: min-content;
    align-content: center;
}

/* Content-height tile: a 16:9 video row + a caption row (both sized to content),
   so the tile matches a 2×2 tile rather than stretching to the full row. */
.viewer__grid--dual .tile {
    display: grid;
    grid-template-rows: auto auto;
    min-width: 0;
}

.viewer__grid--dual .tile__video {
    width: 100%;
    height: auto;
    min-width: 0;
    /* Same height as a four-camera 2×2 tile video (half width × 9/16). */
    aspect-ratio: 16 / 9;
    object-fit: contain;
}

/* NARROW/portrait viewer area: stack the two cameras vertically (one column, two
   equal FILLING rows). Primary decision — a container aspect-ratio query on the
   real viewer area, so it is correct both below the app header and in fullscreen. */
@container viewer-area (max-aspect-ratio: 4 / 3) {
    .viewer__grid--dual {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: repeat(2, minmax(0, 1fr));
        grid-auto-rows: auto;
        align-content: stretch;
    }

    .viewer__grid--dual .tile {
        grid-template-rows: minmax(0, 1fr) auto;
    }

    .viewer__grid--dual .tile__video {
        height: 100%;
        min-height: 0;
        aspect-ratio: auto;
    }

    /* Narrow fullscreen: captions hidden (below), so the video fills its row and
       the two rows divide the usable height evenly. */
    .viewer__grid--dual:fullscreen .tile {
        grid-template-rows: minmax(0, 1fr);
    }
}

/* Fallback for browsers without size container queries: same narrow decision from
   the viewport aspect ratio. Still an aspect-ratio decision (never a fixed px
   breakpoint), gated by @supports so it never double-controls the orientation. */
@supports not (container-type: size) {
    @media (max-aspect-ratio: 4 / 3) {
        .viewer__grid--dual {
            grid-template-columns: minmax(0, 1fr);
            grid-template-rows: repeat(2, minmax(0, 1fr));
            grid-auto-rows: auto;
            align-content: stretch;
        }

        .viewer__grid--dual .tile {
            grid-template-rows: minmax(0, 1fr) auto;
        }

        .viewer__grid--dual .tile__video {
            height: 100%;
            min-height: 0;
            aspect-ratio: auto;
        }

        .viewer__grid--dual:fullscreen .tile {
            grid-template-rows: minmax(0, 1fr);
        }
    }
}

/* A focused camera (either orientation) fills the WHOLE adaptive viewer: override
   the dual grid to a single full-height cell so the focused tile spans it and the
   video fills (the shared .tile--focused rules keep object-fit: contain and the
   caption row). Applied to every dual tile so the hidden background tile overlaps
   instead of pushing an implicit track that would shrink the focused camera. */
.viewer__grid--dual.viewer__grid--camera-focused {
    grid-template-rows: minmax(0, 1fr);
    grid-auto-rows: minmax(0, 1fr);
    align-content: stretch;
}

.viewer__grid--dual.viewer__grid--camera-focused .tile {
    grid-column: 1 / -1;
    grid-row: 1 / -1;
    grid-template-rows: minmax(0, 1fr) auto;
}

/* --- Browser fullscreen (#viewer-grid), ADAPTIVE views only. Hide the whole
   caption bar consistently with the configured four-camera fullscreen so it
   reserves no height, and let the video use the full tile. Scoped to
   --single/--dual so the configured 3+/four-camera fullscreen presentation is
   never touched, and so the Fisk/min overlay (not a .tile__caption) stays visible. */
.viewer__grid--single:fullscreen .tile__caption,
.viewer__grid--dual:fullscreen .tile__caption {
    display: none;
}

/* Single fullscreen: a video-only tile filling the whole screen. (Wide Dual keeps
   its 16:9 content tiles centered — matching the four-camera fullscreen tile size
   — because its caption row simply collapses when hidden.) */
.viewer__grid--single:fullscreen .tile {
    grid-template-rows: minmax(0, 1fr);
}

/* View selector — shown when a user has several accessible views. */
.viewpick {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1rem;
    margin-top: 0.5rem;
}

.viewpick__card {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 1.1rem 1.25rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text-primary);
    text-decoration: none;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.viewpick__card:hover,
.viewpick__card:focus-visible {
    border-color: var(--accent);
    background: var(--bg-elevated);
    color: var(--text-primary);
}

.viewpick__name {
    font-size: 1.05rem;
    font-weight: 600;
}

.viewpick__meta {
    font-size: 0.8rem;
    color: var(--text-secondary);
}

@media (max-width: 720px) {
    /* Only the CONFIGURED grid (3+ cameras) collapses to one column on narrow
       screens. The adaptive single/dual layouts are handled entirely by their own
       CSS: single is one cell, and dual's orientation is decided by the viewer-
       area aspect-ratio query (portrait → stacked), so they must NOT be reset here
       — doing so would fight the responsive dual orientation and drop the focused-
       camera span. */
    .viewer__grid:not(.viewer__grid--single):not(.viewer__grid--dual) {
        grid-template-columns: 1fr !important;
    }

    /* Configured tiles carry inline grid-column/grid-row placement for the desktop
       2x2 layout (Viewer.razor). Reset it here so a single implicit column is used
       instead of the desktop 2-track placement collapsing tiles. Adaptive grids
       are excluded so their CSS placement is preserved. */
    .viewer__grid:not(.viewer__grid--single):not(.viewer__grid--dual) > .tile {
        grid-column: auto !important;
        grid-row: auto !important;
    }
}

.tile {
    margin: 0;
    background: var(--bg-video-tile);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    position: relative;
    z-index: 1;
}

.tile__video {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: var(--bg-video-tile);
}

.tile__caption {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 0.35rem 0.65rem;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.tile__caption code {
    color: var(--accent);
    font-size: 0.75rem;
}

.tile__meta {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.tile__fullscreen {
    background: none;
    border: 0;
    color: var(--text-faint);
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
    padding: 0 0.2rem;
    line-height: 1;
}

.tile__fullscreen:hover { color: var(--text-primary); }

/* Per-tile state badge — unobtrusive, in the caption bar, never covering the
   camera image (§17 surveillance reliability). Status is text + color, never
   color alone (accessibility). */
.tile__status {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    white-space: nowrap;
}

.tile__status--connecting {
    color: var(--text-secondary);
    background: var(--bg-inset);
}

.tile__status--live {
    color: var(--success);
    background: var(--success-bg);
}

.tile__status--reconnecting {
    color: var(--warning);
    background: var(--warning-bg);
}

.tile__status--offline {
    color: var(--danger);
    background: var(--danger-bg);
}

/* --- Camera detail / focus mode (§6). Pure CSS layering over viewer state —
   the same <video> element stays attached (no HLS reconnect). The focused tile
   fills the grid; other tiles stay in the DOM (video keeps playing) but hidden. */
.tile__focus {
    background: none;
    border: 0;
    color: var(--text-faint);
    cursor: pointer;
    font: inherit;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
    white-space: nowrap;
    z-index: 30;
}

.tile__focus:hover,
.tile__focus:focus-visible { color: var(--text-primary); }

/* Focused camera fills the grid via GRID SPANNING — it stays in normal grid
   flow (position: relative), so a one-camera grid never collapses when its only
   tile is focused (in-page or in browser fullscreen). The tile's placement (a
   POSITIVE column/row span covering every configured camera row, including
   implicit rows) is set inline in Viewer.razor from the view's calculated
   extent; a negative line index (1 / -1) would not reach implicit rows. An
   explicit video row + caption row keeps the camera name, connection status and
   «Tilbake til rutenett» control visible instead of being clipped by overflow. */
.viewer__grid--camera-focused .tile--focused {
    position: relative;
    z-index: 20;
    border-color: var(--accent);
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
}

.tile--focused .tile__video {
    width: 100%;
    height: 100%;
    min-height: 0;
    aspect-ratio: auto;
    object-fit: contain;
}

.tile--background {
    /* Kept in the DOM so playback/token refresh continue; just not visible. */
    visibility: hidden;
}

/* --- Fisk/min overlay (§4). A direct child of #viewer-grid, absolutely
   positioned so it never occupies a grid cell nor affects the calculated
   columns/rows. Always above cameras and a focused camera (z-index 100), and
   automatically visible when #viewer-grid enters browser fullscreen. The box is
   non-interactive except for its drag/reset controls, so ordinary camera
   interaction outside it is never blocked. */
.viewer__fish-overlay {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 100;
    pointer-events: none;
    touch-action: none;
    /* User resize (§4): ONLY the Fisk/min data typography grows (via
       --fish-data-scale, applied to the compact value/unit/secondary). The
       overlay box itself is NEVER visually scaled — it grows naturally to contain
       the larger numbers while padding, border, controls and footer stay fixed.
       JS sets --fish-data-scale (100 %..300 %) and reduces it only so the box
       still fits the grid. At scale 1 the overlay is identical to production. */
    --fish-data-scale: 1;
}

.viewer__fish-overlay-box {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.55rem 0.75rem;
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: var(--radius-sm);
    background: rgba(10, 14, 22, 0.62);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
    color: #f5f7fa;
}

@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .viewer__fish-overlay-box {
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px);
        background: rgba(10, 14, 22, 0.5);
    }
}

.viewer__fish-overlay-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    /* Leave the extreme lower-right corner clear for the resize handle. */
    padding-right: 0.85rem;
    /* Keep the controls block at its original wrapped width and left-aligned so
       the box's 100 % width is unchanged AND the controls never stretch or change
       spacing as the enlarged data widens the box. 13.375rem (border-box) == the
       former 15rem overlay cap minus the box padding (2×0.75rem) and border
       (2×1px), reproducing the original reset-text wrapping exactly. */
    box-sizing: border-box;
    max-width: 13.375rem;
    align-self: flex-start;
    z-index: 101;
    pointer-events: auto;
}

.viewer__fish-overlay-handle {
    pointer-events: auto;
    touch-action: none;
    cursor: grab;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 999px;
    color: #dfe4ea;
    font: inherit;
    font-size: 0.68rem;
    font-weight: 600;
    padding: 0.1rem 0.5rem;
    letter-spacing: 0.02em;
}

.viewer__fish-overlay-handle:active { cursor: grabbing; }

.viewer__fish-overlay-reset {
    pointer-events: auto;
    background: none;
    border: 0;
    color: #aeb6c2;
    cursor: pointer;
    font: inherit;
    font-size: 0.66rem;
    padding: 0.1rem 0.2rem;
    text-decoration: underline;
}

.viewer__fish-overlay-reset:hover,
.viewer__fish-overlay-reset:focus-visible { color: #fff; }

/* Lower-right resize grip. A generous, touch-friendly hit area (the whole
   button) with a small, unobtrusive diagonal grip drawn in the corner. Sits on
   the always-dark overlay box, so its neutral translucent white lines suit both
   the dark and light app themes and never clash with customer branding. */
.viewer__fish-overlay-resize {
    position: absolute;
    right: -0.1rem;
    bottom: -0.1rem;
    width: 1.6rem;
    height: 1.6rem;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    cursor: nwse-resize;
    pointer-events: auto;
    touch-action: none;
    z-index: 101;
}

.viewer__fish-overlay-resize::before {
    content: "";
    position: absolute;
    right: 0.28rem;
    bottom: 0.28rem;
    width: 0.68rem;
    height: 0.68rem;
    border-right: 2px solid rgba(255, 255, 255, 0.55);
    border-bottom: 2px solid rgba(255, 255, 255, 0.55);
    border-bottom-right-radius: 3px;
}

.viewer__fish-overlay-resize:hover::before,
.viewer__fish-overlay-resize:focus-visible::before {
    border-right-color: rgba(255, 255, 255, 0.9);
    border-bottom-color: rgba(255, 255, 255, 0.9);
}

/* --- Fish-counter tile (§4): full and compact overlay presentations. The
   value area reserves space so healthy/stale updates never shift layout. */
.fishtile {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 0.35rem;
    width: 100%;
    aspect-ratio: 16 / 9;
    padding: 0.75rem 1rem;
    text-align: center;
    color: var(--text-primary);
}

/* Compact overlay: dense, left-aligned, no fixed aspect ratio. */
.fishtile--compact {
    aspect-ratio: auto;
    width: auto;
    padding: 0;
    gap: 0.15rem;
    align-items: flex-start;
    text-align: left;
    color: inherit;
}

.fishtile__primary {
    display: flex;
    flex-direction: column;
    align-items: center;
    line-height: 1;
}

.fishtile--compact .fishtile__primary {
    flex-direction: row;
    align-items: baseline;
    gap: 0.4rem;
}

.fishtile__value {
    font-size: clamp(2.5rem, 8vw, 5rem);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
    color: var(--accent);
}

.fishtile--compact .fishtile__value {
    /* Data scales with the user's resize preference; 1.9rem is the production
       baseline at scale 1. */
    font-size: calc(1.9rem * var(--fish-data-scale, 1));
    line-height: 1;
}

.fishtile__unit {
    margin-top: 0.25rem;
    font-size: 0.9rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.fishtile--compact .fishtile__unit {
    margin-top: 0;
    font-size: calc(0.72rem * var(--fish-data-scale, 1));
    color: #c4ccd6;
}

.fishtile__secondary {
    font-size: 0.9rem;
    color: var(--text-secondary);
}

.fishtile--compact .fishtile__secondary {
    font-size: calc(0.74rem * var(--fish-data-scale, 1));
    color: #c4ccd6;
    font-variant-numeric: tabular-nums;
}

.fishtile__footer {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.72rem;
    color: var(--text-faint);
}

.fishtile--compact .fishtile__footer {
    gap: 0.4rem;
    font-size: 0.66rem;
    color: #aeb6c2;
}

.fishtile__badge {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    white-space: nowrap;
}

.fishtile--compact .fishtile__badge { font-size: 0.62rem; padding: 0.05rem 0.4rem; }

.fishtile__badge--live {
    color: var(--success);
    background: var(--success-bg);
}

.fishtile__badge--stale {
    color: var(--warning);
    background: var(--warning-bg);
}

.fishtile__status {
    font-size: 1rem;
    font-weight: 600;
}

.fishtile--compact .fishtile__status { font-size: 0.78rem; }

.fishtile__status--muted { color: var(--text-secondary); }
.fishtile__status--down { color: var(--danger); }

.fishtile--compact .fishtile__status--muted { color: #c4ccd6; }

/* Narrow screens: keep the overlay readable and draggable but smaller. The
   compact value uses a smaller 1.5rem baseline here, still multiplied by the
   same data scale so 100 % is unchanged on narrow screens. */
@media (max-width: 720px) {
    .fishtile--compact .fishtile__value { font-size: calc(1.5rem * var(--fish-data-scale, 1)); }
}
