/*
 * .MD — station shell.
 *
 * The discipline: every element here does something. The band selector navigates, the
 * on-air lamp reads the globe's real rotation. Chrome that only looks like an instrument
 * is the failure mode this world is most exposed to.
 *
 * The tuning plate and the frequency numerals were removed at the client's request — the
 * column read as too heavy against the field. The globe is turned by dragging it.
 */

/* The rail is fixed for the whole site, so every page inherits the left navigation
   without restructuring its own layout. Reserving the column on <body> keeps that true
   for the pages that predate this shell as well as the ones built for it. */
body { padding-left: var(--pd-rail); }

.station {
    position: relative;
    z-index: 3;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

/* ---------- Left rail: the band selector ---------- */

.station-rail {
    position: fixed;
    left: 0;
    top: 0;
    width: var(--pd-rail);
    height: 100dvh;
    z-index: 20;
    display: flex;
    flex-direction: column;
    padding: clamp(20px, 3vh, 34px) 0 clamp(18px, 3vh, 28px);
    border-right: 1px solid var(--pd-hairline);
    /* Client: the solid strip "very much cuts the eye". Dropped to a wash so the
       propagation field, its grid and its drifting points stay visible behind the
       band names, with just enough ground left to hold their contrast. */
    background: linear-gradient(to right, color-mix(in oklab, var(--pd-void-0) 46%, transparent), color-mix(in oklab, var(--pd-void-0) 16%, transparent));
    backdrop-filter: blur(14px);
    overflow-y: auto;
    scrollbar-width: thin;
}

/* The .MD wordmark — a drawn dot, not the word "Punct" */
/* Baseline-aligned so the dot reads as the period in ".MD" rather than a bullet
   floating at cap height. It doubles as the on-air lamp, which is the point. */
.wordmark {
    display: flex;
    align-items: baseline;
    gap: 5px;
    padding: 0 var(--pd-rail-pad) clamp(26px, 4vh, 44px);
    text-decoration: none;
}
/* Unlit until the station actually has something on air. A permanently red lamp
   reports nothing, which is the exact chrome this world must avoid. */
.wordmark-dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--pd-ink-faint);
    flex: none;
    align-self: flex-end;
    margin-bottom: 2px;
    transition: background .5s var(--pd-ease), box-shadow .5s var(--pd-ease);
}
.wordmark-dot.is-live {
    background: var(--pd-live);
    box-shadow: 0 0 0 4px var(--pd-live-halo);
}
.wordmark-text {
    font-family: var(--pd-font-display);
    font-weight: 900;
    font-size: 27px;
    letter-spacing: -.04em;
    color: var(--pd-ink);
    line-height: 1;
}

.rail-label {
    font-family: var(--pd-font-mono);
    font-size: 9.5px;
    letter-spacing: .28em;
    text-transform: uppercase;
    color: var(--pd-ink-faint);
    padding: 0 var(--pd-rail-pad) 12px;
}

.bands { display: flex; flex-direction: column; }

/* A band, not a nav link: name on the left, tick on the edge.
   The 42px column here used to hold the frequency numeral. The numerals were removed at
   the client's request and the column was not, so every band name was being laid into
   the 42px slot with 119px of empty grid beside it. */
.band {
    position: relative;
    display: flex;
    align-items: baseline;
    gap: 10px;
    /* 9px gave a 42px row: every band sat under the 44px touch minimum. */
    padding: 12px var(--pd-rail-pad);
    text-decoration: none;
    color: var(--pd-ink-dim);
    transition: color .18s var(--pd-ease), background .18s var(--pd-ease);
}
/* Scaled rather than animated on height: transitioning height thrashes layout. */
.band::before {
    content: "";
    position: absolute;
    left: 0; top: 50%;
    width: 3px; height: 68%;
    background: var(--pd-carrier);
    transform: translateY(-50%) scaleY(0);
    transition: transform .22s var(--pd-ease), background .18s var(--pd-ease);
}
.band:hover { color: var(--pd-ink); background: var(--pd-signal-wash); }
.band:hover::before { transform: translateY(-50%) scaleY(.62); background: var(--pd-signal); }

