/* =====================================================================================
   MOUNTAIN-TRAIL MOBILE SYSTEM — SHARED PRESENTATION LAYER  (2026-08-07)

   Production rollout of the mobile system approved on the Yr Wyddfa mobile-scan prototype
   (the design, six routes + a scramble) and the Cnicht one (the transfer test, two routes).
   Both prototypes were deleted 2026-08-07 once this shared layer superseded them; they are
   recoverable from git history and nothing here depends on them.

   Loaded last by every mountain-trail page. Everything that changes layout lives inside
   @media (max-width: 760px). Above that the only rules that apply are the display:none
   defaults for elements this system adds, plus one grid-placement restore for the .fitfor
   wrapper — so DESKTOP RENDERS EXACTLY AS THE PAGE DID BEFORE.

   It modifies no shared component: not the route-stage swapper, the route-steps slider,
   the gallery lightbox, the orientation/preview maps, the saved-places store or the
   feedback widget.

   NOTHING IS HARD-CODED TO A MOUNTAIN. Route count, which two stats a chip can honestly
   show, whether a conditions item has enough text to fold, and the "route profile/detail
   below" cue wording are all read from the page at runtime (assets/mountain-mobile.js).
   The one per-page string is data-fit-summary on .verdict-panel, which has to be a fact
   about that mountain and therefore lives in that mountain's own HTML.

   LAYERS ON MOBILE:  CHOOSE (route picker) -> PREPARE (selected route) ->
                      DECIDE (conditions, noticeboard) -> EXPLORE (rails, ending)
   ===================================================================================== */

/* ---- elements this system adds: invisible unless the mobile layer turns them on */
.rpick-spark,
.mscan-detail,
.mscan-toggle,
.fitfor__head,
.rail-dots,
.fcast-sum,
.fcast-toggle,
.fcast-about { display: none; }

/* The .fitfor wrapper takes the grid slot .verdict-panel used to hold, so the desktop
   reckoning grid is unchanged (full width, second row). Above 900px only: below that each
   page's own rule already flattens the grid to one auto column, and forcing row 2 here
   would jump the block ahead of the "Turn back if…" pact. */
@media (min-width: 901px) {
    .covenant__grid > .fitfor { grid-column: 1 / -1; grid-row: 2; }
}

