/* Sports Tourism — page-scoped. Loaded only by apps/pages/tourism.q. */

/* Sections stay transparent so body's fixed bg-main.jpg shows through, matching the homepage.
   <main> carries no bg class here for the same reason — main.css already sets
   [data-canvas=container]{background-color:transparent}. */
.st-intro { background: transparent; }

/* Get In Touch form section — same transparent treatment as .st-intro above. */
.st-contact { background: transparent; }

/* The comp caps the form well inside the page's normal container width.
   !important is required, and is the theme's own idiom here: main.css:66 sets
   `.container{max-width:1200px !important}` and offers .widish/.wide/.widest modifiers
   (1400/1600/1800), all !important, but nothing narrower. Without it this measured 1200px.
   Two selectors deep as well, because tourism.css is emitted BEFORE Bootstrap's CSS — the app
   appends to $RUBIX->css, which runs before the template's plugin::load(). */
.st-contact .st-contact-inner { max-width: 1000px !important; }

/* Centred divider: rule, dots, rule. The theme's shared .site-bord puts the dots on the LEFT and
   grows a single rule to the right — it styles the line via `> div:last-child`, so it cannot be
   reused for a centred variant without fighting that selector. This is the same 1px rule and the
   same three squares, just balanced. */
.st-divider-line { height: 1px; flex: 1 1 auto; }

/* Pin the hero's imgLiquid backdrop to the section edges.
   `.wbg` is the selector js/visionamp.js:63 uses to turn the <img> into a background on the
   <picture>. The markup carries `left-0`, which is NOT a real class — it exists in neither this
   theme's CSS nor Bootstrap 5 — so the absolutely-positioned <picture> falls back to its static
   position, i.e. inside #st-hero's px-3 padding box: 16px in from the left and, being w-100, 16px
   over the right edge. #st-hero is the only element in the theme pairing `wbg` with horizontal
   padding, which is why no other hero shows this. Do not "fix" it by removing px-3 — that padding
   is what keeps the hero text off the screen edge on phones. */
#st-hero > picture { left: 0; right: 0; }

/* gap:0, not a small gap. The 3px it used to carry was not a border — nothing here draws one —
   it was the page background showing between the tiles as a pale seam. Zeroing it also makes the
   parted-tile gap exactly --st-panel-w rather than that plus 3px, so the book lands edge to edge
   against the tiles instead of leaving a hairline of page down each side of it. */