.band-active { color: var(--pd-ink); }
.band-active::before { transform: translateY(-50%) scaleY(1); }

/* This rule and .band-active were separated by a bare newline, which made them one
   descendant selector — so the band name was styled ONLY inside the active band and
   every other band fell back to 16px/400. The active nav item was smaller and lighter
   than its siblings, and the row height changed as you navigated. */
.band-name {
    font-family: var(--pd-font-sans);
    font-size: 13.5px;
    font-weight: 500;
    letter-spacing: -.005em;
}

/* ---------- Language ---------- */

.rail-lang { padding: 14px var(--pd-rail-pad) 0; }

.lang-switch { display: flex; gap: 4px; }

.lang-btn {
    font-family: var(--pd-font-mono);
    font-size: 10px;
    letter-spacing: .16em;
    /* Was 35x27 — not reachable with a thumb, and this is the only way to change
       language on the whole site. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0 10px;
    background: transparent;
    border: 1px solid var(--pd-hairline);
    border-radius: var(--pd-etch);
    color: var(--pd-ink-faint);
    cursor: pointer;
    transition: color .18s var(--pd-ease), border-color .18s var(--pd-ease);
}
.lang-btn:hover { color: var(--pd-ink); border-color: var(--pd-signal); }

/* Amber marks the tuned state, exactly as it does on the band scale. */
.lang-btn-active { color: var(--pd-carrier); border-color: var(--pd-carrier); }

/* ---------- Join ---------- */

/* Deliberately not a band. The bands are the scene; this is the invitation to enter it,
   so it takes the ghost-button plate rather than a ninth row on the scale. */
.rail-join {
    display: block;
    margin: 16px var(--pd-rail-pad) 0;
    padding: 12px 14px;
    text-align: center;
    font-family: var(--pd-font-mono);
    font-size: 10.5px;
    font-weight: 500;
    letter-spacing: .16em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--pd-ink-dim);
    border: 1px solid var(--pd-hairline-bright);
    border-radius: var(--pd-etch);
    min-height: 44px;
    transition: color .18s var(--pd-ease), border-color .18s var(--pd-ease);
}

.rail-join:hover { color: var(--pd-signal); border-color: var(--pd-signal); }

/* Amber marks what is tuned, exactly as it does on the band scale. */
.rail-join-active { color: var(--pd-carrier); border-color: var(--pd-carrier); }

/* ---------- Globe stage ---------- */

.stage { position: relative; overflow: hidden; flex: 1; min-height: 100dvh; }

/* Absolute, not in flow. As a flow child with height: 100% against a flex parent whose
   own height is content-derived, the canvas's intrinsic attribute height won the
   circular resolution and pushed the stage past the viewport — on a landscape phone the
   "full-bleed" stage scrolled 102px and the caption ended up under the band strip. */
#globeCanvas {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

/* Station identification, top right — who is on air */
.stage-ident {
    position: absolute;
    top: clamp(20px, 3vh, 34px);
    right: var(--pd-gutter);
    text-align: right;
    display: flex;
    flex-direction: column;
    gap: 6px;
    pointer-events: none;
    z-index: 4;
}
.ident-line {
    font-family: var(--pd-font-mono);
    font-size: 10px;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--pd-ink-faint);
}
.ident-live { display: flex; align-items: center; justify-content: flex-end; gap: 7px; color: var(--pd-ink-faint); }
/* Only pulses while the globe is actually turning; station.js toggles .is-live from
   the real rotation speed, so the lamp reports transmission rather than decorating it. */
.ident-live::before {
    content: "";
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--pd-ink-faint);
    transition: background .4s var(--pd-ease);
}
.ident-live.is-live { color: var(--pd-ink-dim); }
.ident-live.is-live::before {
    background: var(--pd-live);
    animation: pd-pulse 2.6s ease-in-out infinite;
}
@keyframes pd-pulse { 0%,100% { opacity: .4; } 50% { opacity: 1; } }