@media (max-width: 760px) {

/* =========================================================================
   1. ROUTE CHOOSER — compact choices, one full card for the selected line

   Two columns whatever the route count (2, 3 or 6): the point of the chip row is
   comparison, and each page's own <=520px rule would stack it back into a list.
   ========================================================================= */
body.mscan .route-ledger { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }

body.mscan .route-ledger .route-card__face { padding: 11px 12px 12px; border-radius: 11px; }
body.mscan .route-ledger .route-card__char,
body.mscan .route-ledger .route-card__for { display: none; }
body.mscan .route-ledger .route-card__grade {
    margin-bottom: 5px; padding: 2px 7px; font-size: 0.5rem; letter-spacing: 0.1em; }
body.mscan .route-ledger .route-card__name { font-size: 0.94rem; }
body.mscan .route-ledger .route-card__tick { top: 9px; right: 9px; width: 17px; height: 17px; }
body.mscan .route-ledger .route-card__tick svg { width: 9px; height: 9px; }
body.mscan .route-ledger .route-card input:checked + .route-card__face { transform: none; }
/* the "profile below" cue belongs to the one detail card, not to every chip */
body.mscan .route-ledger .route-card input:checked + .route-card__face::after { display: none; }

/* Chip stats. The JS picks at most two by LABEL, and only labels every card on the page
   carries, so the row compares like with like — never by position, because the stats grid
   is ordered differently on almost every mountain. A card whose data cannot honestly fill
   either slot shows no stats at all rather than a padded one. */
body.mscan .route-ledger .route-card__stats { margin-top: 9px; padding-top: 8px; gap: 4px 10px; }
body.mscan .route-ledger .route-card__stats > div { display: none; }
body.mscan .route-ledger .route-card__stats > div.mm-stat { display: flex; }
body.mscan .route-ledger .route-card__stats > div.mm-stat--1 { order: 1; }
body.mscan .route-ledger .route-card__stats > div.mm-stat--2 { order: 2; }
body.mscan .route-ledger .route-card__stats.mm-nostats { display: none; }
body.mscan .route-ledger .route-card__stats dt { font-size: 0.48rem; }
body.mscan .route-ledger .route-card__stats dd { font-size: 0.8rem; }

/* Each choice draws its own ascent line — the shape is half the comparison. */
body.mscan .route-ledger .rpick-spark {
    display: block; width: 100%; height: 22px; margin: 0 0 9px; overflow: visible; }
body.mscan .route-ledger .rpick-spark path {
    fill: none; stroke: rgba(228, 196, 122, 0.32); stroke-width: 1.5;
    vector-effect: non-scaling-stroke; stroke-linecap: round; stroke-linejoin: round;
    transition: stroke 0.2s ease; }
body.mscan .route-ledger input:checked + .route-card__face .rpick-spark path {
    stroke: var(--gold-warm); stroke-width: 1.9; }

/* The one full card, for the selected route only. Rebuilt from the real card on every
   change, so it can never drift from the selector. */
body.mscan .mscan-detail { display: block; margin-top: 14px; }
body.mscan .mscan-detail[hidden] { display: none; }
body.mscan .mscan-detail__label {
    display: flex; align-items: center; gap: 10px; margin: 0 0 8px;
    color: rgba(236, 199, 110, 0.9); font-size: 0.58rem; font-weight: 700;
    letter-spacing: 0.16em; text-transform: uppercase; }
body.mscan .mscan-detail__label::after {
    content: ""; flex: 1 1 auto; height: 1px; background: rgba(228, 196, 122, 0.2); }
body.mscan .mscan-detail .route-card__face {
    position: relative; padding: 15px 16px 16px;
    border: 1px solid var(--gold-warm); border-radius: 12px;
    background: linear-gradient(180deg, rgba(237, 176, 90, 0.16), rgba(20, 33, 27, 0.6));
    box-shadow: 0 16px 34px rgba(0, 0, 0, 0.3), inset 0 0 0 1px rgba(237, 176, 90, 0.4); }
body.mscan .mscan-detail .route-card__name { margin: 0; font-size: 1.3rem; }
body.mscan .mscan-detail .route-card__stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
body.mscan .mscan-detail .route-card__tick { display: none; }
/* The cue wording is the page's own (Conwy pages say "detail", Gwynedd "profile"); the JS
   lifts it from the checked chip's ::after and hands it over as --mm-cue. */
body.mscan .mscan-detail .route-card__face::after {
    content: var(--mm-cue, "Route detail below \2193"); display: block;
    margin-top: 12px; padding-top: 10px;
    border-top: 1px solid rgba(228, 196, 122, 0.24);
    color: var(--gold-warm); font-size: 0.6rem; font-weight: 700;
    letter-spacing: 0.12em; text-transform: uppercase; }

/* A serious scramble stays its own thing: its label, its danger frame and its warnings are
   never folded into the walking chips. (Only Yr Wyddfa carries one today — Crib Goch.) */
body.mscan .board__label--scramble { margin-top: 26px; }

/* =========================================================================
   Shared disclosure control (conditions, fit-for, notices)
   ========================================================================= */
body.mscan .mscan-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    min-height: 40px; margin: 9px 0 0; padding: 0; border: 0; background: none;
    color: rgba(236, 199, 110, 0.92); cursor: pointer;
    font-family: inherit; font-size: 0.62rem; font-weight: 700;
    letter-spacing: 0.13em; text-transform: uppercase; }
body.mscan .mscan-toggle__chev {
    width: 7px; height: 7px;
    border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px); transition: transform 0.2s ease; }
body.mscan .mscan-toggle[aria-expanded="true"] .mscan-toggle__chev {
    transform: rotate(225deg) translate(-2px, -2px); }
body.mscan .mscan-toggle:focus-visible { outline: 2px solid rgba(228, 196, 122, 0.7); outline-offset: 3px; }

/* =========================================================================
   2. IS TODAY THE DAY — summary first, safety meaning never hidden

   Adaptive: only an item with a genuine second half gets a control (see the JS
   threshold). An item that is one short sentence stays fully visible — on several
   mountains that means no control appears at all, which is the correct outcome.
   ========================================================================= */
body.mscan .weather-list li.is-collapsed .wx-rest { display: none; }
body.mscan .weather-list li { padding-bottom: 14px; border-bottom: 1px solid rgba(228, 196, 122, 0.12); }
body.mscan .weather-list li:last-child { border-bottom: 0; padding-bottom: 0; }

