/* Browse module styles. Sits on top of /common/css/dl.css and
   /common/css/dl-form.css. Channel colors alias the shared chart
   palette in dl.css so every module's charts stay in sync; the
   canvas drawing in dive-chart.js reads these via getComputedStyle. */

:root {
    --ch-depth: var(--chart-bar);       /* primary axis on the profile */
    --ch-temp:  var(--chart-temp);
    --ch-press: var(--chart-press);
    --ch-sac:   var(--chart-13);        /* neutral grey: --chart-sac = depth, colours are taken/mean things */
    --ch-ppo2:  var(--chart-ppo2);
    --ch-sp:    var(--chart-setpoint);
    --ch-cns:   var(--chart-cns);
    --ch-hr:    var(--chart-hr);
    --ch-tts:   var(--chart-tts);
    --ch-gf99:  var(--chart-gf99);
    --ch-ceil:  var(--chart-ceil);
    --ch-tiss:  var(--chart-tiss);
    --ch-event: var(--chart-event);
    --ch-photo: var(--chart-photo);
    --ch-bookmark: var(--chart-bookmark);
}

/* (List-page rules moved to the "03 Browse list redesign" block at the end.) */

/* Star colours: the dive header uses DL.stars (common/css/dl-stars.css,
   lazy-loaded); the list's own rating buttons are styled in the list block. */

/* ── Detail page ───────────────────────────────────────────────── */

/* Cards used throughout the detail page. */
.stats-card,
.chart-card {
    background: var(--bg-card);
    border-radius: var(--radius-md);
    padding: 16px;
    margin-top: 16px;
}
.stats-card h3,
.chart-card h3 {
    margin: 0 0 10px;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.7;
}

/* Section-level footnote under a stats card (e.g. the no-fly / desat
   estimate disclaimer). Muted and small so it reads as a caption. */
.stats-card-note {
    margin-top: 0.5rem;
    margin-bottom: 0;
    font-size: 12px;
    color: var(--text-muted);
}

/* Computer card — vendor logo + vendor/model text. Logo sits in a
   neutral white chip so transparent SVG/PNG logos stay visible on
   dark themes; rounded corners match the rest of the chrome. The
   initials placeholder (rendered when no asset is bundled) reuses
   the same chip dimensions so the card height doesn't jump when a
   logo lands or 404s. */
.computer-card-body {
    display: flex;
    align-items: center;
    gap: 18px;
}
.computer-media {
    flex: 0 0 96px;
    width: 96px;
    height: 96px;
    border-radius: 14px;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0,0,0,0.18) inset;
}
.computer-logo {
    max-width: 80%;
    max-height: 80%;
    object-fit: contain;
}
/* Manual badge is a full-bleed tile (its own rounded background and
   inner padding), so fill the chip edge-to-edge — no white frame. */
.computer-logo.is-manual {
    max-width: 100%;
    max-height: 100%;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.computer-initials {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-weight: 700;
    font-size: 30px;
    letter-spacing: 0.04em;
    /* The chip's --bg-card matched white above; reset for the
       initials variant so the HSL fill from JS shows through. */
    background: transparent;
}
.computer-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.computer-vendor {
    font-size: 18px;
    font-weight: 600;
    color: var(--text);
    line-height: 1.2;
}
.computer-product {
    font-size: 14px;
    color: var(--text-muted);
    line-height: 1.2;
}

/* Collapsible events card on the dive-detail page. The <summary> is
   styled to look like the h3 header used by the other cards so the
   page reads consistently; only the caret + count distinguish it.
   The card itself stays inside .stats-card chrome. */
.events-card > details > .events-summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.7;
    font-weight: 600;
}
.events-card > details[open] > .events-summary { margin: 0 0 10px; }
.events-card > details > .events-summary::-webkit-details-marker { display: none; }
.events-card > details > .events-summary::marker { display: none; }
.events-caret {
    display: inline-block;
    transition: transform 0.15s ease;
    opacity: 0.6;
    font-size: 11px;
}
.events-card > details:not([open]) > .events-summary .events-caret {
    transform: rotate(-90deg);
}
.events-count {
    opacity: 0.55;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
}

/* Top-of-page stats grid — Profile card spans the full first row;
   the remaining four cards sit in a single line beneath it.
   Tablet: 2 columns (4 cards → 2×2). Mobile: 1 column. */
.stats-grid {
    display: grid;
    gap: 16px;
    grid-template-columns: repeat(4, 1fr);
    margin-top: 16px;
}
.stats-grid .stats-card:first-child {
    grid-column: 1 / -1;
}
.stats-grid .stats-card {
    margin-top: 0;       /* the grid does the spacing */
    padding: 18px 18px 12px;
}
/* Profile card (full-width first slot) — lay tiles in a 4-col grid
   so they fill the width instead of all stacking in one column. */
.stats-grid .stats-card:first-child .stat-tiles {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0 12px;
}
.stats-grid .stats-card:first-child .stat-tile {
    padding: 8px 4px;
}
/* First row (tiles 1-4) needs no top border; subsequent rows keep it. */
.stats-grid .stats-card:first-child .stat-tile:nth-child(-n+4) {
    border-top: none;
    padding-top: 4px;
}

@media (max-width: 900px) {
    .stats-grid { grid-template-columns: repeat(2, 1fr); }
    .stats-grid .stats-card:first-child .stat-tiles {
        grid-template-columns: repeat(2, 1fr);
    }
    /* On the 2-col grid, tiles 3+ fall into row 2+ — restore their border. */
    .stats-grid .stats-card:first-child .stat-tile:nth-child(n+3) {
        border-top: 1px solid rgba(255,255,255,0.04);
        padding-top: 8px;
    }
}

/* Stat tiles inside each stats card. One tile per metric:
       [emoji icon] [bold value]
                    [small uppercase label]
   The value is the dominant element so the page reads as a
   dashboard rather than a label/value table. */
.stat-tiles {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.stat-tile {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 10px 4px;
    border-top: 1px solid rgba(255,255,255,0.04);
}
.stat-tile:first-child { border-top: none; padding-top: 4px; }
.stat-tile-icon {
    flex: 0 0 32px;
    font-size: 22px;
    line-height: 1;
    text-align: center;
    /* Keep emoji glyphs visually centered with the value baseline
       across platforms; emoji ascent varies. */
    display: flex;
    align-items: center;
    justify-content: center;
    height: 32px;
}
.stat-tile-body {
    flex: 1 1 auto;
    min-width: 0;
}
.stat-tile-value {
    font-size: 18px;
    font-weight: 600;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
    word-break: break-word;
}
.stat-tile-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin-top: 2px;
}
/* Inline "why is this empty?" pointer rendered under non-editable
   tile values (e.g. the SAC tile when avg_depth_m is NULL). Looks
   like a subtle muted link; clicking opens the pencil on the
   referenced field. */
.stat-tile-hint {
    margin-top: 6px;
    padding: 0;
    background: none;
    border: 0;
    color: var(--accent);
    font-size: 11px;
    text-align: left;
    cursor: pointer;
    font: inherit;
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
}
.stat-tile-hint:hover { color: var(--text); }

/* Inline editor toggle for tiles that double as edit affordances
   (currently just "Started"). Lives at the right edge of the tile
   row so the value/label stack stays unchanged in read-only mode;
   the active state keys off `.is-active` to flag the picker is open. */
.stat-tile-edit {
    margin-left: auto;
    flex: 0 0 28px;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.stat-tile-edit:hover {
    background: var(--bg-elev);
    color: var(--text);
    border-color: var(--border);
}
.stat-tile-edit.is-active {
    background: var(--accent-soft);
    color: var(--accent);
    border-color: var(--accent);
}

/* Inline editor body inside an editable stat tile. Replaces the
   read-only value/label stack; carries one input (or input+label
   pair) per `r.edit.fields` column. Composite tiles (lat/lon/alt,
   salinity type+density, GF low+high) stack vertically — each input
   gets the full tile width so a 6-decimal coordinate fits without
   being chopped in half. */
.tile-edit-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
}
/* One per-field row inside the editor. Carries an optional short
   label on the left ("Lat" / "Lon" / "Alt" / "Low" / "High") and the
   input + unit suffix on the right. The label is fixed-width so all
   three location rows line up neatly. */
.tile-edit-field {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}
/* dl-form's .dlf-field defaults to 18 px margin-bottom for spacing
   between fields in a vertical form. Inside an editable stat tile
   the field is the only one in its row, and that bottom margin
   makes the dropdown wrapper taller than its visible chrome — so a
   centre-aligned ✓ button ends up sitting on the top half of the
   dropdown instead of its visual midline. Zero the margin here so
   heights match and align-items: center does the right thing. */
.tile-edit-field .dlf-field {
    margin-bottom: 0;
}
.tile-edit-field-label {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    min-width: 38px;
}
.tile-edit-num {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    flex: 1 1 auto;
}
.tile-edit-input {
    /* Pin to content-box so a tight `width: …ch` plus padding never
       clips the trailing digit (same fix as feedback_global_border_box
       — :root sets `* { box-sizing: border-box }` globally). */
    box-sizing: content-box;
    flex: 1 1 auto;
    min-width: 6ch;
    width: auto;
    background: var(--bg-elev);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 4px 6px;
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}
.tile-edit-input:focus {
    outline: none;
    border-color: var(--accent);
}
/* Free-text tile editor (migration 00024): bailout_notes (textarea) +
   freedive_discipline (single-line). Stretches to the row width and
   drops the numeric tabular-nums the base .tile-edit-input carries. */
.tile-edit-text {
    box-sizing: border-box;
    width: 100%;
    font-variant-numeric: normal;
}
textarea.tile-edit-text {
    resize: vertical;
    min-height: 2.4em;
    line-height: 1.35;
    font-family: inherit;
}
.tile-edit-suffix {
    color: var(--text-muted);
    font-size: 12px;
    white-space: nowrap;
}
.tile-edit-hint {
    color: var(--text-muted);
    font-size: 11px;
    line-height: 1.3;
    margin-top: 4px;
    font-style: italic;
}
/* Inline ✓ confirm at the end of an edit row. Replaces the tile-
   level pencil while editing (the latter is hidden via inline
   `display: none` from JS) so the ✓ never overlaps a dropdown's
   trailing Manage button. `margin-left: auto` pushes it to the right
   edge of its field-row; on composite tiles that's the bottom-most
   row, on single-field tiles it rides beside the input. */
.tile-edit-done {
    margin-left: auto;
    flex: 0 0 28px;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--accent);
    border-radius: 6px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tile-edit-done:hover {
    filter: brightness(1.08);
}

/* Page-title editor — pencil sits beside the h1, swaps for an input
   in edit mode. `.page-title` is forced to inline-flex so the pencil
   rides on the title baseline; `.title-text` is the read-only span
   so the input swap doesn't shift the title's typography. */