/* Standing caption, bottom of the stage */
.stage-caption {
    position: absolute;
    left: var(--pd-gutter);
    bottom: clamp(20px, 4vh, 40px);
    max-width: 34ch;
    pointer-events: none;
    z-index: 4;
}
.stage-caption h1 {
    font-family: var(--pd-font-display);
    font-weight: 800;
    font-size: clamp(30px, 3.6vw, 52px);
    line-height: 1.02;
    letter-spacing: -.035em;
    color: var(--pd-ink);
    margin: 0 0 10px;
    text-wrap: balance;
}
.stage-caption p {
    font-family: var(--pd-font-sans);
    font-size: 14.5px;
    line-height: 1.6;
    color: var(--pd-ink-dim);
    margin: 0;
}

/* Was an <em>. Archivo publishes no italic face, so the browser sheared the largest
   type on the site into a synthetic oblique. Colour carries the accent instead — blue,
   because it marks nothing stateful. */
.stage-caption .caption-accent { color: var(--pd-signal); }

/* The caption is pointer-events: none so drags pass through to the globe; the one
   interactive thing in it has to opt back in. */
.stage-caption .caption-link {
    display: inline-block;
    margin-top: 18px;
    pointer-events: auto;
}

/* Shown when the globe cannot start. Not an apology screen — the station still has to
   say who is on air, so this is the real list, reachable and readable. */
.stage-fallback {
    position: relative;
    z-index: 5;
    padding: clamp(32px, 7vh, 76px) var(--pd-gutter);
    max-width: 74ch;
}
.fallback-list { list-style: none; margin: 22px 0 0; padding: 0; display: flex; flex-direction: column; }
.fallback-list li { border-top: 1px solid var(--pd-hairline); }
.fallback-list li:last-child { border-bottom: 1px solid var(--pd-hairline); }
.fallback-list a {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 16px;
    padding: 13px 2px;
    text-decoration: none;
    flex-wrap: wrap;
}
.fallback-name {
    font-family: var(--pd-font-sans);
    font-weight: 600;
    font-size: 15px;
    color: var(--pd-ink);
}
.fallback-meta {
    font-family: var(--pd-font-mono);
    font-size: 10.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--pd-ink-faint);
}
.fallback-list a:hover .fallback-name { color: var(--pd-signal); }

.globe-tip {
    position: fixed;
    z-index: 40;
    pointer-events: none;
    opacity: 0;
    transform: translateY(3px);
    transition: opacity .16s var(--pd-ease), transform .16s var(--pd-ease);
    background: var(--pd-panel);
    backdrop-filter: blur(9px);
    border: 1px solid var(--pd-hairline-bright);
    border-radius: 2px;
    padding: 8px 11px;
    max-width: 240px;
}
.globe-tip.on { opacity: 1; transform: translateY(0); }
.globe-tip .tn {
    font-family: var(--pd-font-sans);
    font-size: 14px;
    font-weight: 600;
    color: var(--pd-ink);
    display: block;
}
.globe-tip .td {
    font-family: var(--pd-font-mono);
    font-size: 9.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--pd-carrier);
    display: block;
    margin-top: 3px;
}

/* ---------- Tuned-in panel: the globe keeps turning behind it ---------- */

.tuned {
    position: absolute;
    top: 0; right: 0;
    height: 100%;
    width: min(432px, 42vw);
    z-index: 12;
    background: var(--pd-panel-solid);
    backdrop-filter: blur(18px);
    border-left: 1px solid var(--pd-hairline-bright);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform .5s var(--pd-ease);
    overflow-y: auto;
    overscroll-behavior: contain;
}
.tuned.open { transform: translateX(0); }

.tuned-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 14px;
    padding: 22px var(--pd-gutter) 0;
}
/* Replaces the banned eyebrow. Red, because it marks what is actually live. */
.tuned-band {
    font-family: var(--pd-font-mono);
    font-size: 10px;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--pd-live);
    display: flex;
    align-items: center;
    gap: 7px;
}
.tuned-band::before {
    content: "";
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--pd-live);
    flex: none;
}

.tuned-close {
    font-family: var(--pd-font-mono);
    font-size: 10px;
    letter-spacing: .18em;
    text-transform: uppercase;
    background: none;
    border: 1px solid var(--pd-hairline-bright);
    border-radius: 2px;
    color: var(--pd-ink-faint);
    padding: 6px 10px;
    cursor: pointer;
    flex: none;
    transition: color .18s var(--pd-ease), border-color .18s var(--pd-ease);
}
.tuned-close:hover { color: var(--pd-ink); border-color: var(--pd-signal); }