/* "Good fit for / Think twice if" folds into one compact block. Nothing is lost. */
body.mscan .fitfor {
    margin-top: 26px; padding: 16px 16px 14px;
    border: 1px solid rgba(228, 196, 122, 0.22); border-radius: 12px;
    background: linear-gradient(180deg, rgba(43, 66, 58, 0.42), rgba(20, 33, 27, 0.5)); }
body.mscan .fitfor__head { display: block; }
body.mscan .fitfor__title {
    margin: 0; font-family: Georgia, "Times New Roman", serif;
    font-size: 1.08rem; font-weight: 600; color: var(--cream); }
body.mscan .fitfor__sum {
    margin: 5px 0 0; color: var(--muted); font-size: 0.9rem; line-height: 1.5; }
body.mscan .fitfor.is-collapsed .verdict-panel { display: none; }
body.mscan .fitfor .verdict-panel { margin-top: 14px; padding-top: 16px; }

/* =========================================================================
   3. NOTICEBOARD — the warning stays pinned, the instructions fold

   The compact pinned slip, read in one glance. The stacked centred icon and the
   full-width stamp row are what made it tall — the icon sits in the top corner and the
   stamp shares the control's row. This layout is the SAME open or closed: the number,
   icon, title and summary never move, and "what to do" only adds the instructions
   underneath (order: 1 keeps them last, below the control row).
   ========================================================================= */
body.mscan .notice.is-collapsed .notice__acts { display: none; }
body.mscan .notice .mscan-toggle { color: var(--bronze); }
body.mscan .notice--danger .mscan-toggle { color: #a83515; }
body.mscan .notice .mscan-toggle:focus-visible { outline-color: rgba(156, 106, 34, 0.8); }

body.mscan .notice {
    flex-direction: row; flex-wrap: wrap; align-items: center;
    padding: 11px 14px 11px 42px; row-gap: 2px; column-gap: 10px; }
body.mscan .notice__num { top: 11px; left: 11px; width: 22px; height: 22px; font-size: 0.76rem; }
body.mscan .notice__icon {
    position: absolute; top: 10px; right: 13px; width: 24px; height: 24px; margin: 0; }
body.mscan .notice__title { flex: 1 1 100%; padding-right: 32px; font-size: 1rem; }
body.mscan .notice__body { flex: 1 1 100%; margin: 3px 0 0; font-size: 0.82rem; line-height: 1.4; }
body.mscan .notice .mscan-toggle { min-height: 32px; margin: 0; }
body.mscan .notice__stamp {
    align-self: center; margin: 0 0 0 auto; padding: 3px 8px; font-size: 0.55rem; }
body.mscan .notice__acts { order: 1; flex: 1 1 100%; margin: 9px 0 2px; }

/* =========================================================================
   4 + 5. GALLERY AND NEARBY — horizontal centred rails

   Every frame and every destination is kept; the grid becomes one swipe.

   NO SIDE PADDING ON THE RAIL. An earlier version padded each rail so the first and
   last card could also reach the centre snap position; that padding is what held the
   first card permanently inset, and it shrank the card twice over, because flex-basis
   resolves against the padded content box (82% of 0.82W was only ~59% of the screen).
   Without it, centre snapping gives the wanted end behaviour for free: the first card
   can only come to rest flush against the left content edge and the last flush against
   the right, while every card between them still centres with a symmetrical peek.

   The card is then sized FROM the peek rather than the other way round —
       card = 100% - 2 x (gap + 24px)
   so the peek stays a fixed ~24px at every phone width instead of drifting with the
   viewport, and the card grows to ~270px at 390 (59% -> 69% of the screen).
   Approved on the Glyder Fawr pilot, 2026-08-07.
   ========================================================================= */
body .gallery__grid,
body .valley__places {
    display: flex; padding-inline: 0;
    overflow-x: auto; scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch; scrollbar-width: none; }
body .gallery__grid::-webkit-scrollbar,
body .valley__places::-webkit-scrollbar { display: none; }

body .gallery__grid { gap: 12px; margin-top: 26px; }
/* margin: 0 is load-bearing — .shot is a <figure> and still carries the UA 40px side
   margin, which in a flex rail pushes the next frame clean off the peek. The
   nth-child(n+4) half of the selector overrides each page's "hidden until Show more".
   Card width: 2 x (12px gap + 24px peek) = 72. */
body .gallery__grid .shot,
body .gallery__grid .shot:nth-child(n+4) {
    display: flex; flex: 0 0 calc(100% - 72px); min-height: 250px; margin: 0;
    scroll-snap-align: center; }
/* the show-more toggle is redundant once every frame is one swipe away */
body .gallery__more, body .gallery__toggle { display: none; }

body .valley__places { gap: 14px; }
/* 2 x (14px gap + 24px peek) = 76 */
body .valley__places .place { flex: 0 0 calc(100% - 76px); scroll-snap-align: center; }

/* Rail position readout — presentational, the swipe is the control. */
body.mscan .rail-dots {
    display: flex; justify-content: center; gap: 6px; margin: 14px 0 0; }
body.mscan .rail-dots span {
    width: 5px; height: 5px; border-radius: 50%;
    background: rgba(228, 196, 122, 0.28); transition: background 0.2s ease, transform 0.2s ease; }
body.mscan .rail-dots span.is-on { background: var(--gold-warm); transform: scale(1.5); }

/* =========================================================================
   6. ENDING — one closing map action, not two
   Map preview, Save and the whole-panel click-through all stay; only the duplicate
   button goes, so the cinematic finale carries the closing CTA.
   ========================================================================= */
body .mapview .atlas-ending__cta { display: none; }
body .mapview .atlas-ending__sub { margin-bottom: 4px; }

}