.page-title {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
.title-edit {
    flex: 0 0 28px;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.title-edit:hover {
    background: var(--bg-elev);
    color: var(--text);
    border-color: var(--border);
}
.title-edit.is-active {
    background: var(--accent-soft);
    color: var(--accent);
    border-color: var(--accent);
}
.title-edit-input {
    font: inherit;
    color: var(--text);
    background: var(--bg-elev);
    border: 1px solid var(--accent);
    border-radius: 6px;
    padding: 2px 8px;
    width: 100%;
    max-width: 32rem;
    min-width: 0;
}
.title-edit-input:focus { outline: none; }

/* Computer card — the vendor / product rows each carry an inline
   pencil button. `display: inline-flex` keeps the text and the
   pencil on the same baseline; `.row-edit` reuses the stat-tile-edit
   visual so the page only ships one edit-button look. */
.computer-vendor,
.computer-product {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}
.computer-text-value {
    min-width: 0;
}
.row-edit {
    flex: 0 0 26px;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.row-edit:hover {
    background: var(--bg-elev);
    color: var(--text);
    border-color: var(--border);
}
.row-edit.is-active {
    background: var(--accent-soft);
    color: var(--accent);
    border-color: var(--accent);
}

/* Tanks + gas mixes side by side */
.stats-side-by-side {
    display: grid;
    gap: 16px;
    grid-template-columns: 1fr;
}
@media (min-width: 900px) {
    .stats-side-by-side { grid-template-columns: 1fr 1fr; }
}
.stats-side-by-side .stats-card { margin-top: 16px; }
@media (min-width: 900px) {
    .stats-side-by-side .stats-card { margin-top: 16px; }
}

.summary-edit-row {
    display: grid;
    grid-template-columns: minmax(140px, 1fr) 2fr;
    gap: 8px 12px;
    align-items: center;
    margin: 12px 0;
}
.summary-edit-row .label { opacity: 0.65; }
.summary-edit-row .dlf-field { margin: 0; }

.notes-input {
    width: 100%;
    box-sizing: border-box;
    background: var(--bg-elev);
    border: 1px solid transparent;
    border-radius: 8px;
    color: var(--text);
    padding: 8px 10px;
    font: inherit;
    min-height: 64px;
    resize: vertical;
}
.notes-input:focus { outline: none; border-color: var(--accent); }

.detail-actions {
    display: flex;
    gap: 8px;
    margin-top: 16px;
    flex-wrap: wrap;
}

.detail-banner {
    margin: 0 0 12px;
    padding: 10px 12px;
    background: rgba(248,113,113,0.12);
    color: var(--err);
    border-radius: 8px;
    font-size: 13.5px;
}

/* ── Prev/next dive nav ─────────────────────────────────────────── */
/* Sits between the page-header and the stats-grid. Two anchors
   pushed to opposite edges so the eye reads "back ←   → forward"
   without a separator row. Disabled (chronological end) state keeps
   the same footprint to avoid layout jump on navigation. */
.dive-nav {
    display: flex;
    align-items: stretch;
    justify-content: space-between;
    gap: 8px;
    margin: 0 0 16px;
}
.dive-nav-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    color: var(--text);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 8px;
    text-decoration: none;
    font-size: 13.5px;
    line-height: 1.2;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
.dive-nav-link:hover {
    background: var(--bg-elev);
    border-color: var(--accent-soft);
}
.dive-nav-link.is-disabled {
    opacity: 0.4;
    pointer-events: none;
    cursor: default;
}
.dive-nav-arrow {
    font-size: 16px;
    line-height: 1;
}

.detail-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
    margin-top: 6px;
}
.detail-table th {
    text-align: left;
    font-weight: 600;
    opacity: 0.7;
    padding: 4px 6px;
    border-bottom: 1px solid var(--bg-elev);
    white-space: nowrap;
}
.detail-table td {
    padding: 4px 6px;
    font-variant-numeric: tabular-nums;
    border-bottom: 1px solid rgba(255,255,255,0.04);
}

/* ── Chart card ────────────────────────────────────────────────── */
.channel-chips {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.channel-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    background: var(--bg-elev);
    color: var(--text);
    border: 1px solid transparent;
    cursor: pointer;
    font-size: 13px;
    user-select: none;
}
.channel-chip[aria-pressed="true"] {
    background: var(--accent-soft);
    border-color: var(--accent);
}
.channel-chip .swatch {
    width: 10px;
    height: 10px;
    border-radius: 2px;
    display: inline-block;
}
/* Safety stop info chip — read-only indicator, appended after toggle chips. */
.safety-stop-chip { cursor: default; }
.safety-stop-chip.is-complete {
    color: var(--ok);
    background: var(--ok-soft);
    border-color: var(--ok);
}
.safety-stop-chip.is-incomplete {
    color: var(--err);
    background: var(--err-soft);
    border-color: var(--err);
}

/* Big chart at the bottom.
   ⚠️ Naming collision with the stat module:
   dive.html loads /stat/stat.css (for the site-picker modal classes),
   which also defines `.chart-card` — but with `height: 320px` and
   `display: flex` for the stat-page grid cards. Those bleed into this
   page because the class names overlap. The override below cancels
   both properties so the browse-detail panel auto-sizes to its content
   (chips + chart-wrap + padding) instead of being pinned to 320 px.
   stat.css's `.chart-card canvas { width/height: 100% !important }`
   happens to compute to the same value as Chart.js's inline sizing
   (canvas's parent is chart-wrap, which has the same explicit height
   we feed into chart.resize), so it stays put — no visual effect. */
/* `.chart-card.chart-card-large` (specificity 0,2,0) beats stat.css's
   `.chart-card { padding: 14px 14px 18px }` (0,1,0) — stat.css is
   lazy-loaded after browse.css by the lookups modal, so a single-
   class override would lose the cascade tie. */
.chart-card.chart-card-large {
    height: auto;
    display: block;
    /* Two-class selector so it beats stat.css's single-class
       `.chart-card { border-radius: 12px }` regardless of load order. */
    border-radius: var(--radius-lg);
    padding: 10px 0 0;
    margin-bottom: 48px;
    overflow: hidden;        /* clips the negative-margin canvas to the
                                rounded card corners */
}
.chart-card.chart-card-large .channel-chips {
    margin: 0 0 8px;
    padding: 0 12px;         /* chips keep their inset; only the canvas
                                breaks out to the card edges */
}
.chart-wrap {
    position: relative;
    width: 100%;
    height: clamp(520px, 78vh, 1100px);
    overflow: hidden;
}
.dive-chart {
    display: block;
}
.chart-tooltip {
    position: absolute;
    pointer-events: none;
    background: var(--bg-card);
    border: 1px solid var(--bg-elev);
    border-radius: 8px;
    padding: 8px 10px;
    font-size: 12.5px;
    min-width: 160px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.35);
    opacity: 0;
    transition: opacity .12s ease;
    z-index: 2;
}
.chart-tooltip.is-visible { opacity: 1; }
.chart-tooltip .tt-time {
    font-variant-numeric: tabular-nums;
    opacity: 0.6;
    margin-bottom: 4px;
}
.chart-tooltip .tt-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    font-variant-numeric: tabular-nums;
}
.chart-tooltip .tt-swatch {
    display: inline-block;
    width: 8px; height: 8px;
    border-radius: 2px;
    margin-right: 6px;
    vertical-align: middle;
}

.chart-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    opacity: 0.6;
}

/* ── Chart zoom toolbar ─────────────────────────────────────────────
   Sits at the right end of the channel-chip row (above the canvas) so the
   controls never cover the profile. `position:relative` anchors the help
   popover beneath it. */
.chart-zoom-toolbar {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 4px;
    position: relative;
}
.chart-zoom-btn {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--bg-elev);
    color: var(--text);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    padding: 0;
}
.chart-zoom-btn:hover:not(:disabled) {
    background: var(--accent-soft);
    border-color: var(--accent);
}
.chart-zoom-btn:disabled {
    opacity: 0.35;
    cursor: default;
}

/* Help popover — toggled by the "?" toolbar button, anchored beneath it. */
.chart-help-pop {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 6px;
    max-width: 260px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-card);
    color: var(--text);
    font-size: 12px;
    line-height: 1.45;
    box-shadow: 0 8px 24px var(--shadow);
    z-index: 4;
}
.chart-help-pop[hidden] { display: none; }

/* Shown briefly when a bare wheel passes over the profile: the page
   scrolls, and this tells the reader how to zoom instead. */
.chart-wheel-hint {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: var(--space-3, 12px) var(--space-4, 16px);
    border: 1px solid var(--border);
    border-radius: var(--radius-md, 8px);
    background: var(--bg-card);
    color: var(--text);
    font-size: var(--font-size-lg);
    box-shadow: var(--shadow-md, 0 8px 24px rgba(0,0,0,.25));
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--duration-fast) var(--ease-standard);
    z-index: 3;
}
.chart-wheel-hint.is-visible { opacity: 1; }

/* ── Photos on the profile timeline ─────────────────────────────────
   Camera glyphs are drawn on the canvas by the dl-media-markers plugin;
   everything below is the DOM chrome around them. The hover thumbnail is
   a real element rather than a canvas drawImage so it gets lazy loading,
   object-fit and the theme tokens for free — none of which canvas gives
   without hand-rolling an image cache and a repaint dance. */
.chart-photo-pop {
    position: absolute;
    z-index: 5;
    display: flex;
    gap: 6px;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-card);
    box-shadow: 0 8px 24px var(--shadow);
    /* The glyph underneath owns the pointer: a popover that swallowed
       hover would flicker itself out of existence on every mousemove. */
    pointer-events: none;
}
.chart-photo-pop[hidden] { display: none; }
.chart-photo-pop__item { display: flex; flex-direction: column; gap: 3px; }
.chart-photo-pop__img {
    display: block;
    width: 96px;
    height: 96px;
    object-fit: cover;
    border-radius: 6px;
    background: var(--bg-subtle);
}
.chart-photo-pop__cap {
    max-width: 96px;
    color: var(--text-muted);
    font-size: 11px;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Sync bar — visible only while the chart is in camera-clock sync mode.
   Dashed + accent-washed so it reads as a temporary mode, not furniture. */
.photo-sync-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin: 10px 12px 0;
    padding: 10px 14px;
    border: 1px dashed var(--accent);
    border-radius: 10px;
    background: var(--accent-soft);
}
.photo-sync-bar[hidden] { display: none; }
.photo-sync-bar__hint { color: var(--text); font-size: 13px; }
.photo-sync-bar__offset {
    /* Tabular figures: the readout updates on every pointermove during a
       drag, and proportional digits make it jitter sideways as it counts. */
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--text);
}
.photo-sync-bar__spacer { flex: 1 1 auto; }

/* "Outside the profile" strip — the photos whose corrected time missed the
   dive. This is the signal that tells the diver the offset is wrong; a
   silently-dropped photo would read as data loss. */
.photo-outside {
    margin: 10px 12px 0;
    padding: 10px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-elev);
}
.photo-outside[hidden] { display: none; }
.photo-outside__title {
    margin: 0 0 8px;
    color: var(--text-muted);
    font-size: 12px;
    font-weight: 600;
}
.photo-outside__row { display: flex; flex-wrap: wrap; gap: 12px; }
.photo-outside__item {
    display: flex;
    flex-direction: column;
    gap: 4px;
    align-items: center;
    max-width: 96px;
}
.photo-outside__img {
    display: block;
    width: 64px;
    height: 64px;
    object-fit: cover;
    border-radius: 6px;
    background: var(--bg-subtle);
    cursor: pointer;
}
.photo-outside__delta {
    color: var(--text-muted);
    font-size: 11px;
    line-height: 1.3;
    text-align: center;
}
.photo-outside__hint { margin: 10px 0 0; font-size: 12px; color: var(--text-muted); }
.photo-outside__fix {
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}
.photo-outside__fix:hover { color: var(--accent-hover); }

/* ── Diver bookmarks on the profile ─────────────────────────────────
   Pins are drawn on the canvas by the dl-bookmarks plugin; this is the
   chrome around them. The hover popover mirrors .chart-photo-pop (same
   flip-below + clamp logic in JS) but carries text rather than images. */
.chart-bookmark-pop {
    position: absolute;
    z-index: 5;
    max-width: 260px;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-card);
    color: var(--text);
    font-size: 12px;
    line-height: 1.4;
    box-shadow: 0 8px 24px var(--shadow);
    pointer-events: none;
}
.chart-bookmark-pop[hidden] { display: none; }
.chart-bookmark-pop__time {
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    margin-right: 6px;
}

.bookmarks-list { display: flex; flex-direction: column; gap: 2px; }
.bookmark-row {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 6px 8px;
    border-radius: 8px;
    cursor: pointer;
    transition: background var(--duration-fast, 120ms) var(--ease-standard, ease);
}
.bookmark-row:hover { background: var(--bg-subtle); }
.bookmark-row__time {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}
.bookmark-row__depth {
    flex: 0 0 auto;
    color: var(--text-subtle);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}
.bookmark-row__kind { flex: 0 0 auto; }
.bookmark-row__text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text);
    font-size: 0.9rem;
}
.bookmark-row__actions { flex: 0 0 auto; display: flex; gap: 2px; }
.bookmark-row__btn {
    border: 0;
    background: none;
    padding: 2px 5px;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
}
.bookmark-row__btn:hover { background: var(--bg-elev); color: var(--text); }

/* An orphaned bookmark sits in a stretch the diver cut out. It is kept —
   dive_profile_revert brings the profile back byte-for-byte and a
   hand-typed note has to come back with it — so it reads as present but
   inert rather than disappearing. */
.bookmark-row--orphaned { cursor: default; }
.bookmark-row--orphaned:hover { background: none; }
.bookmark-row--orphaned .bookmark-row__text { color: var(--text-subtle); }
.bookmark-orphan-badge {
    flex: 0 0 auto;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--bg-subtle);
    color: var(--text-subtle);
    font-size: 0.7rem;
}

/* Pulsed for ~1.2 s when its pin is clicked on the chart. */
.bookmark-row--highlight { background: var(--accent-soft); }

.bookmark-edit {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 8px;
    padding: 8px;
    margin: 2px 0 6px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-elev);
}
.bookmark-edit__text   { flex: 1 1 16rem; min-width: 12rem; }
.bookmark-edit__kind   { flex: 0 0 9rem; }
.bookmark-edit__animal { flex: 1 1 14rem; min-width: 10rem; }
.bookmark-edit__actions { flex: 0 0 auto; display: flex; gap: 6px; }
/* ── Logbook signatures ─────────────────────────────────────────────
   The ink is an inline <svg> in .signature-ink, so it inherits `color` and
   scales with the card — no DPR handling, no repaint on resize. */
.signatures-list { display: flex; flex-direction: column; gap: 10px; }
.signature-card {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-elev);
}
.signature-ink {
    flex: 0 0 auto;
    width: 150px;
    height: 56px;
    color: var(--text);
}
.signature-ink svg { display: block; width: 100%; height: 100%; }
.signature-ink--missing {
    display: flex;
    align-items: center;
    color: var(--text-subtle);
    font-size: 0.72rem;
    font-style: italic;
}
.signature-meta { flex: 1 1 auto; min-width: 0; }
.signature-name {
    display: flex;
    align-items: baseline;
    gap: 8px;
    color: var(--text);
    font-size: 0.95rem;
    font-weight: 600;
}
.signature-role {
    flex: 0 0 auto;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--text);
    font-size: 0.7rem;
    font-weight: 500;
}
.signature-creds {
    color: var(--text-muted);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}
.signature-when {
    color: var(--text-subtle);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
}
.signature-note {
    margin-top: 2px;
    color: var(--text-muted);
    font-size: 0.8rem;
}
.signature-delete {
    flex: 0 0 auto;
    align-self: flex-start;
    border: 0;
    background: none;
    padding: 2px 5px;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
}
.signature-delete:hover { background: var(--bg-subtle); color: var(--text); }