.tuned-body { padding: 16px var(--pd-gutter) 34px; }

/* Content resolves in rather than snapping — the signal-lock raise */
.tuned-resolve { animation: pd-resolve .55s var(--pd-ease) both; }
@keyframes pd-resolve {
    from { opacity: 0; filter: blur(6px); transform: translateY(8px); }
    to   { opacity: 1; filter: blur(0); transform: translateY(0); }
}

.tuned-portrait {
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border-radius: 2px;
    border: 1px solid var(--pd-hairline);
    background: var(--pd-void-2);
    display: block;
    margin-bottom: 16px;
}
.tuned-name {
    font-family: var(--pd-font-display);
    font-weight: 800;
    font-size: 27px;
    letter-spacing: -.03em;
    color: var(--pd-ink);
    margin: 0 0 8px;
    line-height: 1.08;
}
.tuned-bio {
    font-family: var(--pd-font-sans);
    font-size: 14px;
    line-height: 1.68;
    color: var(--pd-ink-dim);
    margin: 14px 0 0;
}
.tuned-actions { display: flex; gap: 10px; margin-top: 22px; flex-wrap: wrap; }

.tuned-fields {
    margin-top: 18px;
    border-top: 1px solid var(--pd-hairline);
    padding-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 9px;
}
.tuned-field {
    display: grid;
    grid-template-columns: 82px 1fr;
    gap: 12px;
    font-family: var(--pd-font-mono);
    font-size: 10.5px;
    letter-spacing: .08em;
}
.tuned-field dt { color: var(--pd-ink-faint); text-transform: uppercase; margin: 0; }
.tuned-field dd { color: var(--pd-ink-dim); font-weight: 500; margin: 0; }

/* ---------- Intro: lines resolve into .MD, then become the globe ---------- */

/* The overlay ships in the markup so it covers from the very first paint; the pre-paint
   script in App.razor stamps data-intro="seen" on repeat visits and this hides it before
   anything renders. Gating it from OnAfterRender dropped a cover over a painted station. */
:root[data-intro="seen"] .intro { display: none; }

.intro {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: grid;
    place-items: center;
    background: var(--pd-void);
    pointer-events: none;
    /* The iris collapses toward the globe's centre, so the mark resolves and is then
       swallowed by the sphere rather than scaled up and cross-faded over it. */
    animation: pd-intro-iris 1.05s var(--pd-ease) 2.35s both;
}
@keyframes pd-intro-iris {
    0%   { clip-path: circle(150% at 62% 48%); }
    99%  { clip-path: circle(0% at 62% 48%); visibility: visible; }
    100% { clip-path: circle(0% at 62% 48%); visibility: hidden; }
}
/* The mark travels into that same point as the iris closes on it. */
.intro-mark { animation: pd-intro-mark 1.05s var(--pd-ease) 2.35s both; }
@keyframes pd-intro-mark {
    0%   { transform: translate(0, 0) scale(1); opacity: 1; }
    100% { transform: translate(12vw, -2vh) scale(.12); opacity: .9; }
}
.intro-mark { display: flex; align-items: center; gap: 12px; }
.intro-dot {
    width: 20px; height: 20px;
    border-radius: 50%;
    /* Unlit. Nothing is on air while the cover is up, and a lamp authored lit as a
       static style is precisely what the Unlit-By-Default Rule forbids. Here it is the
       period in ".MD", not a transmission indicator. */
    background: var(--pd-ink-faint);
    opacity: 0;
    animation: pd-lock-dot .7s var(--pd-ease) 1.15s both;
}
.intro-text {
    font-family: var(--pd-font-display);
    font-weight: 900;
    font-size: clamp(52px, 9vw, 116px);
    letter-spacing: -.05em;
    color: var(--pd-ink);
    line-height: 1;
    opacity: 0;
    animation: pd-lock-text .85s var(--pd-ease) .95s both;
}
.intro-scan {
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(
        to bottom,
        transparent 0 3px,
        var(--pd-signal-glow) 3px 4px);
    animation: pd-scanlines 1.5s linear both;
}
.intro-status {
    position: absolute;
    bottom: 12vh;
    font-family: var(--pd-font-mono);
    font-size: 10px;
    letter-spacing: .3em;
    text-transform: uppercase;
    color: var(--pd-ink-faint);
    animation: pd-status 2.4s steps(1) both;
}

