/* WalesMap location-page motion — reveal-on-scroll, hero entrance, hero cursor glow.
   Progressive enhancement: without location-motion.js, all content is visible
   and static; nothing here depends on JS to be usable. */

/* ---------------- Reveal on scroll ----------------
   The pre-reveal hidden state is scoped to `scripting: enabled` deliberately.
   `.reveal { opacity: 0 }` only becomes visible again when location-motion.js
   adds .is-visible, so declaring it unconditionally left every revealed section
   permanently invisible whenever JS was disabled or the script failed to load —
   the exact opposite of this file's progressive-enhancement contract.

   Gating on the native `scripting` media feature fixes that with no page-level
   markup, no inline head snippet, and no flash of hidden content: with scripting
   off the rule never applies and the content is simply visible and static. A
   browser that does not support the feature ignores the block entirely, which
   degrades the same safe way — content visible, animation skipped. */
@media (scripting: enabled) {
    .reveal {
        opacity: 0;
        transform: translateY(16px);
        transition: opacity 0.7s ease, transform 0.7s ease;
    }
}

.reveal.is-visible {
    opacity: 1;
    transform: none;
}

/* ---------------- Hero entrance: copy rises in sequence on load ---------------- */
.location-hero__copy > * {
    opacity: 0;
    transform: translateY(22px);
    animation: location-hero-rise 0.95s cubic-bezier(0.2, 0.7, 0.2, 1) forwards;
}

.location-hero__copy > *:nth-child(1) { animation-delay: 0.15s; }
.location-hero__copy > *:nth-child(2) { animation-delay: 0.32s; }
.location-hero__copy > *:nth-child(3) { animation-delay: 0.5s; }
.location-hero__copy > *:nth-child(4) { animation-delay: 0.68s; }

@keyframes location-hero-rise {
    to {
        opacity: 1;
        transform: none;
    }
}

/* ---------------- Hero cursor-follow glow (fine pointers only; toggled by JS) ---------------- */
.location-hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    opacity: 0;
    /* rgba form of --gold-warm (#edb05a) — gradient stops can't reference a
       custom property alone when alpha is needed without color-mix(). */
    background: radial-gradient(280px 280px at var(--mx, 30%) var(--my, 60%), rgba(237, 176, 90, 0.16), transparent 70%);
    transition: opacity 0.5s ease;
    mix-blend-mode: screen;
}

.location-hero.glow-on::after {
    opacity: 1;
}

/* ---------------- Scroll route thread (large desktop only) ----------------
   A cartographic progress rail in the left gutter, mirroring the landing page.
   Because location pages alternate dark and cream "paper" chapters, the rail
   is two-tone: warm gold light over dark chapters, bronze over paper — swapped
   by JS based on the chapter you are actually reading (viewport centre). Colours
   are driven through custom properties so the swap crossfades in one place. */