/* Where a mark came from, on a signature that arrived over /sign. INFO, not
   OK: it says "this reached us from the dive centre", never "verified" — the
   disclaimer below the list still applies to it in full. */
.signature-source {
    margin-top: 2px;
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--info-soft);
    color: var(--info);
    font-size: 0.72rem;
}

/* The offer panel: a code to read across a counter, and a clock. */
.sign-request {
    padding: 12px;
    margin-bottom: 10px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-subtle);
    text-align: center;
}
.sign-request__title {
    font-weight: 600;
    margin-bottom: 4px;
}
.sign-request__lead {
    margin: 0 auto 10px;
    max-width: 42ch;
    color: var(--text-muted);
    font-size: 0.78rem;
    line-height: 1.45;
}
/* Read aloud across a desk, so: big, monospaced and widely tracked. */
.sign-request__code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 1.7rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    color: var(--text);
}
.sign-request__qr {
    display: inline-block;
    margin: 10px 0 4px;
    padding: 8px;
    border-radius: 8px;
    /* A QR needs a light quiet zone to scan at all — the same documented
       exception the pairing offer and TOTP enrollment take in /settings/.
       This is the code's own optics, not page chrome. */
    background: #fff;
}
.sign-request__qr img { display: block; }
/* Who the QR is for. The payload is JSON with a URL inside, so a phone's own
   camera offers to open the logbook and the QR reads as broken; this line is
   cheaper than changing a wire format two apps agree on. */
.sign-request__qrhint {
    margin: 4px auto 0;
    max-width: 34ch;
    color: var(--text-subtle);
    font-size: 0.7rem;
    line-height: 1.4;
}
.sign-request__status {
    margin: 6px 0 0;
    color: var(--text-subtle);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
}
.sign-request__actions {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-top: 10px;
}

/* The honesty line. Rendered whether or not anybody has signed — it is what
   stops the feature over-claiming, not a footnote to a list. */
.signature-disclaimer {
    margin: 12px 0 0;
    color: var(--text-subtle);
    font-size: 0.72rem;
    line-height: 1.45;
}

.signature-form {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    padding: 12px;
    margin-bottom: 10px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-subtle);
}
.signature-form > .dlf-field { flex: 1 1 12rem; min-width: 10rem; }
.signature-form__actions {
    flex: 1 1 100%;
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

/* ✍ chips. "Signed", never "verified" — see browse.signature.disclaimer. */
.hero-signed-badge {
    display: inline-block;
    margin-left: 10px;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--text);
    font-size: 0.7rem;
    font-weight: 500;
    vertical-align: middle;
}
.row-signed-badge {
    margin-left: 5px;
    color: var(--text-muted);
    cursor: help;
}

.bookmark-pin-hint {
    margin: 8px 0 0;
    color: var(--accent);
    font-size: 0.78rem;
}

/* ── Range-selection analysis panel ─────────────────────────────────
   Appears below the canvas after the user drags a range on the chart. */
.chart-range-panel {
    margin: 10px 12px 0;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-elev);
}
.chart-range-panel .range-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}
.chart-range-panel .range-title {
    font-weight: 600;
    margin-right: auto;
}
/* .range-btn look: the shared .dl-btn system (01 chart redesign). */
.chart-range-panel .range-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 6px 18px;
}
.chart-range-panel .range-cell {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 3px 0;
    border-bottom: 1px solid rgba(255,255,255,0.05);
    font-variant-numeric: tabular-nums;
}
.chart-range-panel .range-label { color: var(--text-muted); }
.chart-range-panel .range-value { font-weight: 500; }

/* ── Dive-site picker (detail page) ─────────────────────────────── */
/* The site cell holds two stacked elements: the picker row (dropdown
   + Manage button) and the optional Google-Maps link below it. */
.summary-edit-row .site-cell {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
.summary-edit-row .site-picker {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 8px;
    align-items: center;
}
.summary-edit-row .site-picker .dlf-field { min-width: 0; margin: 0; }
.summary-edit-row .site-link-host:empty { display: none; }
.site-link {
    display: inline-block;
    font-size: 13px;
    color: var(--accent);
    text-decoration: none;
    word-break: break-all;
}
.site-link:hover { text-decoration: underline; }

/* 🗺️ button next to the site name on /browse/dive.html when the site
   catalog row carries map_image_ext (migration 00024). Click opens
   DL.ImageViewer on the /cgi/sites_map_image bytes. */
.site-map-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin-right: 8px;
    padding: 0;
    background: var(--bg-elev);
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    vertical-align: middle;
    transition: border-color 120ms, background-color 120ms;
}
.site-map-btn:hover {
    border-color: var(--accent);
    background: var(--accent-soft);
}
.site-map-btn:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-soft);
}

/* Small circular thumbnail rendered next to the resolved Site /
   Center name when the catalog row has an uploaded photo (image_ext).
   Surfaces /cgi/sites_image / /cgi/centers_image bytes; the
   ?v=updated_at cache-bust lives in the consumer (see dive.html). */
.dive-tile-thumb {
    width: 28px; height: 28px;
    border-radius: 50%;
    object-fit: cover;
    vertical-align: middle;
    margin-right: 8px;
    background: var(--bg-elev);
    border: 1px solid var(--border);
}

/* ── Sites modal ────────────────────────────────────────────────── */
/* Add-row form. Flat 3-column / 2-row grid: labels in row 1,
   inputs + Add button in row 2. Auto-row sizing keeps the inputs and
   the button on the same baseline regardless of label height, which
   the previous wrapper-based layout couldn't guarantee. */
.sites-form {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto;
    column-gap: 10px;
    row-gap: 6px;
    margin-top: 14px;
    align-items: center;
}
.sites-form .dlf-label { margin: 0; }
.sites-form .dlf-button { white-space: nowrap; }

.sites-table td.site-location { max-width: 260px; }
.sites-table td.site-location a { word-break: break-all; }
.sites-table td.site-latlon {
    font-variant-numeric: tabular-nums;
    font-size: 12.5px;
    opacity: 0.7;
    white-space: nowrap;
}
.sites-table td.site-used {
    font-variant-numeric: tabular-nums;
    text-align: right;
    opacity: 0.7;
}
.sites-table td.sites-empty {
    text-align: center;
    opacity: 0.55;
    padding: 20px 8px;
    font-size: 13px;
}

/* ── Picker-cell pattern (divemode / specialities rows) ─────────────
   Same 2-column grid the site picker uses: control on the left, the
   Manage button on the right. Shared class so adding more lookups
   later doesn't need new CSS. */
.summary-edit-row .picker-cell {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 8px;
    align-items: center;
    min-width: 0;
}
.summary-edit-row .picker-cell .dlf-field { min-width: 0; margin: 0; }

/* ── Tank-type cell (within tanks table) ────────────────────────────
   The dropdown sits in a regular table cell, so we strip the
   dlf-field's default margin / max-width and let the cell control
   width. */
.tank-type-cell { min-width: 160px; }
.tank-type-cell .dlf-field { margin: 0; min-width: 0; }
.tank-type-cell .dlf-combo { width: 100%; }

/* Read-only value cells in the tanks table ("Used" / "Gas (L)"). Keep
   "60 bar" / "1234 L" on a single line so the column doesn't double in
   height when the table is tight. */
#tanks-card .detail-table td { white-space: nowrap; }

/* Inline numeric cells (volume / start / end). Plain <input type=number>
   styled to match the dlf-input look so the row reads as a single record
   rather than a row of bare browser controls. The trailing unit (L / bar)
   sits in a sibling span so it doesn't get caught by the input's own
   number-spinner styling. Inputs are sized to fit typical values
   (≤4 chars) so the read-only "Used" / "Gas (L)" columns to the right
   keep enough room to render their values on one line. */
.tank-number-cell { padding: 4px 6px; white-space: nowrap; }
.tank-number-wrap {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
}
.tank-number-input {
    width: 5em;
    box-sizing: border-box;
    padding: 4px 6px;
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 4px;
    font: inherit;
    text-align: right;
}
.tank-number-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-soft);
}
.tank-number-unit {
    color: var(--text-muted);
    font-size: 0.9em;
}

.tank-manage-row {
    margin-top: 10px;
    display: flex;
    justify-content: flex-end;
}

/* Per-row "Update" button in the tank table. Tight cell so the table
   keeps its compact rhythm; the button styling rides on .dlf-btn
   (kind: ghost) so it picks up the active theme like every other ghost
   button on the page. */
.tank-action-cell {
    padding: 4px 6px;
    text-align: right;
    white-space: nowrap;
}
.tank-action-cell .dlf-btn {
    padding: 4px 10px;
    font-size: 0.85em;
}

/* ── Empty-tanks add form (dive detail) ─────────────────────────────
   Rendered when a dive has no tank rows: a hint, the shared
   DL.Tanks.editor (table + "+ Add tank" button — styled by
   /common/css/dl-tanks.css), and a page-specific actions row hosting
   the "Save tanks" submit button alongside the Add button. The .tank-
   remove-btn / .dl-tanks-table row styling lives in the shared
   stylesheet so /addlog/ and this page stay visually identical. */
.tank-add-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 10px;
}

/* ── Lookups modal form ──────────────────────────────────────────── */
/* Two-column add-row: name input on the left, Add button on the right.
   The hidden header label cell + spacer keep the form lined up with
   the table above without forcing the input to span the button width. */
.lookups-form {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 10px;
    row-gap: 6px;
    margin-top: 14px;
    align-items: center;
}
.lookups-form .dlf-label { margin: 0; }
.lookups-form .dlf-button { white-space: nowrap; }

.lookups-table td.lookup-source {
    font-size: 12.5px;
    opacity: 0.75;
}
.lookups-table td.lookup-used {
    font-variant-numeric: tabular-nums;
    text-align: right;
    opacity: 0.7;
}
.lookups-table td.lookups-empty {
    text-align: center;
    opacity: 0.55;
    padding: 20px 8px;
    font-size: 13px;
}

@media (max-width: 640px) {
    /* Tanks and gas-mix tables carry nowrap cells plus dropdowns and
       buttons that push row width past ~400 px. Scroll horizontally
       inside each card so the table stays readable without making the
       whole page wider than the viewport. The same technique is used
       for the dive list (.dives-scroll) above. */
    #tanks-box,
    #gasmixes-box {
        overflow-x: auto;
        overscroll-behavior-x: contain;
    }

    /* Stats grid: single column on small phones. */
    .stats-grid {
        grid-template-columns: 1fr;
    }

    .sites-form { grid-template-columns: 1fr; }
    .summary-edit-row .site-picker { grid-template-columns: 1fr; }
    .summary-edit-row .picker-cell { grid-template-columns: 1fr; }
    .lookups-form { grid-template-columns: 1fr; }

    /* ── Dive list, mobile ─────────────────────────────────────────
       The list has 10 columns (incl. the pick column) and every cell
       is `white-space: nowrap`, so on phones it overflows the viewport.
       Strategy:
         1. Wrap the table in a horizontal scroller so anything left over
            still slides into view instead of pushing the page width.
         2. Hide the three lowest-value columns — temp / mode / SAC — they
            are available on the per-dive detail page anyway.
         3. Shrink cell padding + font a touch so the remaining 7 columns
            (pick · # · date · dur · max · stars · site) tend to fit
            without horizontal scrolling on common phone widths.
       Trade-off: vertical sticky on `thead` is given up on mobile —
       turning the wrapper into a scroll container changes the sticky
       reference frame, so the headers travel with the page on phones.
       Worth it to keep the layout intact. Desktop is unchanged: the
       wrapper has `overflow: visible` by default and adds no styling. */
    .dives-scroll {
        overflow-x: auto;
        /* No `-webkit-overflow-scrolling: touch` — deprecated since iOS 13
           (modern Safari does momentum scrolling natively) and historically
           broke `position: sticky` inside the container by promoting it to
           its own compositor layer. */
        overscroll-behavior-x: contain;
    }
    /* Sticky thead is given up on phones. Per the CSS overflow spec, the
       wrapper above turns into a scroll container for BOTH axes the moment
       `overflow-x: auto` is set (a `visible` on the other axis is implicitly
       promoted to `auto`). That reparents the sticky context for the thead
       from the page to the wrapper, and on iOS Safari that renders the
       headers below the first few rows — exactly the bug we'd otherwise
       hit. The browse toolbar lives outside the wrapper, so it still pins
       to viewport-top and gives the page enough top-context on small
       screens. Reverting to `static` is enough; clearing `top` keeps it
       tidy in devtools. */
    .dives-table thead th {
        position: static;
        top: auto;
    }
    .dives-table thead th[data-col="temp"],
    .dives-table thead th[data-col="mode"],
    .dives-table thead th[data-col="sac"],
    .dives-table tbody td[data-col="temp"],
    .dives-table tbody td[data-col="mode"],
    .dives-table tbody td[data-col="sac"] {
        display: none;
    }
    .dives-table { font-size: 13px; }
    .dives-table thead th,
    .dives-table tbody td {
        padding: 8px 6px;
    }
    /* Site cell is the longest free-text column — give it less max width
       on phones so it doesn't dominate the row when other columns are
       hidden. The cell already truncates with ellipsis. */
    .dives-table td.site { max-width: 160px; }
}

/* ── Media card ──────────────────────────────────────────────────── */