@keyframes pd-lock-text {
    from { opacity: 0; filter: blur(16px); letter-spacing: .18em; }
    to   { opacity: 1; filter: blur(0); letter-spacing: -.05em; }
}
@keyframes pd-lock-dot {
    from { opacity: 0; transform: scale(2.4); filter: blur(8px); }
    to   { opacity: 1; transform: scale(1); filter: blur(0); }
}
@keyframes pd-scanlines {
    from { opacity: .85; transform: translateY(-6px); }
    to   { opacity: 0; transform: translateY(6px); }
}
@keyframes pd-status {
    0%   { content: ""; opacity: 0; }
    12%  { opacity: 1; }
    88%  { opacity: 1; }
    100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    /* No held cover: the mark is shown briefly, then removed outright. */
    .intro { animation: pd-intro-cut .01ms linear .5s both; }
    @keyframes pd-intro-cut { to { visibility: hidden; opacity: 0; } }
    .intro-mark { animation: none; }
    .intro-text, .intro-dot { animation-duration: .25s; animation-delay: 0s; }
    .intro-scan { display: none; }
    .tuned { transition: none; }
    .tuned-resolve { animation: none; }
}

/* ---------- Responsive ---------- */

@media (max-width: 900px) {
    /* The rail goes to the BOTTOM edge on phones. A horizontal strip at the top would
       re-derive precisely the nav bar this redesign removed; at the bottom it stays a
       band strip and is thumb-reachable. */
    /* 128px was reserved for a strip that measures ~69px: sixty pixels of dead space at
       the bottom of every phone page, and the same 128px subtracted from the globe. */
    body { padding-left: 0; padding-bottom: 78px; }

    .station-rail {
        top: auto;
        bottom: 0;
        width: 100%;
        height: auto;
        flex-direction: row;
        align-items: center;
        gap: 12px;
        border-right: none;
        border-top: 1px solid var(--pd-hairline-bright);
        background: color-mix(in oklab, var(--pd-void-0) 74%, transparent);
        padding: 10px var(--pd-rail-pad);
        overflow: visible;
        /* No backdrop-filter here: filter creates a containing block for fixed
           descendants, which anchored the wordmark to the strip instead of the
           viewport. The ground below is a wash, so the field shows through — which is
           the same trade the desktop rail makes deliberately. */
        backdrop-filter: none;
    }

    /* The station must not scroll: rail height is reserved by body padding, so the
       inner column has to stop short of a full viewport or the caption slides under. */
    .station { min-height: auto; }

    /* Wordmark leaves the strip and sits over the stage, where it reads as identity
       rather than as the first item of a menu. */
    .wordmark {
        position: fixed;
        top: 10px;
        left: calc(var(--pd-gutter) - 8px);
        z-index: 21;
        /* Was padding: 0, giving a 61x27 target. */
        padding: 9px 8px;
        min-height: 44px;
        align-items: center;
    }
    .rail-label { display: none; }

    .bands {
        flex-direction: row;
        overflow-x: auto;
        scrollbar-width: none;
        gap: 2px;
        flex: 1;
        min-width: 0;
        scroll-snap-type: x proximity;
        /* Fades the trailing edge so it is visible that more bands lie off-screen —
           at 375px five of the eight are. Without it the strip looked complete and most
           of the navigation was simply unreachable-looking. */
        mask-image: linear-gradient(to right, #000 0, #000 86%, transparent 100%);
    }
    .bands::-webkit-scrollbar { display: none; }

    /* Still a band: frequency above name, carrier tick along the top edge. */
    .band {
        grid-template-columns: 1fr;
        grid-auto-rows: min-content;
        align-items: center;
        justify-items: center;
        gap: 2px;
        /* Was 6px, giving a 32px row on the primary phone navigation. */
        padding: 13px 12px;
        white-space: nowrap;
        scroll-snap-align: center;
    }
    .band::before {
        left: 50%; top: 0;
        width: 100%; height: 2px;
        transform: translateX(-50%) scaleX(0);
        transition: transform .22s var(--pd-ease), background .18s var(--pd-ease);
    }
    .band:hover::before { transform: translateX(-50%) scaleX(.5); }
    .band-active::before { transform: translateX(-50%) scaleX(1); }
    .band-name { font-size: 12px; }

    .rail-join {
        margin: 0;
        flex: none;
        padding: 12px 12px;
        white-space: nowrap;
        align-content: center;
    }

    .rail-lang { padding: 0; flex: none; }

    .stage { min-height: calc(100dvh - 78px); }
    .tuned { width: 100%; }
    .stage-caption { max-width: none; right: var(--pd-gutter); bottom: 20px; }
    /* The display token already floors at 30px, so the phone needs no separate step. */
    /* The full ident block sat on top of the globe in portrait. Only the on-air lamp
       earns the space here; location and count repeat what the page already says. */
    .stage-ident { top: 14px; }
    .stage-ident .ident-line:not(.ident-live) { display: none; }
}

/* ---------- Short viewports: the landscape phone ---------- */

/* A phone turned sideways is not a small desktop and not a tall phone — it is a viewport
   with width to spare and almost no height. The portrait composition stacks a globe above
   a bottom-anchored caption, which needs vertical room that does not exist here: the
   caption ran past the fold and sat underneath the band strip.
   Landscape has the one thing portrait lacks, so the composition turns: caption in a left
   column, globe to the right of it. That is the desktop arrangement without the rail. */
@media (max-width: 900px) and (max-height: 560px) {
    .stage-caption {
        top: 50%;
        bottom: auto;
        right: auto;
        transform: translateY(-50%);
        max-width: min(42vw, 34ch);
    }

    /* No type overrides here. The display token already floors at 30px and the caption
       copy is documented at 14.5px, so shrinking them invented two steps that are on no
       ramp — the problem in landscape was where the caption sat, not how big it was. */
    .stage-caption h1 { margin-bottom: 6px; }

    .stage-caption .caption-link { margin-top: 12px; }

    /* The ident block and the wordmark share the top edge with very little of it. */
    .stage-ident { top: 10px; }
    .wordmark { top: 6px; }
}

/* ---------- Input method ---------- */

/* Screen size does not tell you how the visitor is pointing at it. Nothing in this build
   asked, so every hover affordance was simply dead on touch. */
@media (hover: none) {
    /* The card is already a link; on touch the overlay was an invitation that never
       appeared, covering the portrait it was supposed to reveal. */
    .artist-overlay { display: none; }

    /* Hover ticks and washes never fire on touch, and leaving them declared means a
       tapped element keeps the hover state until something else is tapped. */
    .band:hover { background: transparent; }
    .band:hover::before { transform: translateY(-50%) scaleY(0); }
}

@media (hover: none) and (max-width: 900px) {
    .band:hover::before { transform: translateX(-50%) scaleX(0); }
}

/* Coarse pointers need the room between targets, not just the targets. */
@media (pointer: coarse) {
    .lang-switch { gap: 8px; }
    .tuned-actions { gap: 14px; }
}

/* ---------- Safe areas ---------- */

/* The band strip is the primary navigation on a phone and it sits on the bottom edge —
   which on any device with a home indicator or a rounded corner is exactly where the
   system furniture is. App.razor now sets viewport-fit=cover so these resolve. */
@media (max-width: 900px) {
    .station-rail {
        padding-bottom: max(10px, env(safe-area-inset-bottom));
        padding-left: max(var(--pd-rail-pad), env(safe-area-inset-left));
        padding-right: max(var(--pd-rail-pad), env(safe-area-inset-right));
    }

    body {
        padding-bottom: calc(78px + env(safe-area-inset-bottom));
    }

    .stage { min-height: calc(100dvh - 78px - env(safe-area-inset-bottom)); }
}