/* =============================================================================
   7. ROUTE-STAGE / WAYPOINT RAIL — the same alignment, by a different mechanism

   620px, not 760px, because that is where assets/route-steps.css turns the stage list
   into a rail at all; above it the list is a static grid and none of this applies.

   THIS RAIL MUST NOT USE THE TECHNIQUE ABOVE. assets/route-steps-slider.js holds no
   index variable — it derives the active stage entirely from
   `Math.round(scrollLeft / step)`, and that drives the card, the progress dot, the
   arrows AND the numbered marker on the route profile. The identity only holds because
   the scrollport's side padding is exactly half the leftover space, which is what makes
   stage i centre at precisely scrollLeft = i * step. Deleting that padding the way the
   gallery rail does would desynchronise every one of them.

   So the padding stays and the maths is untouched — P = (W - card) / 2 still holds
   exactly. What changes is WHERE the padding sits: the scrollport is pulled outward by
   the same amount it is padded, so the padding now occupies the page margin instead of
   the content column. Net effect: the card becomes the full content column (256 -> 304px
   at 390, 66% -> 78% of the screen), the first stage's left edge lands on the content
   edge and the last on the right, and a centred middle stage keeps a symmetrical peek.

   HOW FAR THE BLEED CAN GO IS SET BY THE BOARD, NOT BY TASTE. Unlike the two rails
   above, this one sits inside `.board`, a bordered rounded panel, and the peek has to
   render between the stage column and that border. There is exactly 18px of board
   padding + 1px of border there — `.board` is padded clamp(18px, 2.6vw, 30px), which
   resolves to 18px at every width this query covers, up to and including 620, and is
   identical on all 11 mountain pages. So the bleed caps at 19px and the peek at
   19 - 8 = 11px. At the gallery's 24px the rail runs 13px past the panel and the peeking
   cards cut through the board's border on both sides. Approved at 11px, Glyder Fawr
   pilot, 2026-08-07.

   Nothing else moves: snap, gap, dots, arrows, markers, height interpolation, the
   hazard line and the profile synchronisation all read the same numbers as before.
   ============================================================================= */
@media (max-width: 620px) {
    .profile .legend {
        --stage-pad: 19px;                                   /* board padding + border */
        margin-inline: calc(-1 * var(--stage-pad));
        padding-inline: var(--stage-pad);
    }
}