/* Header row: heading on left, edit-pencil button on right */
.detail-section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
}
.detail-section-header h3 { margin: 0; }
/* Keeps two header actions together at the right-hand end instead of letting
   space-between push the first one into the middle of the row. */
.detail-section-actions { display: inline-flex; gap: 4px; align-items: center; }

/* Pencil / edit toggle button used in the media-card header */
.detail-pencil {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.95rem;
    padding: 0.15rem 0.5rem;
    line-height: 1.4;
    transition: color .15s, background .15s, border-color .15s;
}
.detail-pencil:hover {
    color: var(--accent);
    background: var(--accent-soft);
    border-color: var(--accent);
}

/* Tile gallery — flexbox so tiles have a natural capped size.
   Each tile grows from a 280px base but never exceeds 480px,
   so a single tile stays compact and multiple tiles fill the row
   naturally without phantom empty columns. */
.media-preview-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 0.25rem;
    align-items: flex-start;
}
.media-tile {
    flex: 1 1 280px;
    max-width: min(100%, 480px);
    min-width: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-card);
}

/* Preview wrapper — just clips corners and provides background colour.
   Height comes entirely from the content inside (the embed/img/video
   below), each of which carries its own aspect-ratio: 16/9. */
.media-preview {
    position: relative;   /* anchor for the Drive overlay */
    width: 100%;
    overflow: hidden;
    background: var(--bg-elev);
    line-height: 0;       /* removes the inline gap under block children */
}

/* Each content type declares aspect-ratio: 16/9 for itself so the
   browser sizes it directly — no padding tricks, no position:absolute,
   no height:100% against a zero content-height. */
.media-embed {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    border: none;
}
.media-img {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
}
.media-video {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
}
.media-fallback {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: var(--bg-elev);
    gap: 0.5rem;
}
/* Drive thumbnail poster — fills the preview area like a cover image */
.media-drive-thumb {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: var(--bg-elev) center / cover no-repeat;
    cursor: pointer;
}
.media-drive-thumb:hover .media-drive-play-btn,
.media-drive-thumb:focus-visible .media-drive-play-btn {
    background: rgba(0, 0, 0, 0.82);
    transform: scale(1.08);
}
.media-drive-play-btn {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.62);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    pointer-events: none;
    transition: background .15s, transform .15s;
}
.media-drive-play-btn svg { margin-left: 4px; } /* optical centre for play triangle */

/* Full-screen modal — backdrop */
.media-drive-modal {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.88);
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
/* Inner box — sized to give Drive's player room to render controls */
.media-drive-modal-box {
    position: relative;
    width: min(92vw, 1024px);
}
/* Clipping wrapper: scale(1.1) from the top-left corner keeps the video
   anchored and pushes Drive's right/bottom chrome strips outward so
   overflow:hidden clips them. Origin 0 0 = video stays put; chrome exits. */
.media-drive-modal-wrap {
    width: 100%;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border-radius: 4px;
}
.media-drive-modal-iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: none;
    transform: scale(1.12);
    transform-origin: 0 0;
}
/* Close button overlays the top-right corner of the iframe */
.media-drive-modal-close {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    z-index: 10;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.65);
    border: none;
    color: #fff;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background .15s;
}
.media-drive-modal-close:hover { background: rgba(0, 0, 0, 0.9); }

.media-fallback-icon     { width: 2.5rem; height: 2.5rem; color: var(--text-muted); }
.media-fallback-icon svg { width: 100%; height: 100%; }
.media-fallback-domain   { font-size: 0.8rem; color: var(--text-muted); }

/* Caption bar below the preview */
.media-caption {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.7rem;
    min-height: 2.2rem;
    border-top: 1px solid var(--border);
}
.media-caption-link {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text);
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 500;
}
.media-caption-link:hover   { text-decoration: underline; color: var(--accent); }
.media-title-url             { color: var(--text-muted); font-weight: 400; }

.media-remove-btn {
    flex-shrink: 0;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    padding: 0.1rem 0.35rem;
    font-size: 1.1rem;
    line-height: 1;
    border-radius: 4px;
}
.media-remove-btn:hover { color: var(--err); background: var(--accent-soft); }

/* Add-URL row (shared between addlog and browse detail) */
.media-add-row {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-top: 0.75rem;
    align-items: flex-start;
}
.media-add-row .dlf-field         { margin-bottom: 0; }
.media-add-row .media-url-field   { flex: 2; min-width: 12rem; }
.media-add-row .media-title-field { flex: 1; min-width: 8rem; }
/* Legacy raw-input selectors — harmless if unused */
.media-url-input   { flex: 2; min-width: 12rem; }
.media-title-input { flex: 1; min-width: 8rem; }

/* ── Uploaded photos (migration 00025) ───────────────────────────────── */
.media-img-upload { cursor: zoom-in; }
.media-img-upload:hover { filter: brightness(1.05); }

/* Caption for uploaded rows: title text + optional EXIF capture hint,
   stacked so the hint sits under the title without a link affordance. */
.media-caption-label {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}
.media-caption-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text);
    font-size: 0.85rem;
    font-weight: 500;
}
.media-exif-hint {
    font-size: 0.72rem;
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.media-exif-hint-off { color: var(--err); }

/* Camera clock offset row — view mode only. Calibrating a clock is a
   different job from reordering and uploading, so it stays out of the
   media card's edit mode entirely. */
.media-offset-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 8px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.media-offset-row__label {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 0.78rem;
    font-weight: 600;
    align-self: center;
}
.media-offset-row__field { flex: 0 0 9rem; }
.media-offset-row__field input { font-variant-numeric: tabular-nums; }
.media-offset-hint {
    margin: 8px 0 0;
    color: var(--text-muted);
    font-size: 0.72rem;
    line-height: 1.4;
}

/* Upload file picker on the add-row */
.media-upload-field { flex: 0 0 auto; }
.media-upload-field input[type="file"] { display: none; }

/* Drag-to-reorder affordances in edit mode */
.media-tile-draggable { cursor: grab; }
.media-tile-dragging  { opacity: 0.45; }
.media-tile-dragover  { outline: 2px dashed var(--accent); outline-offset: 2px; }

/* ── Conditions card (migration 00022) ───────────────────────────────
   The fetched-conditions card reuses the generic .stats-card /
   .stat-tile chrome; these rules add the per-tile "manual override"
   badge, the Fetch action row, and the provider-attribution caption. */

/* Span the full grid width and lay the tiles in a 4-col grid — mirrors
   the Profile (:first-child) card so the 10 condition tiles fill the
   width instead of stacking in one narrow column. */
.stats-grid .stats-card-conditions { grid-column: 1 / -1; }
.stats-grid .stats-card-conditions .stat-tiles {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0 12px;
}
.stats-grid .stats-card-conditions .stat-tile { padding: 8px 4px; }
.stats-grid .stats-card-conditions .stat-tile:nth-child(-n+4) {
    border-top: none;
    padding-top: 4px;
}
@media (max-width: 900px) {
    .stats-grid .stats-card-conditions .stat-tiles {
        grid-template-columns: repeat(2, 1fr);
    }
    .stats-grid .stats-card-conditions .stat-tile:nth-child(n+3) {
        border-top: 1px solid rgba(255,255,255,0.04);
        padding-top: 8px;
    }
}

/* Post-dive readiness card (no-fly / desat clocks). Full-width like the
   conditions card, tiles laid in a horizontal title/value row so the short
   list doesn't stretch the Decompression column. */
.stats-grid .stats-card-readiness { grid-column: 1 / -1; }
.stats-grid .stats-card-readiness .stat-tiles {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0 12px;
}
.stats-grid .stats-card-readiness .stat-tile { padding: 8px 4px; }
.stats-grid .stats-card-readiness .stat-tile:nth-child(-n+4) {
    border-top: none;
    padding-top: 4px;
}
@media (max-width: 900px) {
    .stats-grid .stats-card-readiness .stat-tiles {
        grid-template-columns: repeat(2, 1fr);
    }
    .stats-grid .stats-card-readiness .stat-tile:nth-child(n+3) {
        border-top: 1px solid rgba(255,255,255,0.04);
        padding-top: 8px;
    }
}

.cond-override-badge {
    display: inline-block;
    margin-top: 4px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    white-space: nowrap;
}
.cond-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-top: 12px;
}
.cond-caption {
    color: var(--text-muted);
    font-size: 11px;
    line-height: 1.4;
}

/* ── Dive share actions ────────────────────────────────────────── */
.dive-share-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
    align-items: center;
}

.dive-share-btn,
.dive-export-html-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    border: 1px solid var(--border);
    background: var(--bg-elev);
    color: var(--text);
    transition: background 0.15s, border-color 0.15s;
    line-height: 1.4;
}

.dive-share-btn:hover,
.dive-export-html-btn:hover {
    background: var(--bg-card);
    border-color: var(--accent);
    color: var(--accent);
}

.dive-share-btn img {
    flex-shrink: 0;
    opacity: 0.7;
}


/* ── Dive hero (photo-redesign spec section 12) ────────────────────────────
   .page-hero supplies the frame, photo layer and scrim from
   common/css/dl-components.css; these are the dive-page specifics. */
.dive-hero { margin-bottom: var(--section-gap); }
/* The title keeps its inline pencil button, so it must not be a block-level
   flex child fighting the hero's own layout. */
.dive-hero .page-hero__title {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .4em;
}
.dive-hero .page-hero__eyebrow { font-variant-numeric: tabular-nums; }
.dive-hero .page-hero__subtitle:empty { display: none; }

/* Profile chart is now the first and largest element on the page.
   Spec: minimum 320-360px desktop, 240-280px mobile. */
.dive-profile-card.chart-card.chart-card-large {
    min-height: 360px;
    margin-bottom: var(--section-gap);
}
.dive-profile-card #chart-host { min-height: 320px; }
@media (max-width: 720px) {
    .dive-profile-card.chart-card.chart-card-large { min-height: 280px; }
    .dive-profile-card #chart-host { min-height: 240px; }
}

/* ── Profile editor ────────────────────────────────────────────────────── */
/* The card pins a min-height so the read-only chart never collapses; while
   editing it grows instead, because the editor stacks a cut list, a
   before→after strip and a button row under the same canvas. */
.dive-profile-head {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: 0 var(--space-3) var(--space-2);
    min-height: var(--control-sm);
}
.dive-profile-edited {
    font-size: var(--font-size-xs);
    color: var(--warn);
    background: var(--warn-soft);
    border-radius: var(--radius-pill);
    padding: 2px var(--space-3);
    margin-right: auto;
}
/* A reconstruction is INFORMATION, not a warning — the edited badge next to
   it owns --warn because a cut means the stored numbers no longer match the
   recording, whereas a drawn profile never claimed to be one. */
.dive-profile-synthetic {
    font-size: var(--font-size-xs);
    color: var(--info);
    background: var(--info-soft);
    border-radius: var(--radius-pill);
    padding: 2px var(--space-3);
    margin-right: auto;
}
/* Merged dives take the same INFORMATION pair as a reconstruction: the
   numbers are honestly derived, they just describe two recordings the diver
   said were one outing. The badge's title names the parts, because the rows
   it absorbed no longer exist anywhere else. */
.dive-profile-merged {
    font-size: var(--font-size-xs);
    color: var(--info);
    background: var(--info-soft);
    border-radius: var(--radius-pill);
    padding: 2px var(--space-3);
    margin-right: auto;
}
.dive-profile-card.is-editing { min-height: 0; }
.dive-profile-card.is-editing #chart-host { min-height: 0; }

.profile-editor { padding: 0 0 var(--space-4); }
.pe-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-3);
    padding: 0 var(--space-4) var(--space-3);
}
.pe-title { color: var(--text); }
.pe-hint  { color: var(--text-muted); font-size: var(--font-size-xs); }
.pe-chart { min-height: 320px; }

.pe-cuts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4) 0;
}
.pe-empty { color: var(--text-subtle); font-size: var(--font-size-xs); }
.pe-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--err);
    background: var(--err-soft);
    border: 1px solid var(--err);
    border-radius: var(--radius-sm);
    padding: 2px var(--space-2) 2px var(--space-3);
}
.pe-chip-x {
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
    font-size: var(--font-size-md);
    line-height: 1;
    padding: 0 2px;
}
.pe-chip-x:hover { color: var(--text); }

.pe-preview {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-4) 0;
}
.pe-cell { display: flex; flex-direction: column; gap: 2px; }
.pe-cell-label { font-size: var(--font-size-xs); color: var(--text-muted); }
.pe-cell-value { display: inline-flex; align-items: baseline; gap: var(--space-2); }
.pe-was   { color: var(--text-subtle); text-decoration: line-through; }
.pe-arrow { color: var(--text-subtle); }
.pe-now   { color: var(--accent); font-weight: 600; }
.pe-estimate { flex-basis: 100%; margin: var(--space-2) 0 0; }
.pe-warn {
    flex-basis: 100%;
    margin: var(--space-2) 0 0;
    color: var(--warn);
    font-size: var(--font-size-xs);
}

.pe-foot {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-4) 0;
}

/* ── Hand-drawn profile editor ─────────────────────────────────────────── */
/* Structurally a twin of the .pe-* block above: same bar, same footer, same
   card growth while editing. The point table itself is the shared widget
   (.pp-*, common/css/dl-profile-points.css) because /addlog/ mounts it too. */