.st-grid { list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(2,1fr); gap:0; }
.st-tile { position:relative; overflow:hidden; min-height:340px; background-size:cover; background-position:center; }
.st-tile::before { content:''; position:absolute; inset:0; background:rgba(var(--primary-color-rgba),.45); transition:background .3s ease; z-index:1; }
.st-tile:hover::before { background:rgba(var(--primary-color-rgba),.25); }
.st-tile-video { position:absolute; top:50%; left:50%; width:auto; height:100%; min-width:100%; object-fit:cover; transform:translate(-50%,-50%); z-index:0; }
.st-tile-open { position:absolute; inset:0; z-index:2; width:100%; border:0; background:transparent; color:#fff; display:flex; flex-direction:column; justify-content:center; align-items:center; gap:1rem; cursor:pointer; }
/* Size comes from the h1 main pair in the markup, so the tile title tracks the Get In Touch
   heading and the rest of the site through main.css:976+ and its breakpoint overrides, rather
   than from a clamp here that only this page would know about. No font-size in this rule on
   purpose: .h1.main is (0,2,0) against this selector's (0,1,0), so one here could never apply. */
.st-tile-title { text-transform:uppercase; letter-spacing:.06em; font-weight:700; }
/* Learn More matches the house button hover exactly: main.css:2457-2473 gives every .btn
   `border-radius:0` with a 0.3s ease transition and flips it to 5px on hover, and
   main.css:2487-2496 fills .btn-main with --secondary-color and white text. The tile button is a
   <span> inside the tile's <button>, not a .btn, so it inherited none of that -- replicated here
   rather than adding .btn, whose `color: var(--primary-color)` would put dark text on a photo. */
.st-tile-more { border:2px solid #fff; padding:.5rem 1.5rem; text-transform:uppercase; font-size:.8rem;
                letter-spacing:.1em; border-radius:0; transition:all .3s ease; }
.st-tile:hover .st-tile-more,
.st-tile-open:focus-visible .st-tile-more {
    background-color:var(--secondary-color); border-color:var(--secondary-color);
    color:var(--bs-white); border-radius:5px;
}
.st-tile-open:focus-visible { outline:3px solid var(--secondary-color); outline-offset:-6px; }
@media (max-width:767px){ .st-grid{ grid-template-columns:1fr; } .st-tile{ min-height:240px; } }

/* The scrim over the tile grid, per the reference component. Both halves of it — the dimming and the
   click-to-close — earn their place. A centred 900px book leaves the outer half of every tile
   clickable beside it, and clicking one re-enters revealTile() with `.is-open` already set: the
   content swaps instantly, no fold, under a stationary panel. Under the scrim the tile buttons
   are simply unreachable, so that case stops existing rather than needing a guard.
   It hangs on .st-grid-wrap and not on .st-grid because a pseudo-element's clicks are reported
   against the element that generated it — so a click anywhere over a dimmed tile arrives at
   js/tourism.js with `e.target === .st-grid-wrap` and nothing else has to be hit-tested.
   `visibility`, not opacity alone: an opacity-0 scrim would still swallow every tile click while
   the panel is closed. The 0.9s visibility delay on the way out is the end of the close sequence,
   the same number as the panel's own. */
.st-grid-wrap { position:relative; }
/* pointer-events:none because the stage above now owns click-to-close. Left hit-testing, this
   would keep swallowing tile clicks for the full 0.9s after Close — so switching directly from
   one tile to another cost three clicks instead of one.
   The delays here are the NON-book schedule: that path's sheet is gone at 0.5s, so a 0.9s fade
   left the grid sitting dimmed for another 400ms after the panel had left the screen. The book's
   0.4s/0.9s schedule is restated inside its own media query below. */
.st-grid-wrap::before { content:''; position:absolute; inset:0; z-index:3; pointer-events:none;
                        background-color:rgba(var(--primary-color-rgba),.6);
                        opacity:0; visibility:hidden;
                        transition:opacity .5s 0s, visibility 0s .5s; }
body.st-open .st-grid-wrap::before { opacity:1; visibility:visible;
                        transition:opacity .5s 0s, visibility 0s 0s; }

/* ---- the fold panel ---------------------------------------------------------------------
   The CodyHouse "Content Folding" component. Two empty wings whose ::after elements are the
   painted faces that rotate in from edge-on, and a third layer holding the content stacked
   over both once they have landed flat. The content is deliberately NOT inside a wing: a wing
   is 50% wide and overflow:hidden, so a gallery measured in one would get half the panel width
   and be cut in half at the seam.

   The delay pattern throughout, stated once so it does not have to be re-derived at each rule:
   every animated property carries its delay in the BASE rule and 0s in the `.is-open` rule.
   Base = the CLOSING transition, `.is-open` = the OPENING one.
     OPEN   wings rotate at once (.5s 0s) -> content visible at .5s -> children stagger .5/.55/.6/.65
     CLOSE  content fades out first (.2s, reversed stagger, done by .2s) -> wings start folding
            away at .4s and run .5s -> the panel's visibility flips off at .9s
   0.9s is therefore not a round number, it is the end of the sequence — and js/tourism.js
   re-applies the `hidden` attribute on the same 0.9s. Change one, change the other. */

/* Declared on :root rather than on .st-panel because its two consumers are in different
   subtrees — the book's own width, and the distance the grid tiles part — and custom properties
   only inherit downward. The tiles must part by EXACTLY half the panel width: the illusion is
   that the book opens into the gap the tiles vacate, so if the two numbers ever drift the tiles
   either slide under the panel or leave a strip of page showing through the middle. Deriving one
   from the other in CSS is the point; they cannot disagree later. */
:root { --st-panel-w: 1000px; }

/* pointer-events:none on the stage is what lets a click beside the book fall through to the
   scrim and close the panel. .st-book turns hit-testing back on for itself and pointer-events
   inherits, so the wings, the content layer and Close are all live without repeating it.
   perspective stays even though each wing now carries its own: it is what already makes
   #st-panel a stacking context and a containing block for fixed descendants, which is the
   assumption the `body > .fancybox-container` override below is written against. */
.st-panel { position:fixed; inset:0; z-index:100000; display:flex; align-items:stretch;
            justify-content:flex-end; perspective:1000px; pointer-events:none; }
/* The stage IS the dismiss surface: it spans the viewport, so a click anywhere that is not the
   book lands on it and closes the panel. Doing it here rather than on the grid scrim is what
   makes click-outside work everywhere instead of only over the tiles — the scrim only covers
   .st-grid-wrap, so with a short grid the whole Get In Touch section below it stayed live and
   clickable behind an aria-modal dialog on a frozen page.
   Gated on .is-open, which comes off at t=0, and NOT on the `hidden` attribute, which is not
   re-applied until 0.9s: hit-testing has to stop when the panel starts leaving, not when it has
   finished, or the centre of the screen swallows every click for most of a second. pointer-events
   inherits, so this one pair also governs .st-book, the wings, the content layer and Close. */
.st-panel.is-open { pointer-events:auto; }

/* Base = the non-book path: no wings, the whole sheet slides in from the right. This is what
   ships below 992px and on any pointer that cannot hover; the book is layered on top of it in
   the media query further down. */
.st-book { position:relative; width:92%; max-width:var(--st-panel-w); height:100%; overflow:hidden;
           background-color:#fff;
           transform:translateX(100%); transition:transform .5s ease-in-out; }
.st-panel.is-open .st-book { transform:translateX(0); }
.st-fold-left, .st-fold-right { display:none; }

/* The content layer, and the scroll container. overflow-y stays in the BASE rule rather than
   being gated behind .is-open as the reference gates it: reverting to overflow:visible drops
   scrollTop to 0, so a visitor who had scrolled a long panel watches it snap back to the top
   before the fold even starts. */
/* The padding is declared through custom properties because the hero below has to cancel it to
   sit full-bleed, and the two paths do not pad the same: 4rem for the book, a viewport clamp for
   the plain sheet. One literal negative margin would be wrong on one of them, and wrong only at
   certain window widths, which is the kind of thing that survives review and shows up later. */
.st-panel-inner { position:absolute; inset:0; z-index:2; overflow-y:auto;
                  --st-pad-x:clamp(1.5rem,4vw,4rem); --st-pad-y:3rem;
                  padding:var(--st-pad-y) var(--st-pad-x); }

/* The panel hero: the tile's own still with the title across it, so the photo the visitor just
   clicked is the one that greets them. Breaks out of .st-panel-inner's padding by cancelling it
   exactly — hence the custom properties above — and pulls up past the top padding so it meets the
   top edge of the sheet.
   aspect-ratio rather than a fixed height: the client uploads whatever crop they have, and a
   fixed height would letterbox some and crop the heart out of others. object-fit does the work. */
.st-panel-hero { position:relative; margin:calc(var(--st-pad-y) * -1) calc(var(--st-pad-x) * -1) 0;
                 aspect-ratio:21/9; overflow:hidden; display:flex; align-items:flex-end; }
.st-panel-hero img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.st-panel-hero::after { content:''; position:absolute; inset:0;
                        background:linear-gradient(to top, rgba(var(--primary-color-rgba),.85) 0%,
                                                            rgba(var(--primary-color-rgba),.15) 60%,
                                                            rgba(var(--primary-color-rgba),0) 100%); }
.st-panel-hero h2 { position:relative; z-index:1; margin:0;
                    padding:0 var(--st-pad-x) 1.5rem; }
.st-panel-hero ~ * { position:relative; }

/* One photo per accordion section. Sits inside a collapsed panel among body copy, so it reads as
   an illustration of that venue rather than a banner.
   Cropped to a band rather than left at its natural height: the client uploads whatever they have,
   and a portrait shot at width:100% came out 713px tall -- six venues of that is a panel nobody
   scrolls to the end of. A fixed ratio also makes the venues scannable against each other, which a
   column of different-shaped photos is not. max-height caps it on wide viewports where 16:9 of a
   full-width panel is still too much. Nothing is lost to the crop: the fancybox link on the
   wrapper opens the uncropped original. */
.st-section-photo img { width:100%; aspect-ratio:16/9; max-height:340px; object-fit:cover;
                        display:block; }

/* The same venue with several photos gets a carousel, and it is NOT styled here on purpose.
   css/main.css and css/interior.css now name .st-section-gallery alongside #image-carousel in
   every rule that sizes a carousel — slide heights, track widths, the hover scale — so this one
   inherits the tile gallery pixel for pixel at every breakpoint, and keeps inheriting it if those
   numbers ever move. Restating them here is what would let the two drift apart.
   It is a class rather than that id only because an id has to be unique and the tile gallery
   already holds it. */

/* Fancybox must clear the fold panel. Fancybox 3 sets .fancybox-container to z-index 99992 and
   appends it to <body>, a sibling of #st-panel at 100000 — so a lightboxed gallery image opened
   BEHIND the panel. Scoped `body >` rather than !important because jquery.fancybox.min.css loads
   AFTER tourism.css, so a bare .fancybox-container would tie on specificity and lose on order. */
body > .fancybox-container { z-index: 100010; }

/* Back-to-top used to be shielded by nothing more than luck: the old right-hand sheet happened
   to cover the bottom-right corner. A centred book leaves that corner open, and .st-panel is
   pointer-events:none — so .cd-top would stay clickable over the panel and scroll a document
   that html.st-lock has frozen, which reads as a dead button. !important because
   plugins/goTop/css/goTop.css loads AFTER tourism.css and owns visibility/opacity on
   .cd-top.cd-is-visible, so a tie would be lost on order. */
/* Gated on html.st-lock rather than body.st-open, and carrying NO transition of its own.
   st-lock goes on when the panel opens (js/tourism.js:208) and comes off at the END of the close,
   in the same hideTimer callback that re-applies the hidden attribute (js:308). It therefore
   already means exactly "while the panel is on screen", and it means it per path: 900ms for the
   book, 500ms for the plain sheet, 0 under reduced motion. Getting the delayed return for free
   from a class that is already correctly timed is why there is no transition declared here --
   see the navs below for what declaring one costs.
   !important stays: plugins/goTop/css/goTop.css loads AFTER tourism.css and owns visibility and
   opacity on .cd-top.cd-is-visible at equal specificity, so a tie would be lost on source order. */
html.st-lock .cd-top { visibility:hidden !important; opacity:0 !important; pointer-events:none; }
/* main.css:563 pins header / #nav-main-sticky-wrapper / #nav-main at z-index 1005 !important and
   main.css:153 does the same for #nav-mobile. The grid scrim is z-index 3 on a wrapper that is
   not a stacking context, so it competes page-wide and loses: an opaque white nav bar stayed lit
   across the top 70-90px of an aria-modal dialog. Worse for a CENTRED book than for the old
   right-hand sheet, because the book is white too — the bar and the book merged into one strip
   and the book's two vertical edges, the entire point of centring it, vanished into it.
   Held out until the end of the close by html.st-lock, so the nav cannot reappear over a book
   that is still folding.
   This previously got that delay from `transition:visibility 0s .9s` on the bare selectors, and
   the shorthand silently discarded the transitions main.css already gives these elements.
   Measured on this page against any other page of the site: #nav-main lost
   `opacity .5s cubic-bezier(...)`, #nav-mobile lost `all .2s linear`, and <header> kept its own
   but picked up a 0.9s delay, so its scroll animation lagged by almost a second -- on this page
   only, which is exactly the kind of thing nobody would connect back to a tourism stylesheet.
   Do not reintroduce a transition here.

   Faded rather than snapped, and declared as an ANIMATION rather than a transition. That is the
   whole point: a `transition` shorthand on these selectors replaces the one main.css gives them,
   which is the bug described above. `animation` is a separate property and composes with
   whatever transition they already carry, so nothing is clobbered. */
@keyframes st-nav-out { from { opacity:1; visibility:visible; } to { opacity:0; visibility:hidden; } }
@keyframes st-nav-in  { from { opacity:0; } to { opacity:1; } }

/* The way back. js/tourism.js hangs st-nav-back on <html> at the moment it drops st-lock, and
   takes it off again once the fade has run. Declared BEFORE the st-lock block below so that a
   re-open landing inside the fade-in loses to the fade-out on source order. */
html.st-nav-back header, html.st-nav-back #nav-main-sticky-wrapper,
html.st-nav-back #nav-main, html.st-nav-back #nav-mobile { animation:st-nav-in .3s ease; }

/* No !important on visibility, deliberately. An !important declaration outranks a keyframe, so
   it would hide the element on the first frame and the fade would never be seen. The selector is
   already (1,1,1) and nothing else in the theme sets visibility on these, so it does not need one.
   No `forwards` either: when the animation ends, falling back to this rule's own
   visibility:hidden is exactly the right resting state. */
html.st-lock header, html.st-lock #nav-main-sticky-wrapper,
html.st-lock #nav-main, html.st-lock #nav-mobile { visibility:hidden; animation:st-nav-out .3s ease; }

/* Close control, per the comp: a red glyph beside a small, wide-tracked dark label.
   It is a child of .st-book — NOT of .st-panel-inner — and absolutely positioned rather than
   sticky, for two reasons that both fail silently. First, on the book path .st-panel-inner spends
   the opening 0.5s at visibility:hidden, and a visibility:hidden element is not focusable: the
   .st-close focus() that ends revealTile() would be a no-op, leaving keyboard focus on the tile
   behind an aria-modal dialog that has no focus trap. Second, sticky only ever worked because the
   old panel scrolled itself; out here it is pinned to a book that does not scroll, so it stays on
   screen however long the content runs. `top:1rem` puts it inside .st-panel-inner's 3rem of top
   padding, clear of the copied <h2>. */
.st-close { position:absolute; top:1rem; right:1.25rem; z-index:3; border:0; background:transparent;
            text-transform:uppercase; letter-spacing:.2em; font-size:.7rem; font-weight:700;
            cursor:pointer; color:var(--primary-color); padding:0; line-height:1;
            transition:color .3s ease; }
.st-close:hover, .st-close:focus { color:var(--secondary-color); }
/* Over a hero the close control sits on a photo, where the dark --primary-color disappears.
   The glyph is already --secondary-color and reads fine; it is the CLOSE word that needs lifting.
   The shadow is what keeps it legible against a pale crop -- white on white otherwise. */
.st-panel.st-has-hero .st-close { color:#fff; text-shadow:0 1px 3px rgba(0,0,0,.55); }
.st-panel.st-has-hero .st-close:hover,
.st-panel.st-has-hero .st-close:focus { color:var(--secondary-color); text-shadow:none; }
.st-close-x { color:var(--secondary-color); font-size:1.15rem; font-weight:400;
              letter-spacing:0; vertical-align:-2px; margin-right:.2em; }

/* ---- the book: desktop, hovering pointers only ------------------------------------------
   992px is the seam this file already used for the panel, not a new breakpoint. The hover /
   pointer conditions mirror js/tourism.js's canHover probe rather than contradicting it, and
   they keep the book off touch hardware for two concrete reasons: below 768px the grid is a
   single column, so there is no gap for a book to open into and a lone rotating flap reads as a
   rendering glitch; and two full-height composited layers (translateZ + backface-visibility)
   over body's `background: ... fixed` is the iOS Safari pairing documented in CLAUDE.local.md. */
@media (min-width: 992px) and (hover: hover) and (pointer: fine) {

    .st-panel { justify-content:center; visibility:hidden; transition:visibility 0s .9s; }
    .st-panel.is-open { visibility:visible; transition:visibility 0s 0s; }

    /* The book takes 0.9s to close where the plain sheet takes 0.5s, so the scrim has to hold
       the dim longer here — it must not clear before the wings have finished folding away. */
    .st-grid-wrap::before { transition:opacity .5s .4s, visibility 0s .9s; }

    /* The book box itself never moves — the wings do. Neutralise the slide from the base rule. */
    .st-book { width:var(--st-panel-w); max-width:100%; transform:none;
               background-color:transparent; transition:background-color 0s .4s; }
    /* Seam insurance. Once the faces have landed (0.5s) the book paints its own white behind
       them, so a sub-pixel hairline where the two 50% halves meet shows white and not the page
       scrolling underneath. It gives that white back up at 0.4s on the way out — the instant the
       faces start folding away. Hold it any longer and the fold happens in front of a white box,
       which is the whole effect gone. */
    .st-panel.is-open .st-book { transform:none; background-color:#fff; transition:background-color 0s .5s; }

    /* Both wings share ONE vanishing point, at the centre seam — that is what makes the two
       faces read as one sheet of paper rather than two independent flaps. */
    .st-fold-left, .st-fold-right { display:block; position:relative; float:left; z-index:1;
                                    width:50%; height:100%; overflow:hidden; perspective:2000px; }
    .st-fold-left  { perspective-origin:100% 50%; }   /* its own right edge */
    .st-fold-right { perspective-origin:0 50%; }      /* its own left edge  */

    .st-fold-left::after, .st-fold-right::after {
        content:''; position:absolute; top:0; left:0; width:100%; height:100%;
        transform:translateZ(0); backface-visibility:hidden;
        transition:transform .5s .4s, background-color .5s .4s;
    }
    /* Closed: each face is hinged at the book's OUTER edge and swung edge-on to the viewer. */
    .st-fold-left::after  { transform-origin:left center;
                            transform:translateX(100%) rotateY(90deg);
                            background-color:#fff; }
    /* The right face starts DARKER and lightens to #fff over the same 0.5s as the rotation.
       Edge-on it is the shadowed underside of a page being turned; flat, it matches the left.
       This is a lighting model, not decoration — drop it and the same geometry reads as two
       panels appearing rather than a book opening. The reference runs #c0c3c3 -> #f9fafa; the same
       perceptual drop against this theme's pure-white sheet lands at #c8cbcb. */
    .st-fold-right::after { transform-origin:right center;
                            transform:translateX(-100%) rotateY(-90deg);
                            background-color:#c8cbcb; }
    .st-panel.is-open .st-fold-left::after,
    .st-panel.is-open .st-fold-right::after { transform:translateX(0);
                            transition:transform .5s 0s, background-color .5s 0s; }
    .st-panel.is-open .st-fold-right::after { background-color:#fff; }

    /* Literal padding, not the clamp() the non-book path uses. The panel is a fixed 1000px here,
       so a viewport-keyed 4vw would shrink the content column by 32px as the window grows from
       1200 to 1600 — a fixed panel whose contents are not. 1000 − 128 leaves an 872px column,
       comfortably wider than the two measures this page already picked for itself: the intro's
       col-xl-8 of a 1200 container (778.67px) and the Get In Touch blurb's col-lg-10 of the
       1000px .st-contact-inner (806.67px). It also keeps the three-across gallery honest —
       872 ÷ 3 is a 290px slide against the 337.9px the same markup gets elsewhere on the site. */
    .st-panel-inner { --st-pad-x:4rem; --st-pad-y:3rem;
                      padding:var(--st-pad-y) var(--st-pad-x); visibility:hidden;
                      transition:visibility 0s .4s; }
    .st-panel.is-open .st-panel-inner { visibility:visible; transition:visibility 0s .5s; }

    /* Per-element stagger. The base rule is the way OUT and it is REVERSED — last thing in is
       the first thing out — so the copy has cleared by 0.2s, well before the wings start folding
       at 0.4s and the snap back to scrollTop 0 becomes visible. */
    .st-panel-content > *              { opacity:0; transform:translateY(40px);
                                         transition:opacity .2s 0s, transform .2s 0s; }
    .st-panel-content > *:nth-child(1) { transition-delay:.2s; }
    .st-panel-content > *:nth-child(2) { transition-delay:.15s; }
    .st-panel-content > *:nth-child(3) { transition-delay:.1s; }
    .st-panel-content > *:nth-child(4) { transition-delay:.05s; }

    /* `transform:none` and not `translateY(0)` for the landed state. translateY(0) is still a
       transform, and it would leave .int-content and .st-gallery as permanent containing blocks
       for any position:fixed a CMS-authored embed drops into $v['content'] (tourism.q:168 echoes
       it raw). `none` interpolates from translateY(40px) identically. */
    .st-panel.is-open .st-panel-content > *                { opacity:1; transform:none;
                                                             transition:opacity .2s .5s, transform .2s .5s; }
    .st-panel.is-open .st-panel-content > *:nth-child(2)   { transition-delay:.55s; }
    .st-panel.is-open .st-panel-content > *:nth-child(3)   { transition-delay:.6s; }
    .st-panel.is-open .st-panel-content > *:nth-child(n+4) { transition-delay:.65s; }

    /* Fades in with the content so it is not left hovering over a folding book — but via
       opacity, never visibility, because an opacity-0 element is still focusable and
       revealTile()'s focus() has to land at t=0. */
    .st-close { right:1.75rem; opacity:0; transition:color .3s ease, opacity .2s 0s; }
    .st-panel.is-open .st-close { opacity:1; transition:color .3s ease, opacity .2s .5s; }

    /* The tiles part to make the gap. The distance is derived, never typed — see --st-panel-w.
       The transition lives on .st-tile unconditionally, not on `body.st-open .st-tile`: gate it
       on the open class and removing that class takes the transition away in the same style
       change that resets the transform, so the tiles snap home instead of sliding. */
    .st-grid .st-tile { transition:transform .5s .4s ease-in-out; }
    body.st-open .st-grid .st-tile:nth-of-type(odd)  { transform:translateX(calc(var(--st-panel-w) / -2));
                                                       transition:transform .5s 0s ease-in-out; }
    body.st-open .st-grid .st-tile:nth-of-type(even) { transform:translateX(calc(var(--st-panel-w) / 2));
                                                       transition:transform .5s 0s ease-in-out; }
}

@media (prefers-reduced-motion: reduce) {
    /* One sledgehammer instead of re-stating a dozen selectors at matching specificity: most of
       what animates above is declared from inside `.st-panel.is-open`, so a plain
       `transition:none` here would lose the cascade to the very rules it has to cancel. Only
       motion is suppressed — every end state is left exactly as it is, so the panel is simply
       already open, which is the correct degradation for a rotation in depth. */
    .st-panel, .st-book, .st-panel-inner, .st-panel-content > *, .st-close,
    .st-fold-left::after, .st-fold-right::after,
    .st-grid-wrap::before, .st-grid .st-tile { transition:none !important; }
    /* The nav fade is an animation, not a transition, so the sweep above does not reach it.
       Cancelling it leaves the plain visibility:hidden, which is the correct degradation --
       the nav still goes, it just goes at once. */
    html.st-lock header, html.st-lock #nav-main-sticky-wrapper,
    html.st-lock #nav-main, html.st-lock #nav-mobile,
    html.st-nav-back header, html.st-nav-back #nav-main-sticky-wrapper,
    html.st-nav-back #nav-main, html.st-nav-back #nav-mobile { animation:none !important; }
    /* And the tiles do not move at all. Snapping 450px sideways with no transition is not a
       reduced version of the push — it is precisely the vestibular jolt this query exists to
       avoid. js/tourism.js reads the same query and drops its hide delay to 0 to match. */
    body.st-open .st-grid .st-tile:nth-of-type(odd),
    body.st-open .st-grid .st-tile:nth-of-type(even) { transform:none; }
}

/* The lock is on <html>, not <body>: main.css:3-6 puts overflow-x:hidden on the root, so the
   root is no longer `visible` and body→viewport overflow propagation has stopped — a body-level
   lock would clip nothing. Deliberately its own class and not .no-scroll, which js/visionamp.js
   blind-toggles for the offcanvas menu. No touch-action here, unlike .no-scroll: it inherits,
   and it would make the panel's own content layer unscrollable on a phone. */
html.st-lock { overflow-y:hidden; padding-right:var(--st-sbw,0px); }

/* The lock pads the DOCUMENT to replace the scrollbar it removed, so the content box keeps its
   old width and its old centre. .st-panel is position:fixed with no transformed ancestor, so its
   containing block is the VIEWPORT, which just got wider by a scrollbar — leaving the book
   centred half a scrollbar to the right of the gap the tiles opened for it. A ~7.5px strip of
   dimmed page showed down one edge of the book while the far tile column tucked under the other,
   asymmetric for the whole open state, at exactly the seam the derived push exists to make
   impossible. The derivation was right; the two coordinate spaces were not the same.
   Invisible on macOS overlay scrollbars, which is how it survives dev. Bootstrap pads .modal
   for the same reason it pads the body. .st-panel centres with flexbox, so padding shifts that
   centring by exactly half the bar. */
html.st-lock .st-panel { padding-right:var(--st-sbw,0px); }