.route-thread {
    --thread-track: rgba(228, 196, 122, 0.22);
    --thread-fill: var(--gold-warm, #edb05a);
    --thread-glow: 0 0 12px rgba(217, 130, 59, 0.5);
    position: fixed;
    top: 0;
    bottom: 0;
    left: max(20px, calc((100vw - 1180px) / 2 - 26px));
    width: 2px;
    z-index: 3;
    background: repeating-linear-gradient(180deg, var(--thread-track) 0 4px, transparent 4px 12px);
    pointer-events: none;
    display: none;
}

/* Over a paper chapter, the rail becomes bronze — the paper-surface accent
   token — and drops the ember glow, which reads as a smudge on cream. */
.route-thread.on-paper {
    --thread-track: rgba(158, 108, 36, 0.28);
    --thread-fill: var(--bronze, #9c6a22);
    --thread-glow: 0 0 8px rgba(158, 108, 36, 0.22);
}

/* The rail is hidden by default (display:none above) and revealed ONLY when both
   conditions hold: the desktop breakpoint, AND .has-location-progress-rail on <html>,
   which location-motion.js adds after the rail is genuinely tracking scroll. Gating
   the reveal on the class — rather than on the breakpoint alone — is what stops the
   inactive dotted track painting beside a native scrollbar when JS is off or fails:
   an un-initialised rail shows nothing at all, so the browser scrollbar is never
   competing with a decorative line that isn't doing its job. */
@media (min-width: 1320px) {
    .has-location-progress-rail .route-thread {
        display: block;
    }
}

.route-thread__fill {
    position: absolute;
    top: 0;
    left: -0.5px;
    width: 3px;
    height: 0;
    border-radius: 3px;
    background-color: var(--thread-fill);
    box-shadow: var(--thread-glow);
    transition: background-color 0.5s ease, box-shadow 0.5s ease;
}

/* ---------------- Native viewport scrollbar, once the rail replaces it ----------------
   The rail already shows scroll position, so the browser's own scrollbar beside it is a
   second, redundant progress indicator. It is hidden only under .has-location-progress-rail,
   which location-motion.js adds to <html> after the rail is genuinely tracking scroll — never
   from static CSS. If the script fails or JS is off, the class never lands and the native
   scrollbar stays, so the page is never left with no scroll affordance.

   Two deliberate limits:
   - Scoped to the rail's own breakpoint. Below 1320px .route-thread is display:none, so there
     is no rail to replace and the native scrollbar must remain.
   - Scoped to the root element only (no descendant selector, and scrollbar-width does not
     inherit), so nested scrollers — panels, maps, modals, the accordion — keep their own
     scrollbars.
   Scrolling itself is untouched: no overflow change, so wheel, trackpad, touch, arrows,
   Page Up/Down and Home/End all behave exactly as before. */
@media (min-width: 1320px) {
    .has-location-progress-rail {
        scrollbar-width: none; /* Firefox */
    }

    .has-location-progress-rail::-webkit-scrollbar {
        width: 0;
        height: 0;
    }
}

/* ---------------- The same rail, below the desktop breakpoint ----------------
   Piloted page-locally on llanddwyn-island.html (2026-07-26), approved, and moved here
   2026-08-03 so every page that loads this file inherits it — the pilot's own stated
   endpoint. Same element, same JS, same gold fill and same gold/bronze chapter swap:
   the only reason the rail was desktop-only was the min-width:1320px gate above.

   MUST STAY BELOW that gate. test_canonical_coast.py asserts the 1320px breakpoint appears
   before the first `.has-location-progress-rail .route-thread` selector, which is what proves
   the desktop reveal was not moved or ungated. Appending keeps that true; prepending fails it.

   Everything here is still behind .has-location-progress-rail, added by location-motion.js only
   once the fill is genuinely tracking scroll — JS off or failed means no rail and no suppressed
   scrollbar, at every width. */
@media (max-width: 1319px) {
    .has-location-progress-rail .route-thread {
        display: block;
        /* Desktop parks the rail in the page gutter (26px left of the 1180px column). Below
           that the gutter IS the section's own left padding, so the rail element becomes that
           gutter — a full-height, invisible box the line is centred inside. 42px mirrors both
           templates that load this file: place-guide `.chapter { padding: 92px 42px }` and
           mountain-trail `.band { padding: 88px 42px }`. pointer-events:none is inherited by
           the fill, so widening the box keeps it clear of taps and the iOS back-swipe. */
        left: 0;
        width: 42px;
        /* No dashed track on a phone — one solid gold line for the distance travelled, and
           nothing else. The desktop repeating-gradient track above is deliberately untouched. */
        background: none;
    }

    /* Centre the line in the rail, not at a hand-picked offset — the gutter width changes per
       breakpoint, this cannot drift. Width, colour, glow, height and the gold/bronze swap are
       untouched; only the horizontal anchor moves. */
    .has-location-progress-rail .route-thread__fill {
        left: 50%;
        transform: translateX(-50%);
    }

    /* One scroll indicator, not two. Same rules and same guard as the 1320px block above:
       class-gated, and scoped to the root element only (no descendant selector, and
       scrollbar-width does not inherit), so panels, maps, modals and the accordion keep
       their own scrollbars. Scrolling itself is untouched — no overflow change. */
    .has-location-progress-rail {
        scrollbar-width: none; /* Firefox */
    }

    .has-location-progress-rail::-webkit-scrollbar {
        width: 0;
        height: 0;
    }
}

/* The phone step (rail box 42px → 20px) rides in the existing <=760px block further down
   rather than opening a second one here — tests/test_canonical_coast.py reads the FIRST
   `@media (max-width: 760px)` in this file as the mobile-hero block, so an extra one above
   it silently hijacks that read. */

/* ===================================================================
   Canonical-coast experience refinements (Din Lligwy pilot → rolled out)

   These three refinements — a raised hero, a scroll cue, and single-column
   mobile nearby cards — live here so every page that loads this stylesheet
   inherits them with no per-page CSS. This file is loaded by EXACTLY the 36
   canonical-coast place-guide pages (17 Anglesey + 10 Conwy + 7 Gwynedd
   place-guide + cwm-idwal + penrhyn-castle) and nothing else, so these rules
   never reach the mountain-trail or town-village templates.

   The `body `-prefixed selectors are deliberate: they carry one more element
   of specificity than each page's own inline `.location-hero` / `.nearby-grid`
   / `.place-card` rules, so this shared refinement wins over the (now legacy)
   inline values regardless of <link>/<style> order — a responsive-padding and
   layout override, never a transform. Pages authored after this date should
   simply rely on these rules and not re-declare the inline values.
   =================================================================== */

/* Hero raised: lift the bottom-aligned copy off the frame edge and open a
   breathing band below it for the scroll cue. Overrides padding-bottom only;
   the inline 126px top / 42px sides are untouched. */
body .location-hero {
    padding-bottom: 108px;
}

/* Scroll cue: a quiet page-level affordance centred at the base of the hero.
   Pure CSS + a real anchor link — visible and clickable with no JS. It links
   to the page's planning section; location-motion.js only *settles* it (fades
   it out) once the reader begins scrolling. */
.hero-scroll-cue {
    position: absolute;
    left: 50%;
    bottom: 26px;
    z-index: 2;
    transform: translateX(-50%);
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 9px;
    color: rgba(236, 199, 110, 0.82);
    text-decoration: none;
}

.hero-scroll-cue__label {
    color: rgba(250, 241, 218, 0.72);
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.22em;
    text-transform: uppercase;
}

.hero-scroll-cue__chevron {
    width: 20px;
    height: 20px;
    animation: hero-scroll-bob 2.4s ease-in-out infinite;
}

.hero-scroll-cue__chevron svg {
    display: block;
    width: 100%;
    height: 100%;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.hero-scroll-cue:hover,
.hero-scroll-cue:focus-visible {
    color: var(--gold, #e3c47e);
}

.hero-scroll-cue:hover .hero-scroll-cue__label,
.hero-scroll-cue:focus-visible .hero-scroll-cue__label {
    color: var(--cream, #f8efd8);
}

.hero-scroll-cue:focus-visible {
    outline: 2px solid rgba(228, 196, 122, 0.6);
    outline-offset: 6px;
    border-radius: 4px;
}

@keyframes hero-scroll-bob {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(5px); }
}

/* Mobile (<=760px): nearby cards go single-column, image-led, less cramped
   than the old two-up. Distance stays a short gold metadata line; the title
   is prominent; the description is clamped to two lines so each card scans
   cleanly; a stronger overlay guarantees text contrast across the
   mixed-brightness thumbnails. The desktop four-card grid is left untouched. */
/* The hero Save control is retired at EVERY width (2026-07-29): the compact icon
   Save in the header (.nav-save, assets/saved-places.css) is the sole hero-area
   entry point, so Save no longer sits third in the hero's CTA row. .nav-save lives
   in .site-header, not .location-hero, so this rule never touches it. */
body .location-hero [data-saved-place-control] {
    display: none;
}

@media (max-width: 760px) {
    /* Full-viewport mobile hero (canonical-coast place-guide rollout, 2026-07).
       svh is the safe floor (never taller than the visible viewport at any toolbar
       state); the dvh line is the progressive enhancement browsers that support it
       use instead, so the hero keeps filling the true visible area as a mobile
       address bar collapses and no strip of the next section shows on load. The
       body-prefix out-specifies each page's inline mobile `.location-hero { 94svh }`.
       padding-bottom carries safe-area-inset-bottom so the scroll cue clears the
       home indicator. */
    body .location-hero {
        min-height: 100svh;
        min-height: 100dvh;
        padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
    }

    /* Header clears a top notch / status bar on notched devices (landscape / PWA;
       env(safe-area-inset-top) is 0 in normal portrait Safari). The header is
       position:absolute; top:0 on every canonical-coast page, so shifting `top` by
       the inset adds the clearance WITHOUT touching each page's own header padding. */
    body .site-header {
        top: env(safe-area-inset-top, 0px);
    }

    /* Scroll rail, phone step: the content inset narrows, so the invisible rail box does
       too. The line stays centred in it via the translateX(-50%) declared with the rest of
       the mobile rail rules above — there is no second horizontal offset to maintain. */
    .has-location-progress-rail .route-thread {
        width: 20px;
    }

    /* Hero category/type eyebrow: desktop only (2026-07-31).
       The small gold line above the place name ("Pentraeth – Benllech, Anglesey",
       "Eryri · Gwynedd · Mountain") is orientation for a reader with room to
       spare. On a phone it competes with the title for the one band of legible
       space over the photograph, and on a long name it pushes the title down
       into the safe-area padding.

       display:none rather than visibility/opacity is deliberate: both heroes are
       `display: grid; align-items: end`, so removing the element from flow takes
       its 14px bottom margin with it and the remaining copy stays anchored to the
       hero's bottom padding — no residual gap, no offset. The wording itself is
       untouched: it stays in the markup, and desktop is unaffected.

       Scoped to the two hero shells that load this stylesheet — .location-hero
       (place-guide) and .hero__copy (mountain-trail) — so the section eyebrows in
       chapter headers and the final CTA are never caught. */
    body .location-hero .eyebrow,
    body .hero__copy > .eyebrow {
        display: none;
    }

    body .nearby-grid {
        grid-template-columns: 1fr;
        gap: 16px;
        margin-top: 32px;
    }

    body .place-card {
        min-height: 240px;
    }

    body .place-card::after {
        background: linear-gradient(180deg, rgba(24, 40, 33, 0.05) 0%, rgba(24, 40, 33, 0.36) 44%, rgba(24, 40, 33, 0.92) 100%);
    }

    body .place-card-content {
        left: 18px;
        right: 18px;
        bottom: 18px;
    }

    body .place-card-content h3 {
        font-size: 1.34rem;
    }

    body .place-card-content p {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
        margin-top: 6px;
    }
}

/* ---------------- Reduced motion: state changes become instant, no animation ---------------- */
@media (prefers-reduced-motion: reduce) {
    .reveal {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .location-hero__copy > * {
        opacity: 1;
        transform: none;
        animation: none;
    }

    .location-hero::after {
        transition: none;
    }

    /* The rail still tracks scroll (direct manipulation, like a scrollbar),
       but the gold↔bronze crossfade becomes an instant swap. */
    .route-thread__fill {
        transition: none;
    }

    /* The scroll-cue chevron stops bobbing; the cue stays visible and clickable.
       location-motion.js also settles it without a fade under reduced motion. */
    .hero-scroll-cue__chevron {
        animation: none;
    }
}