.profile-manual { padding: 0 0 var(--space-4); }
.pm-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-3);
    padding: 0 var(--space-4) var(--space-3);
}
.pm-title { color: var(--text); }
.pm-hint  { color: var(--text-muted); font-size: var(--font-size-xs); }
.pm-points { padding: 0 var(--space-4); }
.pm-estimate { margin: var(--space-3) 0 0; padding: 0 var(--space-4); }
.pm-foot {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-4) 0;
}

/* Danger action inside the chart's own range panel (the "Cut selection"
   button the profile editor injects). Scoped through .chart-range-panel so
   it beats the two-class `.chart-range-panel .range-btn:hover` above. */
/* Danger range action: .dl-btn--danger (01 chart redesign). */

/* ── Advanced disclosure ───────────────────────────────────────────────── */
.dive-advanced { margin-top: var(--section-gap); }
.dive-advanced-body {
    display: grid;
    gap: var(--panel-gap);
    padding-top: var(--space-4);
}

/* §12 section-level Edit. A real 44px control per section, replacing the need to
   hit one of ~40 16px pencils. The pencils remain for single-field tweaks. */
.detail-section-edit {
    flex: 0 0 auto;
    min-height: var(--control-sm);
    padding: 0 var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-subtle);
    color: var(--text-muted);
    font: inherit;
    font-size: var(--font-size-xs);
    font-weight: 600;
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-standard),
                color var(--duration-fast) var(--ease-standard),
                background-color var(--duration-fast) var(--ease-standard);
}
.detail-section-edit:hover { border-color: var(--accent); color: var(--accent); }
.detail-section-edit.is-active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
}
@media (max-width: 720px) {
    /* Touch: the section control is the primary edit affordance, so it gets the
       full target. */
    .detail-section-edit { min-height: var(--touch-target); }
}

/* ══════════════════════════════════════════════════════════════════════════
   Mobile audit fixes (capture/audit-mobile.mjs)
   ══════════════════════════════════════════════════════════════════════════ */

/* A grid/flex item's automatic minimum size is min-content, and the Tanks and
   Gas mixes tables have a wide min-content — at 390px the two cards blew out to
   651px and took the page 276px past the viewport. min-width:0 lets the track
   govern; the tables then scroll inside their own box. */
.stats-card { min-width: 0; }
.stats-side-by-side > .stats-card { min-width: 0; }
#tanks-box,
#gasmixes-box { overflow-x: auto; }

@media (max-width: 720px) {
    /* ── Touch targets ──────────────────────────────────────────────────
       The per-tile ✎ buttons are 25px and there are ~40 of them. Rather than
       inflate 40 controls, they are hidden on touch and the 44px
       `Edit section` control added in §12 is the edit affordance — which is
       what the spec asked for ("the edit affordance should not be a tiny
       pencil floating next to every field"). They remain on pointer devices,
       where a 25px target is fine and per-field editing is quicker. */
    .stat-tile-edit { display: none; }

    /* The remaining one-off edit controls do get inflated — there are few. */
    .title-edit,
    .row-edit,
    .detail-pencil,
    .chart-zoom-btn {
        min-width: var(--touch-target);
        min-height: var(--touch-target);
    }

    /* Dive-list record cards: the select checkbox rendered 12px and the trip
       group chevron 14px. */
    .dives-table .row-pick,
    #dives-head input[type="checkbox"],
    #pick-all {
        width: calc(22px / var(--dl-zoom));
        height: calc(22px / var(--dl-zoom));
    }
    .dives-table tbody td.pick {
        min-width: var(--touch-target);
        min-height: var(--touch-target);
        display: flex;
        align-items: center;
        justify-content: center;
    }
    /* The label is the actual pointer target, so it carries the full size. */
    .row-pick-hit {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: var(--touch-target);
        min-height: var(--touch-target);
        cursor: pointer;
    }
    .trip-chevron,
    .year-sep .trip-chevron {
        min-width: var(--touch-target);
        min-height: var(--touch-target);
    }
}

/* ── Merge-dives dialog ───────────────────────────────────────────────── */
/* Mounted inside DL.Modal.confirm's node slot (browse/merge.js), so the card
   chrome, the backdrop and the buttons are the shared modal's; only the
   question's own body lives here. The before→after strip is deliberately the
   same shape as the profile editor's .pe-preview — it answers the same
   question about the same four numbers, and two different looks for one idea
   would just make the diver re-read it. */
.merge-body { display: flex; flex-direction: column; gap: var(--space-3); }

.merge-parts {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.merge-part {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-subtle);
}
.merge-part.is-survivor {
    border-color: var(--accent);
    background: var(--accent-soft);
}
.merge-part-when {
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
}
.merge-part-main {
    display: flex;
    flex-direction: column;
    gap: 1px;
    flex: 1 1 auto;
    min-width: 0;
}
.merge-part-name {
    color: var(--text);
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.merge-part-figs { color: var(--text-muted); font-size: var(--font-size-xs); }
.merge-part-role {
    color: var(--text-subtle);
    font-size: var(--font-size-xs);
    text-align: right;
    flex: 0 0 auto;
}
/* The surface break between two parts: kept in the merged dive, so it is
   drawn as part of the list rather than as a divider between two lists. */
.merge-gap {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 var(--space-3);
    color: var(--text-subtle);
    font-size: var(--font-size-xs);
}
.merge-gap-mark { color: var(--text-subtle); }

.merge-after {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border);
}
.merge-cell { display: flex; flex-direction: column; gap: 2px; }
.merge-cell-label { font-size: var(--font-size-xs); color: var(--text-muted); }
.merge-cell-value { display: inline-flex; align-items: baseline; gap: var(--space-2); }
.merge-was   { color: var(--text-subtle); text-decoration: line-through; }
.merge-arrow { color: var(--text-subtle); }
.merge-now   { color: var(--accent); font-weight: 600; }

.merge-note { margin: 0; font-size: var(--font-size-xs); color: var(--text-muted); }
.merge-warn {
    margin: 0;
    color: var(--warn);
    font-size: var(--font-size-xs);
}

/* ── Profile editor: typed split point ────────────────────────────────── */
/* The chart's drag-a-range action is still there; this is the same split
   with the time written down, for the diver who knows they surfaced at
   24:30 and should not have to hit that minute with a pointer. The 📍
   button arms the chart for one click (DL.Browse.Chart beginPickMode) and
   fills the field beside it. */
.pe-split {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4) 0;
}
.pe-split-title { color: var(--text); font-size: var(--font-size-sm); }
.pe-split-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
}
.pe-split-field { display: flex; flex-direction: column; gap: 2px; }
.pe-split-label { font-size: var(--font-size-xs); color: var(--text-muted); }
.pe-split-input { display: inline-flex; align-items: stretch; gap: var(--space-1); }
.pe-split-time {
    width: 7ch;
    /* The global `* { box-sizing: border-box }` would eat the padding out of
       a 7ch field and clip "1:04:30"; content-box keeps the digits. */
    box-sizing: content-box;
    min-height: var(--control-sm);
    padding: 0 var(--space-2);
    background: var(--bg-subtle);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-variant-numeric: tabular-nums;
}
.pe-split-time:focus-visible {
    outline: var(--focus-width) solid var(--focus-ring);
    outline-offset: var(--focus-offset);
}
.pe-split-pick {
    min-width: var(--control-sm);
    min-height: var(--control-sm);
    padding: 0 var(--space-2);
    background: var(--bg-subtle);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.pe-split-pick:hover { border-color: var(--border-strong); }
.pe-split-pick.is-picking {
    border-color: var(--accent);
    color: var(--accent);
}
.pe-split-parts { margin: 0; font-size: var(--font-size-xs); color: var(--text-muted); }
.pe-split-err   { margin: 0; font-size: var(--font-size-xs); color: var(--err); }
.pe-split-hint  { margin: 0; font-size: var(--font-size-xs); color: var(--text-muted); }

/* ==== 01 Dive detail redesign (dive.html) ====================================
   Page-level layout for browse/dive.html on top of the shared component layer
   (dl-page-header, dl-quick-metrics, dl-section-nav, dl-data-panel, dl-kv,
   dl-empty-state, dl-state-badge, DL.UI.SaveBar). Everything is scoped under
   .dive-page; .dl-* components are positioned here, never restyled. */

/* ── Header ─────────────────────────────────────────────────────────────── */
.dive-page .dive-header { align-items: flex-end; }
.dive-header__top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin: 0 0 var(--space-2);
}
.dive-header__top .dl-page-header__back { margin: 0 0 0 calc(-1 * var(--space-2)); }
.dive-page .dive-nav {
    display: inline-flex;
    gap: var(--space-1);
    margin: 0;
}
.dive-page .dive-nav-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: var(--control-sm);
    padding: 0 var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--bg-card);
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    font-weight: 500;
    text-decoration: none;
}
.dive-page .dive-nav-link .dl-icon { width: 1.1em; height: 1.1em; }
@media (hover: hover) {
    .dive-page .dive-nav-link:hover { color: var(--accent); border-color: var(--accent); background: var(--bg-card); }
}
.dive-page .dive-nav-link.is-disabled { opacity: .45; pointer-events: none; }
.dive-page .dive-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
}
.dive-page .dive-title .title-text { min-width: 0; }
.dive-page .dive-title .title-edit-input {
    font-size: var(--font-size-xl);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-sm);
    max-width: 36rem;
    flex: 1 1 16rem;
}
.dive-page .dive-share-actions { margin: 0; }
.dive-page .dl-page-header__eyebrow:empty { display: none; }
.dive-page .dl-page-header__eyebrow { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.dive-page .hero-signed-badge {
    margin: 0;
    padding: 1px var(--space-2);
    font-size: var(--font-size-xs);
    letter-spacing: 0;
    text-transform: none;
    background: var(--accent-soft);
}
.dive-header__site-thumb {
    width: calc(22px / var(--dl-zoom));
    height: calc(22px / var(--dl-zoom));
    border-radius: var(--radius-pill);
    object-fit: cover;
    border: 1px solid var(--border);
    background: var(--bg-elev);
    flex: 0 0 auto;
}
.dive-header__rating { margin-left: calc(-1 * var(--space-1)); }
.dive-header__rating .stars { display: inline-flex; gap: 0; letter-spacing: 0; }
.dive-header__rating .stars > span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: calc(28px / var(--dl-zoom));
    min-height: calc(28px / var(--dl-zoom));
    font-size: var(--font-size-lg);
    line-height: 1;
}
@media (max-width: 720px) {
    .dive-header__rating .stars > span { min-width: calc(36px / var(--dl-zoom)); min-height: calc(36px / var(--dl-zoom)); }
}

/* ── Alerts (deleted / deco) ────────────────────────────────────────────── */
.dive-alert {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    margin: 0 0 var(--space-4);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    color: var(--text);
    font-size: var(--font-size-md);
    line-height: var(--line-normal);
}
.dive-alert[hidden] { display: none; }
.dive-alert .dl-icon { flex: 0 0 auto; width: var(--icon-md); height: var(--icon-md); }
.dive-alert--err  { background: var(--err-soft);  border-color: var(--err); }
.dive-alert--err .dl-icon  { color: var(--err); }
.dive-alert--warn { background: var(--warn-soft); border-color: var(--warn); }
.dive-alert--warn .dl-icon { color: var(--warn); }

/* ── Summary + section navigator ────────────────────────────────────────── */
.dive-summary { margin: 0 0 var(--space-4); }
.dive-section-nav { margin: 0 0 var(--space-4); }

/* ── Profile card ───────────────────────────────────────────────────────── */
.dive-page .dive-profile-card.chart-card.chart-card-large {
    min-height: 0;
    margin: 0 0 var(--section-gap);
    padding-top: var(--space-3);
    border: 1px solid var(--border);
}
.dive-page .dive-profile-head {
    justify-content: flex-start;
    flex-wrap: wrap;
    padding: 0 var(--space-4) var(--space-2);
}
.dive-profile-title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: 650;
    color: var(--text);
}
.dive-profile-title .dl-icon { color: var(--accent); }
.dive-profile-head__spacer { flex: 1 1 auto; }
.dive-page .dive-profile-edited,
.dive-page .dive-profile-synthetic,
.dive-page .dive-profile-merged { margin-right: 0; }
.dive-page .dive-profile-card #chart-host { min-height: 0; }
.dive-page .chart-wrap { height: clamp(380px, 60vh, 760px); }
/* A hand-logged dive: one quiet line, not an empty 600 px frame. */
.dive-page .dive-profile-card--empty { padding-bottom: var(--space-3); }
.dive-page .dive-profile-card--empty #chart-host { padding: 0 var(--space-4); }
.dive-page .dive-profile-card + .dive-bookmarks-card { margin-top: calc(var(--space-4) - var(--section-gap)); }

/* ── Cards (story, bookmarks, signatures) ───────────────────────────────── */
.dive-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
    padding: var(--panel-padding);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}
.dive-card[hidden] { display: none; }
.dive-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    min-height: var(--control-sm);
}
.dive-card__title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    color: var(--text);
    font-size: var(--font-size-lg);
    font-weight: 650;
    line-height: var(--line-tight);
    text-transform: none;
    letter-spacing: 0;
    opacity: 1;
}
.dive-card__title .dl-icon { color: var(--accent); flex: 0 0 auto; }
.dive-card__count { color: var(--text-muted); font-size: var(--font-size-sm); font-weight: 400; font-variant-numeric: tabular-nums; }
.dive-card__count:empty { display: none; }
.dive-card__badge { display: inline-flex; }
.dive-card__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.dive-card__foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
.dive-card__foot[hidden] { display: none; }
.dive-card .detail-empty { margin: 0; color: var(--text-muted); font-size: var(--font-size-sm); }
.dive-bookmarks-card,
.dive-signatures-card { margin: 0 0 var(--section-gap); }