/* =============================================================================
   8. PRACTICAL BRIEF — the closed state is genuinely closed

   560px, not 760px, because that is where each page turns the brief into a disclosure
   at all; above it the grid is always open and none of this applies. Verified as the
   breakpoint on all 11 mountain pages.

   Each page already folds the per-route `.pbrief__grid` behind its existing "Show
   practical details…" button. But `.pbrief__note` (the route-source / methodology
   paragraph) and the `.route-scope` "Route figures" box are siblings of the panels, not
   of the toggle, so they sat open underneath the closed button and the brief read as
   three-quarters expanded before it had been opened. Now the same button reveals the
   whole package: grid, methodology note and figures box together.

   WHY A CLASS AND NOT `:has()`. The pilot paired each route's radio with that route's
   own toggle by id (`:has(#r-x:checked):has(#pbrief-more--x:checked)`), which is exact
   but has to be written out per route — 27 routes across these 11 pages, and a new one
   every time a mountain is added. The shared version asks the DOM the same question
   instead (see assets/mountain-mobile.js): which panel is actually displayed, and is
   ITS toggle checked. Same result, no per-page authoring, and it keeps working for any
   future page.

   The pairing matters: each route has its own checkbox and that state survives a route
   switch, so a looser "any toggle is checked" test would leave the note and the figures
   box stranded open after opening one route and switching to another whose own button
   still reads "Show practical details".

   PROGRESSIVE ENHANCEMENT. The hiding is scoped under `body.mscan`, which the script
   only adds once it has run, so with JS off nothing is hidden and the brief renders
   exactly as the page authored it. Nothing here is the only copy of anything.
   ============================================================================= */
@media (max-width: 560px) {
    body.mscan .pbrief:not(.is-brief-open) .pbrief__note,
    body.mscan .pbrief:not(.is-brief-open) .route-scope { display: none; }
}

/* =============================================================================
   9. FORECAST NOTE — a summary, then the explanation on request

   560px, the same breakpoint the Practical Brief uses to become a disclosure.

   On phones the explanatory paragraph under the forecast buttons is five or six lines of
   standing text before the reader reaches the conditions list. It now leads with a
   one-line summary and folds the rest behind a quiet control; the paragraph itself is
   the page's own element, revealed unchanged.

   The markup is injected by assets/mountain-mobile.js (a summary, a checkbox and its
   label, immediately before that paragraph), so the toggle itself is pure CSS and
   nothing here can match on a page that has no forecast paragraph — Mynydd y Dref,
   whose band carries an active closure rather than forecasts, is skipped automatically.
   With JS off nothing is injected, nothing matches, and the paragraph stands as authored.

   The control is deliberately the band's own `.btn--ghost` language — gold hairline on
   dark green, fully rounded — at a lighter border weight, smaller type and a shorter
   height, so it reads as a way into more detail rather than a fourth forecast link.
   Centred and `width: fit-content`, never full width. Approved on the Yr Wyddfa pilot,
   2026-08-07.
   ============================================================================= */
@media (max-width: 560px) {
    .fcast-sum {
        display: block; margin: 16px 0 0;
        color: var(--muted); font-size: 0.95rem; line-height: 1.55; }

    /* hidden from view, still focusable and still the control */
    .fcast-toggle {
        display: inline; position: absolute; width: 1px; height: 1px;
        margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0);
        white-space: nowrap; border: 0; }

    .fcast-about {
        display: flex; width: fit-content; margin: 13px auto 0;
        align-items: center; justify-content: center; gap: 9px;
        min-height: 42px; padding: 0 20px;
        border: 1px solid rgba(228, 196, 122, 0.34); border-radius: 999px;
        background: rgba(30, 50, 40, 0.55);
        color: rgba(244, 230, 197, 0.92); cursor: pointer;
        font-size: 0.82rem; font-weight: 600; letter-spacing: 0.01em;
        transition: border-color 0.2s ease, background 0.2s ease; }
    .fcast-about:hover {
        border-color: rgba(228, 196, 122, 0.55); background: rgba(38, 62, 50, 0.7); }
    .fcast-about__chev {
        width: 6px; height: 6px; margin-top: -2px;
        border-right: 1.5px solid rgba(236, 199, 110, 0.95);
        border-bottom: 1.5px solid rgba(236, 199, 110, 0.95);
        transform: rotate(45deg); transition: transform 0.2s ease; }
    .fcast-toggle:checked ~ .fcast-about .fcast-about__chev {
        margin-top: 2px; transform: rotate(225deg); }
    .fcast-toggle:focus-visible ~ .fcast-about {
        outline: 2px solid rgba(228, 196, 122, 0.7); outline-offset: 3px; }

    /* The page's own paragraph, unchanged, now behind the control. It is the only
       .band__intro that follows the toggle — the section intro above the buttons is a
       preceding sibling and is never matched (verified on all 10 pages that have one). */
    .fcast-toggle ~ .band__intro { display: none; }
    .fcast-toggle:checked ~ .band__intro { display: block; }
}

@media (prefers-reduced-motion: reduce) {
    .mscan-toggle__chev, .rail-dots span, .rpick-spark path,
    .fcast-about, .fcast-about__chev { transition: none; }
}