/* ── The dive (story) ───────────────────────────────────────────────────── */
.dive-story { margin: 0 0 var(--section-gap); }
.dive-story-grid {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--space-4);
    align-items: start;
}
.dive-story-grid > .dive-notes-card   { grid-column: span 7; }
.dive-story-grid > .dive-media-card   { grid-column: span 5; }
.dive-story-grid > #animals-card,
.dive-story-grid > #buddies-card,
.dive-story-grid > .dive-where-card,
.dive-story-grid > .dive-logbook-card { grid-column: span 6; }
@media (max-width: 960px) {
    .dive-story-grid > * { grid-column: 1 / -1 !important; }
}
.dive-notes-text {
    margin: 0;
    color: var(--text);
    font-size: var(--font-size-md);
    line-height: var(--line-relaxed);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.dive-page .dive-notes-input {
    min-height: calc(180px / var(--dl-zoom));
    font-size: var(--font-size-md);
    line-height: var(--line-normal);
    padding: var(--space-3);
    border: 1px solid var(--border);
}
.dive-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.dive-field + .dive-field,
.dive-card > div:empty + .dive-field { margin-top: var(--space-1); }
.dive-field__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    min-height: var(--control-sm);
}
.dive-field__label {
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    font-weight: 600;
    letter-spacing: .02em;
}
.dive-field .dlf-field { margin: 0; min-width: 0; }
.dive-manage-btn { color: var(--text-muted); }
.dive-page .dive-tags-field { min-width: 0; }
.dive-page .site-cell { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.dive-page .site-link-host {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    min-width: 0;
}
.dive-page .site-link-host:empty { display: none; }
.dive-page .site-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-width: 0;
    max-width: 100%;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
}
.dive-page .site-link > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dive-page .site-map-btn { width: auto; height: auto; }
.dive-media-card .media-preview-grid { margin: 0; }

/* ── Technical panels ───────────────────────────────────────────────────── */
.dive-tech { margin: 0 0 var(--section-gap); }
.dive-tech-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-4);
    align-items: start;
}
@media (max-width: 960px) {
    .dive-tech-grid { grid-template-columns: minmax(0, 1fr); }
}
.dive-panel .dl-kv { grid-template-columns: repeat(auto-fill, minmax(min(100%, 10.5rem), 1fr)); }
.dive-panel .dl-data-panel__hint { flex: 1 1 auto; text-align: right; }
.dive-panel .dl-data-panel__hint .dl-state-badge { margin-left: auto; }
.dive-panel--deco[open] { border-color: var(--border-strong); }
.dive-panel__edit { flex: 0 0 auto; }
.dive-panel__empty-toggle { margin-top: var(--space-3); }
.dive-panel__note { margin: var(--space-4) 0 0; }
/* Readiness values carry "duration + newline + wall clock": two calm lines. */
.dive-panel--ready .dl-kv__value { white-space: pre-line; }
.dive-page .dl-kv__row.is-editing { grid-column: 1 / -1; }
.dive-page .dl-kv__row.is-editing .dl-kv__value { display: block; }
.dive-page .dive-pencil { margin: calc(-1 * var(--space-2)) 0; color: var(--text-subtle); }
@media (hover: hover) {
    .dive-page .dive-pencil:hover { color: var(--accent); }
}
.dive-page .tile-edit-row {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-2) var(--space-3);
}
.dive-page .tile-edit-row > .tile-edit-field { flex: 1 1 12rem; min-width: 0; max-width: 24rem; }
.dive-page .tile-edit-row > .dlf-field { flex: 1 1 14rem; max-width: 24rem; margin: 0; }
.dive-page .tile-edit-hint { margin-top: var(--space-1); color: var(--text-muted); font-size: var(--font-size-xs); }
.dive-kv-hint {
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    font-size: var(--font-size-sm);
    text-decoration: underline;
    cursor: pointer;
}
.dive-page .cond-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    margin-top: var(--space-4);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
}
.dive-page .cond-caption { font-size: var(--font-size-xs); }
.dive-page .cond-override-badge { font-size: var(--font-size-xs); }
.dive-subpanel { margin-top: var(--space-5); min-width: 0; }
.dive-subpanel[hidden] { display: none; }
.dive-subpanel__title {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin: 0 0 var(--space-2);
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    font-weight: 650;
}
.dive-panel .dl-kv + .dive-subpanel,
.dive-panel .dive-panel__empty-toggle + .dive-subpanel { margin-top: var(--space-5); }
.dive-panel .dl-data-panel__body > .dive-subpanel:first-child,
.dive-panel .dl-data-panel__body > .computer-card:first-child { margin-top: 0; }
.dive-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.dive-page .detail-table { font-size: var(--font-size-sm); }
.dive-page .detail-table td { border-bottom-color: var(--border); }
.dive-page .tank-manage-row { margin-top: var(--space-2); }
.dive-page .computer-card { margin: 0; }
.dive-advanced-sec { margin: 0 0 var(--section-gap); }
.dive-page .signature-disclaimer { margin: 0; font-size: var(--font-size-sm); color: var(--text-muted); }

/* Tanks on a phone: one stacked card per tank instead of a table that hides
   its last columns off-screen. Labels come from data-label (renderTanks). */
@media (max-width: 720px) {
    .dive-page .dive-tanks-table thead { display: none; }
    .dive-page .dive-tanks-table,
    .dive-page .dive-tanks-table tbody { display: block; }
    .dive-page .dive-tanks-table tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: var(--space-2) var(--space-3);
        padding: var(--space-3) 0;
        border-bottom: 1px solid var(--border);
    }
    .dive-page .dive-tanks-table td {
        display: flex;
        flex-direction: column;
        gap: 2px;
        padding: 0;
        border: 0;
        min-width: 0;
        white-space: normal;
    }
    .dive-page .dive-tanks-table td::before {
        content: attr(data-label);
        color: var(--text-muted);
        font-size: var(--font-size-xs);
        font-weight: 600;
    }
    .dive-page .dive-tanks-table td.tank-type-cell { grid-column: 1 / -1; min-width: 0; }
    .dive-page .tank-number-input { width: 100%; min-height: var(--control-md); }
}

/* ── Phone ──────────────────────────────────────────────────────────────── */
@media (max-width: 720px) {
    .dive-page .dive-header { gap: var(--space-2); }
    .dive-page .dive-header__top { margin-bottom: 0; }
    .dive-page .dive-nav-link .dive-nav-label { display: none; }
    .dive-page .dive-nav-link { min-width: var(--touch-target); min-height: var(--touch-target); justify-content: center; padding: 0; }
    .dive-page .dl-page-header__meta { margin-top: var(--space-2); gap: var(--space-1) var(--space-3); }
    /* Rating gets its own left-aligned line instead of floating mid-row. */
    .dive-page .dive-header__rating { order: 1; flex: 0 0 100%; }
    .dive-page .dive-header__meta > .dl-page-header__meta-item:not(.dive-header__rating) { order: 0; }
    .dive-page .dive-share-actions { flex-wrap: nowrap; }
    .dive-page .dive-share-actions > .dl-action-menu,
    .dive-page .dive-share-actions > .dive-action-analyze { flex: 1 1 auto; min-width: 0; }
    .dive-page .dive-share-actions .dl-btn { font-size: var(--font-size-sm); gap: var(--space-1); }
    .dive-page .dive-share-actions > .dive-action-more { flex: 0 0 auto; }
    .dive-page .dive-share-actions .dl-btn:not(.dl-btn--icon) { width: 100%; padding-left: var(--space-2); padding-right: var(--space-2); }
    .dive-page .dive-share-actions .dl-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Labels beat icons when a phone row has room for only one of them. */
    .dive-page .dive-share-actions .dl-btn:not(.dl-btn--icon) > .dl-icon:first-child { display: none; }
    .dive-page .dive-share-actions { gap: var(--space-1); }
    .dive-page .chart-wrap { height: clamp(300px, 56vh, 460px); }
    .dive-card { padding: var(--space-4); }
    /* The "n recorded · m not recorded" count is a desktop nicety; on a
       phone the title needs the row (a warning badge still shows). */
    .dive-panel .dive-panel__count { display: none; }
    .dive-panel .dl-data-panel__title { flex: 1 1 auto; }
}
/* ==== end 01 Dive detail redesign ==== */


/* ==== 01 Dive chart redesign (dive-chart.js) ==== */
/* Chip bar = [chip list][safety-stop status][zoom toolbar]. Desktop: the
   list wraps and the toolbar sits at the right end. Phone: status + toolbar
   share the first row and the chips become one sideways-scrolling row,
   instead of three wrapped rows above the plot. */
.channel-chips {
    align-items: center;
    gap: var(--space-2);
}
.channel-chips__list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
}
.channel-chip {
    font-size: var(--font-size-sm);
    min-height: calc(32px / var(--dl-zoom));
}
.channel-chip:focus-visible,
.chart-zoom-btn:focus-visible {
    outline: var(--focus-width) solid var(--focus-ring);
    outline-offset: var(--focus-offset);
}
.safety-stop-chip { gap: 6px; font-weight: 600; }
.safety-stop-chip .dl-icon { flex: none; }

/* Zoom / help toolbar: icon buttons, 34 px on a desktop, 44 px on touch
   (the rule lower down and the ≤720 px block above). */
.chart-zoom-btn {
    width: calc(34px / var(--dl-zoom));
    height: calc(34px / var(--dl-zoom));
    border-radius: var(--radius-sm, 6px);
}
.chart-zoom-btn .dl-icon { width: var(--icon-sm); height: var(--icon-sm); }
.chart-zoom-btn[aria-expanded="true"] {
    background: var(--accent-soft);
    border-color: var(--accent);
}
.chart-help-pop {
    width: max-content;
    max-width: min(340px, 80vw);
    font-size: var(--font-size-sm);
    line-height: var(--line-normal, 1.45);
}
.chart-help-pop__list {
    margin: 0;
    padding-left: 1.1em;
    display: grid;
    gap: var(--space-1, 4px);
}

/* A horizontal finger drag on the profile selects a stretch (pointer
   events), a vertical one still scrolls the page. Pinch stays the
   browser's page zoom. */
.dive-chart { touch-action: pan-y pinch-zoom; }

/* Range-selection panel: title + span, a label-above-value grid (4 across,
   2 on a phone), then the actions in their own row. */
.chart-range-panel {
    padding: var(--space-3) var(--space-4);
    /* The panel is the host card's last child; without this it sits flush
       on the card's bottom edge. */
    margin-bottom: var(--space-3);
}
.chart-range-panel .range-head {
    align-items: baseline;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}
.chart-range-panel .range-title {
    margin-right: 0;
    font-size: var(--font-size-md);
}
.chart-range-panel .range-span {
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
}
.chart-range-panel .range-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-3) var(--space-4);
    margin: 0;
}
.chart-range-panel .range-cell {
    flex-direction: column;
    justify-content: flex-start;
    gap: 2px;
    padding: 0;
    border-bottom: 0;
    min-width: 0;
}
.chart-range-panel .range-label {
    font-size: var(--font-size-xs);
    color: var(--text-muted);
}
.chart-range-panel .range-value {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: 600;
    color: var(--text);
    overflow-wrap: anywhere;
}
.chart-range-panel .range-value.is-empty {
    color: var(--text-subtle);
    font-weight: 400;
}
.chart-range-panel .range-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
}

@media (max-width: 720px) {
    .channel-chips__list {
        order: 2;
        flex: 1 0 100%;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 2px;
    }
    .channel-chips__list::-webkit-scrollbar { display: none; }
    .channel-chips__list .channel-chip { flex: 0 0 auto; white-space: nowrap; }
    .channel-chips__list .safety-stop-chip { order: -1; }
    .chart-range-panel { padding: var(--space-3); }
    .chart-range-panel .range-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .chart-range-panel .range-actions .dl-btn { flex: 1 1 auto; }
}
@media (pointer: coarse) {
    .chart-zoom-btn {
        width: var(--touch-target);
        height: var(--touch-target);
    }
    .channel-chips .channel-chip { min-height: var(--touch-target); }
}
/* ==== end 01 Dive chart redesign ==== */

/* ==== 03 Browse list redesign (browse/index.html) ============================
   Every rule is scoped under body.bl-page: addlog and the dive page load this
   stylesheet too. Shared components (.dl-btn, .dl-dive-row, .dl-context-
   toolbar, .dl-chip…) are only positioned here, never re-skinned. */

/* ── Header + control row ──────────────────────────────────────────────── */
.bl-page .bl-header { margin-bottom: var(--space-4); }

/* The page's one sticky area on a desktop: the control row (or, while
   dives are selected, the selection bar in its place). The table's column
   headings pin flush under it via the measured --browse-toolbar-h. */
.bl-page .bl-controls {
    position: sticky;
    top: var(--dl-header-h, var(--header-offset));
    z-index: 20;
    margin-bottom: var(--space-3);
}
.bl-page .bl-controls .dl-context-toolbar {
    margin: 0;
    box-shadow: var(--shadow-xs);
}
.bl-page .bl-toolbar__find { flex: 1 1 24rem; flex-wrap: nowrap; }
.bl-page .bl-toolbar__actions { margin-left: auto; }

.bl-page .bl-search {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 1 1 16rem;
    min-width: 0;
    max-width: 30rem;
    height: var(--control-md);
    padding: 0 var(--space-3);
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text-subtle);
    cursor: text;
    transition: border-color var(--duration-fast) var(--ease-standard),
                box-shadow var(--duration-fast) var(--ease-standard);
}
.bl-page .bl-search:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.bl-page .bl-search input {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: var(--text);
    font: inherit;
    font-size: var(--font-size-sm);
    outline: none;
}
.bl-page .bl-search input::placeholder { color: var(--text-subtle); }

/* Small count pill (Filters, More filters). */
.bl-page .bl-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.6em;
    height: 1.6em;
    padding: 0 var(--space-1);
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: var(--text-on-accent);
    font-size: var(--font-size-xs);
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
.bl-page .bl-count[hidden] { display: none; }
.bl-page .bl-filters-btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }

.bl-page .bl-sort-menu .dl-btn { max-width: 17rem; }
.bl-page .bl-sort-menu .dl-btn > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Table / list switch: two icon buttons in one pill. */
.bl-page .bl-viewswitch {
    display: inline-flex;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg-subtle);
}
.bl-page .bl-viewswitch .dl-btn {
    min-width: var(--control-sm);
    min-height: var(--control-sm);
    border-radius: var(--radius-xs);
}
.bl-page .bl-viewswitch .dl-btn[aria-pressed="true"] {
    background: var(--bg-card);
    color: var(--accent);
    box-shadow: var(--shadow-xs);
}

/* Selection bar (desktop: replaces the control row while dives are picked). */
.bl-page .bl-selbar {
    border-color: var(--accent);
    box-shadow: inset 3px 0 0 var(--accent), var(--shadow-xs);
}
.bl-page .bl-selbar__info { flex-wrap: nowrap; }
.bl-page .bl-selbar__count {
    color: var(--text);
    font-weight: 600;
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.bl-page .bl-selbar__all { color: var(--accent); }

/* ── Applied filters ───────────────────────────────────────────────────── */
.bl-page .bl-applied {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-3);
}
.bl-page .bl-applied[hidden] { display: none; }
.bl-page .bl-chip { max-width: 100%; padding-right: var(--space-2); }
.bl-page .bl-chip__text {
    min-width: 0;
    max-width: 24rem;
    overflow: hidden;
    text-overflow: ellipsis;
}
.bl-page .bl-chip__x { width: 1.5rem; height: 1.5rem; }

/* ── Filter drawer ─────────────────────────────────────────────────────── */
.bl-page .bl-drawer {
    margin: 0 0 var(--space-4);
    padding: var(--panel-padding);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}
.bl-page .bl-drawer[hidden] { display: none; }
.bl-page .bl-drawer__grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 11.5rem), 1fr));
    align-items: end;
}
.bl-page .bl-drawer .dlf-field { margin: 0; min-width: 0; }
/* Labels stay one line so every cell aligns at the bottom. */
.bl-page .bl-drawer .dlf-label {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Long chip labels ("Baller Balázs (Balázs)") wrap inside the cell. The
   DL.Combo popup is portalled, so nothing here may clip it either way. */
.bl-page .bl-drawer .dlf-tag-chip { max-width: 100%; white-space: normal; }
.bl-page .bl-drawer .dlf-tag-chip-label { overflow-wrap: anywhere; }

.bl-page .bl-drawer__more {
    margin-top: var(--space-3);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border);
}
.bl-page .bl-drawer__more > summary {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--touch-target);
    color: var(--text-muted);
    font-size: var(--font-size-sm);
    font-weight: 600;
    list-style: none;
    cursor: pointer;
}
.bl-page .bl-drawer__more > summary::-webkit-details-marker { display: none; }
.bl-page .bl-drawer__more > summary:hover { color: var(--text); }
.bl-page .bl-drawer__chev { transition: transform var(--duration-fast) var(--ease-standard); }
.bl-page .bl-drawer__more[open] .bl-drawer__chev { transform: rotate(90deg); }
.bl-page .bl-drawer__more[open] > summary { margin-bottom: var(--space-2); }
.bl-page .bl-drawer__row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3) var(--space-6);
    margin-top: var(--space-4);
}
.bl-page .bl-modes-field { display: flex; flex-direction: column; gap: var(--space-1); }
.bl-page .bl-modes-label { color: var(--text-muted); font-size: var(--font-size-xs); font-weight: 600; }
.bl-page .bl-modes { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.bl-page .bl-mode-chip {
    min-height: var(--control-sm);
    padding: 0 var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--bg-subtle);
    color: var(--text);
    font: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-standard),
                background-color var(--duration-fast) var(--ease-standard);
}
.bl-page .bl-mode-chip:hover { border-color: var(--accent); }
.bl-page .bl-mode-chip[aria-pressed="true"] {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

/* ── Checkboxes (themed, never the browser's white square) ─────────────── */
.bl-page .bl-pick {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: calc(32px / var(--dl-zoom));
    height: calc(32px / var(--dl-zoom));
    flex: 0 0 auto;
    cursor: pointer;
}
.bl-page .bl-check {
    appearance: none;
    -webkit-appearance: none;
    display: grid;
    place-content: center;
    width: calc(18px / var(--dl-zoom));
    height: calc(18px / var(--dl-zoom));
    margin: 0;
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-xs);
    background: var(--bg-subtle);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-standard),
                border-color var(--duration-fast) var(--ease-standard);
}
.bl-page .bl-check::after {
    content: "";
    width: calc(10px / var(--dl-zoom));
    height: calc(5px / var(--dl-zoom));
    border: 2px solid var(--text-on-accent);
    border-top: 0;
    border-right: 0;
    transform: translateY(-15%) rotate(-45deg);
    opacity: 0;
}
.bl-page .bl-pick:hover .bl-check { border-color: var(--accent); }
.bl-page .bl-check:checked,
.bl-page .bl-check:indeterminate { background: var(--accent); border-color: var(--accent); }
.bl-page .bl-check:checked::after { opacity: 1; }
.bl-page .bl-check:indeterminate::after {
    opacity: 1;
    height: 0;
    border-left: 0;
    transform: none;
}
.bl-page .bl-check:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 2px; }
.bl-page .bl-check:disabled { opacity: .6; cursor: progress; }

/* ── Table view (desktop) ──────────────────────────────────────────────── */
.bl-page .bl-table-wrap {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}
.bl-page .bl-table-wrap[hidden] { display: none; }
.bl-page .bl-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--font-size-sm);
}
.bl-page .bl-table th,
.bl-page .bl-table td {
    height: var(--row-height);
    padding: 0 var(--space-3);
    border-bottom: 1px solid var(--border);
    text-align: left;
    vertical-align: middle;
    white-space: nowrap;
}
.bl-page .bl-table thead th {
    position: sticky;
    top: calc(var(--dl-header-h, var(--header-offset)) + var(--browse-toolbar-h, 56px));
    z-index: 4;
    background: var(--bg-card);
    color: var(--text-muted);
    font-size: var(--font-size-xs);
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}
.bl-page .bl-table thead th:first-child { border-top-left-radius: var(--radius-md); }
.bl-page .bl-table thead th:last-child  { border-top-right-radius: var(--radius-md); }
.bl-page .bl-table tbody tr:last-child > td { border-bottom: 0; }
.bl-page .bl-table .is-num { text-align: right; font-variant-numeric: tabular-nums; }
.bl-page .bl-table th[aria-sort="ascending"],
.bl-page .bl-table th[aria-sort="descending"] { color: var(--accent); }

.bl-page .bl-sort {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    min-height: var(--control-sm);
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
}
.bl-page .bl-sort:hover { color: var(--text); }
.bl-page .bl-sort__ind { display: inline-flex; width: 1em; justify-content: center; }

.bl-page .bl-col-pick { width: 1%; padding-right: 0 !important; padding-left: var(--space-2) !important; }
.bl-page .bl-col-number { width: 1%; color: var(--text-subtle); font-variant-numeric: tabular-nums; }
.bl-page .bl-col-date { width: 1%; }
/* The site takes whatever width is left, and ellipsises inside it. */
.bl-page td.bl-col-site { width: 100%; max-width: 1px; }
.bl-page .bl-col-stars { width: 1%; }
.bl-page .bl-col-badges { width: 1%; text-align: right !important; }

.bl-page .bl-tr { cursor: pointer; }
.bl-page .bl-tr > td { transition: background-color var(--duration-fast) var(--ease-standard); }
@media (hover: hover) {
    .bl-page .bl-tr:hover > td { background: var(--bg-subtle); }
}
.bl-page .bl-tr.is-selected > td { background: var(--accent-soft); }
.bl-page .bl-tr.is-selected > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.bl-page .bl-tr.is-deleted > td:not(.bl-col-badges):not(.bl-col-pick) { opacity: .55; }

.bl-page .bl-open {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);
    color: var(--text);
    text-decoration: none;
}
.bl-page .bl-open:hover .bl-date { text-decoration: underline; }
.bl-page .bl-date { font-weight: 500; }
.bl-page .bl-time { color: var(--text-muted); font-variant-numeric: tabular-nums; }

.bl-page .bl-site {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    overflow: hidden;
    /* Room for .site-pick's negative left margin — without it the
       overflow clip shaves the button's left border on hover. */
    margin-left: calc(-1 * var(--space-2));
    padding-left: var(--space-2);
}
.bl-page .site-pick {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex: 0 1 auto;
    min-width: 0;
    max-width: 65%;
    min-height: var(--control-sm);
    margin-left: calc(-1 * var(--space-2));
    padding: 0 var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.bl-page .site-pick:hover,
.bl-page .site-pick:focus-visible,
.bl-page .site-pick.dl-combo-host {
    border-color: var(--border-strong);
    background: var(--bg-elev);
}
.bl-page .site-pick-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bl-page .site-pick-chev { flex: 0 0 auto; color: var(--text-subtle); }
.bl-page .site-pick.is-empty .site-pick-name { color: var(--text-muted); font-style: italic; font-weight: 400; }
.bl-page .bl-site__where,
.bl-page .bl-site__name {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--text-muted);
    font-size: var(--font-size-xs);
}
.bl-page .bl-site__name { font-style: italic; }
.bl-page .bl-mode { flex: 0 0 auto; }
.bl-page .dl-dive-row__sub .bl-mode { margin-left: var(--space-2); vertical-align: middle; }

/* Inline rating: real buttons with a touchable size. The cell itself swallows
   clicks, so a near miss never opens the dive. */
.bl-page .bl-stars { display: inline-flex; gap: 0; }
.bl-page .bl-stars > button {
    padding: 0 1px;
    border: 0;
    background: none;
    font: inherit;
    font-size: var(--font-size-md);
    line-height: 1;
    cursor: pointer;
}
.bl-page .bl-stars > button { transition: color 80ms linear, transform 80ms ease-out; }
.bl-page .bl-stars > button:hover { transform: scale(1.2); }
.bl-page .bl-stars > .star-on { color: var(--favorite); }
.bl-page .bl-stars > .star-off { color: var(--border-strong); }
.bl-page .bl-stars:hover > .star-off { color: var(--text-subtle); }
.bl-page .bl-stars > button:focus-visible { outline: var(--focus-width) solid var(--focus-ring); outline-offset: 0; border-radius: var(--radius-xs); }
.bl-page td.bl-col-stars { cursor: default; }

.bl-page .bl-badge { font-size: var(--font-size-xs); white-space: nowrap; }
.bl-page .bl-badge + .bl-badge { margin-left: var(--space-1); }
.bl-page .bl-badge--deleted { color: var(--text-muted); }

/* ── Group headers (trip / year) ───────────────────────────────────────── */
.bl-page .bl-table tr.bl-group > td {
    height: auto;
    padding: var(--space-2) var(--space-3);
    background: var(--bg-subtle);
}
.bl-page .bl-list > li.bl-group {
    padding: var(--space-2) var(--space-4);
    background: var(--bg-subtle);
    border-top: 1px solid var(--border);
}
.bl-page .bl-list > li.bl-group:first-child { border-top: 0; }
.bl-page .bl-group__inner {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}
.bl-page .bl-group__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    min-height: var(--control-sm);
    padding: 0 var(--space-2) 0 0;
    border: 0;
    background: none;
    color: var(--text);
    font: inherit;
    font-size: var(--font-size-sm);
    text-align: left;
    cursor: pointer;
}
.bl-page .bl-group__toggle.is-static { cursor: default; }
.bl-page .bl-group__chev {
    flex: 0 0 auto;
    color: var(--text-subtle);
    transition: transform var(--duration-fast) var(--ease-standard);
}
.bl-page .bl-group.is-collapsed .bl-group__chev { transform: rotate(-90deg); }
.bl-page .bl-group__title { font-weight: 650; white-space: nowrap; }
.bl-page .bl-group__dates { color: var(--text-muted); white-space: nowrap; }
.bl-page .bl-group__count { color: var(--text-subtle); font-size: var(--font-size-xs); white-space: nowrap; }
.bl-page .bl-group__only { margin-left: auto; }
@media (hover: hover) and (pointer: fine) {
    .bl-page .bl-group__only { opacity: 0; transition: opacity var(--duration-fast) var(--ease-standard); }
    .bl-page .bl-group:hover .bl-group__only,
    .bl-page .bl-group__only:focus-visible { opacity: 1; }
}
.bl-page .bl-hidden { display: none !important; }

/* ── List view (dl-dive-row) ───────────────────────────────────────────── */
.bl-page .bl-list[hidden] { display: none; }
.bl-page .bl-row {
    grid-template-columns: auto auto minmax(0, 1fr) auto auto;
    grid-template-areas: "select lead main stats end";
    padding-left: var(--space-2);
    cursor: pointer;
}
.bl-page .bl-row .dl-dive-row__select { grid-area: select; }
@media (hover: hover) {
    .bl-page .bl-row:hover { background: var(--bg-subtle); }
    .bl-page .bl-row.dl-dive-row--selected:hover { background: var(--accent-soft); }
}
.bl-page .bl-row.is-deleted > :not(.dl-dive-row__end):not(.dl-dive-row__select) { opacity: .55; }
.bl-page .bl-row .dl-dive-row__title { text-decoration: none; }
.bl-page .bl-row .dl-dive-row__title:hover { text-decoration: underline; }
.bl-page .bl-row .dl-dive-row__title.is-empty { color: var(--text-muted); font-style: italic; font-weight: 500; }
.bl-page .bl-row .bl-hint { margin-left: var(--space-2); }
.bl-page .bl-stat--rating { display: none; }
.bl-page .bl-stat--rating .dl-icon { color: var(--favorite, var(--warn)); }
.bl-page .bl-list .dl-dive-row__stats { min-width: 0; }
@media (max-width: 1100px) {
    .bl-page .bl-list .bl-stat--sac { display: none; }
}

/* List head: select-all + range, joined to the list below it. */
.bl-page .bl-listhead {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--control-md);
    padding: 0 var(--space-4) 0 var(--space-2);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-bottom: 0;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    color: var(--text-muted);
    font-size: var(--font-size-sm);
}
.bl-page .bl-listhead[hidden] { display: none; }
.bl-page .bl-listhead:not([hidden]) ~ .bl-list { border-top-left-radius: 0; border-top-right-radius: 0; }
.bl-page .bl-pick.bl-pick--text { width: auto; gap: var(--space-2); padding-right: var(--space-2); white-space: nowrap; }
.bl-page .bl-listhead__range { margin-left: auto; font-variant-numeric: tabular-nums; }

/* ── Loading ───────────────────────────────────────────────────────────── */
.bl-page .bl-skel {
    display: inline-block;
    height: .85em;
    max-width: 100%;
    vertical-align: middle;
    border-radius: var(--radius-xs);
}
.bl-page li.bl-skel-row .dl-dive-row__main { gap: var(--space-2); }

/* ── Empty state + pager ───────────────────────────────────────────────── */
.bl-page #empty-state { margin-top: var(--space-3); }
.bl-page #empty-state[hidden] { display: none; }
.bl-page .bl-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--space-2) var(--space-4);
    margin-top: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}
.bl-page .bl-pager[hidden] { display: none; }
.bl-page .bl-pager__nav { display: flex; align-items: center; gap: var(--space-3); }
.bl-page .bl-pager__label {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 8rem;
    color: var(--text);
    font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums;
}
.bl-page .bl-pager__page { color: var(--text-muted); font-size: var(--font-size-xs); }
.bl-page .bl-pager__size { margin-left: auto; }

/* ── Tablet: no table, a lighter control row ───────────────────────────── */
@media (max-width: 1023px) {
    .bl-page .bl-pager__size { margin-left: 0; }
}

/* ── Phone ─────────────────────────────────────────────────────────────── */
@media (max-width: 720px) {
    /* Nothing sticky at the top: the only fixed bar on a phone is the
       selection bar at the bottom (dl-context-toolbar--sticky). */
    .bl-page .bl-controls { position: static; }
    .bl-page .bl-toolbar { gap: var(--space-2); }
    .bl-page .bl-toolbar__find { flex: 1 1 100%; }
    .bl-page .bl-search { max-width: none; height: var(--touch-target); }
    .bl-page .bl-toolbar__actions { flex: 1 1 100%; margin-left: 0; flex-wrap: nowrap; }
    .bl-page .bl-sort-menu { flex: 1 1 auto; min-width: 0; }
    .bl-page .bl-sort-menu .dl-btn { width: 100%; max-width: none; justify-content: space-between; }
    .bl-page .bl-mode-chip { min-height: var(--touch-target); }
    .bl-page .bl-drawer__grid { grid-template-columns: 1fr 1fr; }
    .bl-page .bl-drawer__grid > .dlf-field:has(.dlf-tags) { grid-column: 1 / -1; }

    .bl-page .bl-pick { width: var(--touch-target); height: var(--touch-target); }
    .bl-page .bl-row {
        grid-template-columns: auto auto minmax(0, 1fr) auto;
        grid-template-areas: "select lead main end" "select lead stats end";
        column-gap: var(--space-2);
        padding: var(--space-2) var(--space-3) var(--space-2) 0;
    }
    .bl-page .bl-row .dl-dive-row__lead { min-width: 2.8rem; }
    .bl-page .bl-list .dl-dive-row__stats { flex-wrap: wrap; row-gap: 2px; white-space: normal; }
    .bl-page .bl-list .bl-stat--sac { display: inline-flex; }
    .bl-page .bl-stat--rating { display: inline-flex; }
    .bl-page .bl-row__stars { display: none; }
    .bl-page .bl-row .bl-badge { padding: var(--space-1); }
    .bl-page .bl-row .bl-badge__text {
        position: absolute;
        width: 1px; height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
    .bl-page .bl-list > li.bl-group { padding: var(--space-1) var(--space-3); }
    .bl-page .bl-listhead { padding: 0 var(--space-3) 0 0; }

    .bl-page .bl-selbar__count { font-size: var(--font-size-sm); }
    .bl-page .bl-pager { flex-direction: column; }
    .bl-page .bl-pager__nav { width: 100%; justify-content: space-between; }
}

/* ══ TV mode (tasks/tv/03-browse) ══════════════════════════════════════════
   Every rule scoped under .dl-tv (the class dl.js puts on <html> in TV
   mode), tokens only. Old-TV CSS (README D5): no grid gap, inset, clamp(),
   aspect-ratio or :focus-visible, and never raw vh — calc(var(--tv-vh) * N)
   is one logical screen under the CSS zoom. The focus ring itself comes
   from dl-tv.css (.is-focused). */

/* ── List ── */
/* A sticky control row would cover the focused row; DL.TV.reveal() already
   clears the sticky app header. */
.dl-tv .bl-page .bl-controls { position: static; }
.dl-tv .bl-page .bl-list .dl-dive-row {
    min-height: 64px;
    padding: var(--space-3) var(--space-4);
    font-size: var(--font-size-lg);
}
.dl-tv .bl-page .bl-list .dl-dive-row__title { font-size: var(--font-size-xl); }
.dl-tv .bl-page .bl-row.is-focused { background: var(--accent-soft); }
.dl-tv .bl-page .bl-pager .dl-btn { min-height: var(--control-lg); }

/* ── Detail: profile ── */
.dl-tv .dive-page .chart-wrap { height: calc(var(--tv-vh) * 50); }
/* The ring must stay inside the card's overflow: hidden. The clear band it
   draws in is Chart.js layout.padding (dive-chart.js, opts.tv): the canvas
   is sized from the wrap's border box, so CSS padding here would push the
   x-axis title out of the card instead. */
.dl-tv .dive-page .chart-wrap.is-focused { outline-offset: -4px; }
.dl-tv .dive-profile-card.is-tv-full {
    position: fixed;
    left: var(--tv-safe-x);
    right: var(--tv-safe-x);
    top: var(--tv-safe-y);
    bottom: var(--tv-safe-y);
    z-index: 1100;
    margin: 0;
    background: var(--bg-card);
    box-shadow: 0 0 0 4000px var(--overlay);
}
/* 112 px = card head + chip row. */
.dl-tv .dive-profile-card.is-tv-full .chart-wrap {
    height: calc(var(--tv-vh) * 100 - var(--tv-safe-y) * 2 - 112px);
}
.dl-tv .dive-profile-card.is-tv-full .dive-profile-head .dl-btn { display: none; }

.dl-tv .dc-tv-readout {
    position: absolute;
    top: 12px;
    z-index: 2;
    min-width: 220px;
    padding: 12px 16px;
    background: var(--bg-elev);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    font-size: var(--font-size-xl);
    pointer-events: none;
}
.dl-tv .dc-tv-readout[hidden] { display: none; }
.dl-tv .dc-tv-readout.is-left { left: 12px; }
.dl-tv .dc-tv-readout.is-right { right: 12px; }
.dl-tv .dc-tv-readout__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}
.dl-tv .dc-tv-readout__row + .dc-tv-readout__row { margin-top: 4px; }
.dl-tv .dc-tv-readout__label {
    margin-right: 16px;
    color: var(--text-muted);
    font-size: var(--font-size-md);
}
.dl-tv .dc-tv-readout__value {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* ── Detail: story cards without grid gap ── */
.dl-tv .dive-story-grid {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -8px;
}
/* The gutter is a MARGIN: the children are the cards themselves, and a
   gutter padding replaced their own --panel-padding (bodies flush on the
   border — TV spacing pass). `gap: 0` is a reset, not a dependency: the
   desktop grid's gap still applied to this flex row on engines with flex
   gap, so a half-width pair never fitted and every card took a row of its
   own. The basis is a pixel short of half and the card grows into the
   rest, so a rounding error under zoom 0.75 cannot wrap the pair either. */
.dl-tv .dive-story-grid { gap: 0; }
.dl-tv .dive-story-grid > * {
    flex: 1 1 auto;
    width: calc(50% - 17px);
    max-width: calc(50% - 16px);
    margin: 0 8px var(--space-4);
}
.dl-tv .dive-story-grid > .dive-notes-card,
.dl-tv .dive-story-grid > .dive-media-card {
    width: calc(100% - 16px);
    max-width: calc(100% - 16px);
}
.dl-tv .dive-notes-text[data-tv-read] {
    max-height: calc(var(--tv-vh) * 45);
    overflow: auto;
}
.dl-tv #events-box[data-tv-read] {
    max-height: calc(var(--tv-vh) * 45);
    overflow: auto;
}
.dl-tv .dlf-tags.is-readonly { padding: var(--space-2) var(--space-3); }
.dl-tv .dlf-tags.is-readonly .dlf-tags-chips { margin: 0; }
.dl-tv .dive-tv-value { font-size: var(--font-size-lg); }
.dl-tv .dive-tv-value.is-empty { color: var(--text-muted); }
.dl-tv .dive-tank-tv + .dive-tank-tv {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
}

/* ── Detail: media tiles (no iframe in the grid) ── */
.dl-tv .media-tv-grid {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -6px;
}
.dl-tv .media-tv-grid > .media-tile--tv {
    flex: 0 0 33.333%;
    max-width: 33.333%;
    border: 0;
    background: transparent;
    padding: 0 6px;
    margin: 0 0 12px;
    box-sizing: border-box;
}
.dl-tv .media-tile--tv {
    display: block;
    width: 100%;
    color: var(--text);
    text-align: left;
    font: inherit;
    cursor: pointer;
}
.dl-tv .media-tile--tv.is-link-only { cursor: default; color: var(--text-muted); }
.dl-tv .media-tv-thumb {
    position: relative;
    padding-top: 56.25%;               /* 16:9 without aspect-ratio */
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}
.dl-tv .media-tv-thumb img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.dl-tv .media-tv-play {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 56px;
    height: 56px;
    margin: -28px 0 0 -28px;
    border-radius: 50%;
    background: var(--overlay);
    color: var(--text-on-accent);
}
/* The play triangle (the icon set has none). */
.dl-tv .media-tv-play::after {
    content: "";
    position: absolute;
    top: 16px;
    left: 22px;
    border-style: solid;
    border-width: 12px 0 12px 18px;
    border-color: transparent transparent transparent var(--text);
}
.dl-tv .media-tv-caption {
    display: block;
    margin-top: 6px;
    font-size: var(--font-size-md);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.dl-tv .media-tile--tv.is-link-only .media-tv-caption { white-space: normal; }
.dl-tv .media-tile--tv.is-focused { outline: none; }
.dl-tv .media-tile--tv.is-focused .media-tv-thumb {
    outline: var(--tv-focus-width) solid var(--focus-ring);
    outline-offset: var(--tv-focus-offset);
}

/* ── The viewer's gallery / video / embed views (DL.ImageViewer) ── */
.dl-tv .dl-imgview.is-framed .dl-imgview-frame {
    width: 100%;
    max-width: 100%;
    height: 100%;
}
.dl-tv .dl-imgview-video,
.dl-tv .dl-imgview-embed {
    display: block;
    width: 100%;
    height: calc(var(--tv-vh) * 70);
    border: 0;
    background: var(--bg);
}
.dl-tv .dl-imgview-video { object-fit: contain; }
.dl-tv .dl-imgview-stage.is-focused { outline-offset: -4px; }
.dl-tv .dl-imgview-gallery {
    display: flex;
    align-items: baseline;
    padding: 8px 18px 12px;
    color: var(--text-muted);
    font-size: var(--font-size-md);
}
.dl-tv .dl-imgview-gallery__count {
    margin-right: 16px;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--text);
}
