/* The rooms page ("Every room") and the room popup (the open case folder),
 * from the designer's revised board. Loaded by rooms_page.html after
 * tokens.css and components.css; the popup fetched into that page by
 * landing.js is styled by the same sheet. Everything here places
 * components.css's pieces (labels, glass, buttons) on this page.
 *
 * Every colour is a token (tokens.css). The ART is referenced from CSS by
 * url(): a path in a template ({{{@/web/...}}}) is cache-busted, a path
 * here is NOT, and the browser keeps it for a year — so an image named in
 * this file must be RENAMED if it ever changes, never edited in place.
 */
/* `clip`, not `hidden`: the banner strip is full-bleed (100vw) and a
   hidden box would be a scroll container. */
.library-page { overflow-x: clip; background: var(--void-black) url(/web/images/bg-top.webp) top center / 100% auto no-repeat; }
.library-main { width: var(--container); margin: 0 auto; padding: var(--space-16) 0 7rem; }

/* ---- the banner: the keyhole and three lines of title, a torn strip of
   paper right across the page behind the second and third ---- */
.library-banner { position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; column-gap: clamp(var(--space-24), 5vw, var(--space-64)); padding: var(--space-24) 0 0 clamp(var(--space-8), 8vw, 110px); }
/* The strip is a fifth of the page wide, as the board has it; the art is
   a little taller than that and takes the slight squash. */
.library-banner::before { content: ""; position: absolute; top: 22%; left: 50%; width: 100vw; height: min(19vw, 245px); translate: -50% 0; background: url(/web/images/banner-torn.webp) center / 100% 100% no-repeat; pointer-events: none; }
/* The heading's parts are positioned, so they paint over the strip (an
   absolute pseudo-element). */
.library-heading { position: relative; }
/* Cream on the tan as the board sets it, readable by its dark edge: the
   shadow is the outline the board draws, and the big size does the rest. */
.library-heading h1 { position: relative; color: var(--aged-paper); font-size: clamp(2.8rem, 8vw, 6.4rem); line-height: 1; text-shadow: 0 0 1px var(--void-black), 2px 2px 0 var(--void-black), 0 0 16px var(--scrim); }
.library-heading h1 > span { display: block; }
.library-heading h1 > span:first-child { padding-left: .7em; }
.library-heading h1 > span:last-child { padding-left: .25em; }
/* Wrapped a little early, so the magnifier's handle comes down beside it. */
.library-tagline { position: relative; max-width: 26ch; margin: var(--space-32) 0 0 .3em; color: var(--aged-paper); font-size: var(--text-lg); line-height: 1.25; }
.library-tagline mark { display: inline-block; margin-top: .25em; padding: .05em .3em; background: var(--aged-paper); color: var(--brand-mark-red); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
/* Two short lines: the magnifier's handle comes down to the right of them.
   `flex-wrap` and `nowrap` together: without them the items shrink and
   wrap INSIDE themselves at phone width, so "35 rooms" broke across two
   lines. The separator dot is for a reader only. */
.library-stats { position: relative; display: flex; flex-wrap: wrap; gap: .6rem; max-width: 15rem; margin: var(--space-16) 0 0 .3em; color: var(--paper-dim); font-size: var(--text-xs); font-weight: 800; letter-spacing: .1em; text-transform: uppercase; font-variant-numeric: tabular-nums; }
.library-stats > span { white-space: nowrap; }
.library-stats > [aria-hidden] { display: none; }

/* The keyhole, large and still: the wall and the four agents clipped to
   the hole, the metal frame over the seam; it overhangs the strip. */
.keyhole-still { position: relative; width: clamp(110px, 14.5vw, 185px); aspect-ratio: 3 / 5; filter: drop-shadow(0 12px 24px var(--scrim)); }
.keyhole-still img { position: absolute; display: block; }
.keyhole-cut { position: absolute; inset: 5% 12.5% 6.5%; overflow: hidden; background: var(--panel); clip-path: url(#keyhole-cut); }
.keyhole-cut .keyhole-wall { inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; filter: brightness(.9); }
.keyhole-agents { left: 50%; bottom: -2%; width: 150%; translate: -50% 0; }
.keyhole-frame { inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: contain; }

/* The magnifying glass over the last letter. The art is the rim and
   handle only, the lens cut out (its centre 36% / 26.8% of the art, its
   radius 29.2% of the art's width); the glass is drawn here, dark as the
   board's old loupe had it, clear at the top left falling to black, with
   its glare laid over the heading's own "s", larger, in the heading's
   face. The word's real "s" stays in the markup for a reader, see-through
   so the two never double. */
.lens-letter { position: relative; display: inline-block; }
.lens-under { color: transparent; text-shadow: none; }
.lens { position: absolute; top: 56%; left: 52%; width: 2.5em; aspect-ratio: 700 / 956; translate: -36% -26.8%; pointer-events: none; }
.lens img { position: absolute; inset: 0; width: 100%; height: 100%; filter: drop-shadow(.04em .08em .08em var(--scrim)); }
.lens-glass { --glare: var(--type-on-dark); position: absolute; top: 26.8%; left: 36%; display: grid; place-items: center; width: 58.4%; aspect-ratio: 1; padding-bottom: .1em; translate: -50% -50%; overflow: hidden; border-radius: 50%; color: color-mix(in srgb, var(--type-on-dark) 72%, transparent); font-size: 1.75em; line-height: .7; text-shadow: none; background: linear-gradient(140deg, color-mix(in srgb, var(--type-on-dark) 30%, transparent) 8%, color-mix(in srgb, var(--void-black) 55%, transparent) 42%, var(--void-black) 78%); box-shadow: inset 0 0 .1em var(--void-black); }
/* The glare, over the letter: a long arc inside the rim from the top round
   to the right, and two short streaks at the top left. */
.lens-glass::before, .lens-glass::after { content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; }
.lens-glass::before { inset: 5%; border: .06em solid transparent; border-top-color: var(--glare); rotate: 42deg; filter: blur(.004em); }
.lens-glass::after { background: radial-gradient(ellipse 12% 4% at 50% 11%, var(--glare) 75%, transparent 100%), radial-gradient(ellipse 9% 3.5% at 27% 19%, var(--glare) 75%, transparent 100%), radial-gradient(ellipse 4% 3% at 18% 31%, var(--glare) 70%, transparent 100%); rotate: -12deg; }


/* ---- the archive: a cream heading, then the folder in three slices ---- */
/* The section is the narrower column, not the slab inside it: the slab's
   percentage padding resolves against its PARENT's width while its slice
   pseudo-elements measure its own, so the two must be one width (Codex
   review). */
.all-rooms { width: 100%; margin-inline: auto; padding-top: var(--space-64); }
.library-section-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: var(--space-48); color: var(--aged-paper); }
/* Lined up with the folder's paperclip (7.8% of the folder's width in). */
.library-section-heading h2 { padding-left: 7.8%; font-size: clamp(1.9rem, 3.4vw, 2.8rem); line-height: 1.1; }
/* The count is typed on the front sheet, top right above the cards, in
   faded ink multiplied into the paper so its grain shows through. */
.results-count .clear-search { pointer-events: auto; color: inherit; text-decoration: underline; }
.results-count { position: absolute; top: 5.6cqi; left: 11.5%; margin: 0; color: color-mix(in srgb, var(--ink) 88%, var(--paper-dim)); font-family: var(--font-display); font-size: clamp(.85rem, 1.25cqi, 1.1rem); letter-spacing: .03em; mix-blend-mode: multiply; pointer-events: none; }
/* The top and bottom slices are pseudo-elements the height their picture
   dictates (aspect-ratio, so a percentage of the width); the middle slice
   repeats down the content box, and the padding is exactly the two slice
   heights so it never shows above the tab or below the folder's edge. The
   content then pulls itself back up onto the paper with negative margins.
   The -v2 middle slice ends the way the top one does and the -v2 bottom
   one starts the way the middle does, so no join draws a line across the
   paper; `round` fits a whole number of middles, so none is cut short
   above the bottom slice (the slices' own outline still steps a little). */
.archive-slab { position: relative; padding: 34.75% 0; background: url(/web/images/archive-middle-v2.webp) center top / 100% auto no-repeat round content-box; }
.archive-slab::before, .archive-slab::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; aspect-ratio: 2000 / 695; background: center / 100% 100% no-repeat; pointer-events: none; }
.archive-slab::before { z-index: 1; background-image: url(/web/images/archive-top-v4.webp); }
.archive-slab::after { top: auto; bottom: 0; background-image: url(/web/images/archive-bottom-v2.webp); }
/* The index tabs. archive-top-v4.webp is the folder with a bare divider
   strip from just past the paperclip to the loose sheets on the right: the
   strip's top is 2.9% of the folder's width down, the front sheet's edge
   3.7%. Three tabs stand on that strip from the left, sized in cqi (1% of
   the folder's width) so they scale with the art, and made to look handled
   rather than new: each is as wide as its word plus its own margin, a
   little taller or shorter than its neighbours, cut by hand (three shapes,
   tab-shape-a/b/c.svg, whose edges are roughened and softened inside the
   SVG), in the folder's paper (tab-paper-v2.webp, each tab reading its own
   stretch) gone darker at the top and sides where fingers hold it, with a
   faint cut edge and faded ink. The chosen one is in front: lighter, and
   reaching down over the front sheet's edge, so it reads as that sheet's
   own tab; the others sit behind it on the divider. */
.archive-top-controls { position: absolute; top: 0; right: 0; left: 0; z-index: 2; aspect-ratio: 2000 / 695; container-type: inline-size; }
.case-tabs { --tab-h: max(36px, 3.4cqi); position: absolute; top: calc(2.9cqi - var(--tab-h)); left: 12.2cqi; display: flex; align-items: flex-end; height: var(--tab-h); }
.case-tab { --shape: url(/web/images/tab-shape-a.svg); --pad: max(12px, 1.8cqi); --lift: 0px; position: relative; isolation: isolate; display: flex; flex: 0 1 auto; align-items: center; justify-content: center; min-width: 0; height: calc(100% - var(--lift)); padding: 0 var(--pad); color: color-mix(in srgb, var(--ink) 82%, var(--paper-dim)); font-family: var(--font-display); font-size: clamp(.8rem, 1.35cqi, 1.15rem); font-weight: 400; letter-spacing: .02em; line-height: 1; text-decoration: none; white-space: nowrap; }
.case-tab:nth-child(1) { --pad: max(16px, 2.6cqi); --paper-x: 6%; }
.case-tab:nth-child(2) { --shape: url(/web/images/tab-shape-b.svg); --pad: max(18px, 3.4cqi); --lift: .35cqi; margin-left: .35cqi; }
.case-tab:nth-child(3) { --shape: url(/web/images/tab-shape-c.svg); --pad: max(14px, 2.4cqi); --lift: .15cqi; margin-left: .8cqi; --paper-x: 94%; }
.case-tab:nth-child(4) { --shape: url(/web/images/tab-shape-b.svg); --pad: max(14px, 2.2cqi); --lift: .25cqi; margin-left: .5cqi; --paper-x: 30%; }
.case-tab::before, .case-tab::after { position: absolute; z-index: -1; content: ""; pointer-events: none; -webkit-mask: var(--shape) center / 100% 100% no-repeat; mask: var(--shape) center / 100% 100% no-repeat; }
.case-tab::before { inset: -1px -1px 0; background: color-mix(in srgb, var(--ink) 26%, transparent); }
.case-tab::after { inset: 0; background: radial-gradient(130% 150% at 50% 100%, transparent 50%, color-mix(in srgb, var(--ink) 18%, transparent)), linear-gradient(color-mix(in srgb, var(--ink) 12%, transparent), transparent 22%), url(/web/images/tab-paper-v2.webp) var(--paper-x, 50%) 0 / 600px auto, color-mix(in srgb, var(--paper-dim) 32%, var(--aged-paper)); background-blend-mode: normal, normal, multiply; }
/* The chosen tab reaches 2.6cqi down into the front sheet, over its edge
   line (3.7cqi), and dissolves into it over the last 1.6cqi, so there is no
   seam where tab becomes sheet; its cut edge fades out before that. */
.case-tab.is-current { --reach: 2.6cqi; z-index: 1; height: calc(100% - var(--lift) + var(--reach)); margin-bottom: calc(var(--reach) * -1); padding-bottom: var(--reach); color: color-mix(in srgb, var(--brand-mark-red) 85%, var(--paper-dim)); }
.case-tab.is-current::before, .case-tab.is-current::after { -webkit-mask: var(--shape) center / 100% 100% no-repeat, linear-gradient(var(--void-black) calc(100% - 1.6cqi), transparent); -webkit-mask-composite: source-in; mask: var(--shape) center / 100% 100% no-repeat, linear-gradient(var(--void-black) calc(100% - 1.6cqi), transparent); mask-composite: intersect; }
.case-tab.is-current::before { -webkit-mask: var(--shape) center / 100% 100% no-repeat, linear-gradient(var(--void-black) 35%, transparent calc(100% - var(--reach))); -webkit-mask-composite: source-in; mask: var(--shape) center / 100% 100% no-repeat, linear-gradient(var(--void-black) 35%, transparent calc(100% - var(--reach))); mask-composite: intersect; }
.case-tab.is-current::after { background: linear-gradient(color-mix(in srgb, var(--ink) 9%, transparent), transparent 16%), linear-gradient(color-mix(in srgb, var(--paper-dim) 16%, transparent), color-mix(in srgb, var(--paper-dim) 16%, transparent)), url(/web/images/tab-paper-v2.webp) var(--paper-x, 50%) 0 / 600px auto; background-blend-mode: normal; }
a.case-tab:not(.is-current):hover { color: color-mix(in srgb, var(--brand-mark-red) 85%, var(--paper-dim)); }
.case-tab.is-locked { color: color-mix(in srgb, var(--ink) 74%, var(--paper-dim)); cursor: not-allowed; }
.case-tab:focus-visible { border-radius: 8px 8px 0 0; outline: 3px solid var(--brand-mark-red); outline-offset: 2px; }
/* The sticker begins under the front sheet's right edge (92% of the art),
   then lies over the numbered sheets behind it. Clipping its hidden 28px
   also keeps the click target on the visible part of the sticker. */
/* The folder's index stickers, sort then difficulty, spaced evenly down the
   right edge between the front sheet's top corner (14% of the top slice)
   and the binder clip (57%). */
.archive-index { position: absolute; top: 14%; bottom: 43%; left: calc(92% - 28px); z-index: 3; display: flex; flex-direction: column; justify-content: space-evenly; align-items: start; }
.sort-strip { --strip-h: clamp(38px, 3.6vw, 46px); position: relative; display: inline-flex; align-items: center; justify-content: center; width: clamp(72px, 6.8vw, 86px); height: var(--strip-h); padding-left: 28px; clip-path: inset(0 0 0 28px); background: linear-gradient(90deg, color-mix(in srgb, var(--sort-marker) 70%, var(--void-black)), var(--sort-marker) 55%); color: var(--aged-paper); cursor: pointer; rotate: 1deg; filter: drop-shadow(2px 3px 2px var(--scrim)); }
.sort-strip::before { position: absolute; top: 0; bottom: 0; left: 28px; width: 9px; background: linear-gradient(90deg, color-mix(in srgb, var(--void-black) 45%, transparent), transparent); content: ""; pointer-events: none; }
.sort-strip svg { width: 1.25rem; height: 1.25rem; flex: none; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
.sort-strip select { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.sort-strip:focus-within { outline: 3px solid var(--aged-paper); outline-offset: 3px; }
.difficulty-strip { rotate: -1deg; }
.difficulty-strip .stat-level circle, .difficulty-strip circle { fill: currentColor; }
/* The front sheet runs from 6% to 92.3% of the folder's width; the grid keeps
   about 4% of paper clear of its edges on every side, and off the sheets
   behind it. */
.archive-inner { position: relative; z-index: 2; margin: -24% 0 -26%; padding: 0 11.5% 0 10%; color: var(--ink); pointer-events: none; }
/* Its clear margins cross the side marker. Only the actual cards and paging
   controls should receive clicks there. */
.archive-inner > * { pointer-events: auto; }
.empty-results { padding: 3rem 0; color: var(--ink); text-align: center; }

/* The grid: photographs on the paper, grey until chosen; the chosen one in
   colour with its caption as a black slip over its lower part, so nothing
   in the grid moves. Three across on every laptop, so a page of twelve is
   four full rows: an auto-fill at the old 288px minimum fell to two below
   ~1220px. At 901px a card is ~200px and its stats strip still fits on
   one line; two across on a tablet, one on a phone (below). */
.all-rooms-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(var(--space-24), 4vw, var(--space-48)) var(--space-32); }
.archive-card { position: relative; display: block; min-height: 0; border-radius: var(--radius-md); color: var(--aged-paper); text-decoration: none; }
/* A card the filters hide must still go: `display: block` above would
   outrank the browser's own [hidden]. */
.archive-card[hidden] { display: none; }
.archive-card:focus-visible { outline: 3px solid var(--brand-mark-red); outline-offset: 3px; }
/* A print lying on the paper: a soft shadow under it, no hard edge line. */
.card-art { position: relative; z-index: 1; display: block; aspect-ratio: 288 / 268; overflow: hidden; border-radius: var(--radius-md); background: var(--paper-dim); box-shadow: 0 10px 18px -10px color-mix(in srgb, var(--void-black) 60%, transparent); }
.archive-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) brightness(.85); transition: filter var(--motion-base) var(--ease); }
.archive-card:hover img, .archive-card:focus-visible img, .archive-card:focus-within img { filter: none; }
.card-art .cover-missing { color: var(--ink); }
/* Pinned to the photograph's corners: NEW top right, FREE top left (D39). */
.card-art .card-new, .card-art .card-free { position: absolute; top: var(--space-8); right: var(--space-8); z-index: 1; display: block; padding: .28rem .55rem; border-radius: var(--radius-sm); background: var(--brand-mark-red); color: var(--aged-paper); font-size: var(--text-xs); font-weight: 800; letter-spacing: .12em; text-transform: uppercase; }
/* FREE is the one a visitor should spot from across the grid (D39: what a
   free account may play): the torn red label, padded like every label,
   tilted on like a sticker and lifted off the photograph by a shadow. */
.card-art .card-free { top: var(--space-16); right: auto; left: var(--space-16); padding: var(--space-8) var(--space-24); border-radius: 0; background: url(/web/images/label-red-37-v2.svg) center / 100% 100% no-repeat; color: var(--aged-paper); font-size: var(--text-sm); line-height: 1.2; rotate: -2deg; filter: drop-shadow(0 3px 6px color-mix(in srgb, var(--void-black) 55%, transparent)); }
/* A room the signed-in viewer has escaped (`solvedRooms`): in colour from
   the start, with the SOLVED stamp top right. */
.archive-card.is-solved img { filter: none; }
.archive-card.is-solved .card-art::after { content: ""; position: absolute; top: 4%; right: 3%; z-index: 2; width: 34%; aspect-ratio: 430 / 345; background: url(/web/images/stamp-solved.webp) center / contain no-repeat; rotate: -12deg; }
/* The caption is a .glass slip over the photograph's foot, shown on hover. */
.card-caption { position: absolute; right: 0; bottom: 0; left: 0; z-index: 1; display: grid; gap: var(--space-4); padding: var(--space-8) var(--space-16) var(--space-8); border-radius: 0 0 var(--radius-md) var(--radius-md); color: var(--aged-paper); opacity: 0; transition: opacity var(--motion-base) var(--ease); }
.archive-card:hover .card-caption, .archive-card:focus-visible .card-caption, .archive-card:focus-within .card-caption { opacity: 1; }
/* No pointer to hover with: the slip is simply there. */
@media (hover: none) { .card-caption { opacity: 1; } }
.archive-card strong { justify-self: start; padding-bottom: .15em; border-bottom: 2px solid var(--brand-mark-red); font-family: var(--font-display); font-size: var(--text-lg); font-weight: 500; line-height: 1.1; overflow-wrap: anywhere; }
/* The stats strip: what a browser decides on — how hard, how many
   played, how good — always on one line; the icons carry the units, and
   hidden text reads them out. */
.card-stats { display: flex; align-items: center; gap: var(--space-16); font-variant-numeric: tabular-nums; }
.card-stats > span { display: inline-flex; align-items: center; gap: .3rem; color: var(--aged-paper); font-size: var(--text-sm); white-space: nowrap; }
.card-stats svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.stat-level circle { fill: currentColor; }
.card-stats > .stat-rating { font-weight: 700; }
.stat-rating svg { fill: var(--brand-mark-red); stroke: none; }
.card-stats > .stat-rating.is-empty { color: var(--paper-dim); font-weight: 400; }
.stat-rating.is-empty svg { fill: none; stroke: currentColor; }
/* The gauge's needle turns to the tier: low for easy, right over for expert. */
.gauge-needle { transform-box: view-box; transform-origin: 12px 12.6px; transform: rotate(-130deg); }
[data-difficulty="medium"] .gauge-needle, .gauge[data-tier="medium"] .gauge-needle { transform: rotate(0deg); }
[data-difficulty="hard"] .gauge-needle, .gauge[data-tier="hard"] .gauge-needle { transform: rotate(90deg); }
[data-difficulty="expert"] .gauge-needle, .gauge[data-tier="expert"] .gauge-needle { transform: rotate(135deg); }

/* ---- the pager: twelve rooms a page, four rows across the folder ---- */
/* Typed on the folder's paper under the last row, in its ink; the page you
   are on is stuck on the torn red label and is not a link. Every target is
   44px; a phone has about 270px of paper, so it keeps the arrows without
   their words and only the first, last and current page (`pager-far` is a
   page or ellipsis only a laptop shows, `pager-phone` one only a phone does
   — `pagerItems`). */
.pager { display: grid; justify-items: center; gap: var(--space-8); margin-top: var(--space-32); color: var(--ink); font-family: var(--font-display); }
.pager-list { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--space-4) var(--space-8); margin: 0; padding: 0; list-style: none; }
.pager-list a, .pager-here, .pager-off, .pager-gap { display: inline-flex; align-items: center; justify-content: center; gap: .45em; min-width: 44px; min-height: 44px; padding: 0 var(--space-8); color: var(--ink); font-size: var(--text-lg); font-variant-numeric: tabular-nums; text-decoration: none; }
.pager-list a:hover { color: var(--brand-mark-red); }
.pager-list a:focus-visible { outline: 3px solid var(--brand-mark-red); outline-offset: 2px; border-radius: var(--radius-sm); }
.pager-list svg { width: .9em; height: .9em; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
/* The torn label's red with a worn finish: scratches.webp (grain, specks
   and three passes of scratches at an angle, cream and black over nothing)
   over the token red, both cut to the torn label's shape. */
.pager-here { padding: 0 var(--space-16); background: url(/web/images/scratches.webp) center / 120px auto, var(--brand-mark-red); color: var(--aged-paper); -webkit-mask: url(/web/images/label-red-37-v2.svg) center / 100% 100% no-repeat; mask: url(/web/images/label-red-37-v2.svg) center / 100% 100% no-repeat; }
.pager-off { color: color-mix(in srgb, var(--ink) 85%, var(--aged-paper)); }
.pager-gap { min-width: 24px; padding: 0; }
.pager-prev { margin-right: var(--space-16); }
.pager-next { margin-left: var(--space-16); }
.pager-phone { display: none; }
.pager-range { margin: 0; color: color-mix(in srgb, var(--ink) 85%, var(--aged-paper)); font-size: var(--text-lg); font-variant-numeric: tabular-nums; }

@media (max-width: 900px) {
  .all-rooms-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-24) var(--space-16); }
  /* The strip is too short for three words here: the tabs span the front
     sheet instead, clear of the paperclip. */
  .case-tabs { --tab-h: 38px; left: 14cqi; }
  .case-tab { font-size: .85rem; }
}

@media (max-width: 620px) {
  /* The title keeps the column beside the keyhole; the tagline and the
     stats take the whole width under both, out of the magnifier's way. */
  .library-banner { grid-template-areas: "keyhole title" "tagline tagline" "stats stats"; column-gap: var(--space-16); padding: var(--space-16) 0 0 var(--space-8); }
  .library-banner::before { top: 26%; }
  /* Both scale with the phone: at 320px the title's column is ~180px, and
     "Unlocked" cannot wrap (Codex review). */
  .keyhole-still { grid-area: keyhole; width: clamp(84px, 26vw, 110px); }
  .library-heading { display: contents; }
  .library-heading h1 { grid-area: title; font-size: clamp(1.9rem, 11vw, 2.8rem); }
  .library-heading h1 > span:first-child { padding-left: 0; }
  .library-tagline { grid-area: tagline; max-width: none; margin: var(--space-24) var(--space-8) 0; font-size: var(--text-base); }
  .library-stats { grid-area: stats; max-width: none; margin-left: var(--space-8); }
  .library-section-heading { align-items: start; flex-direction: column; }
  .case-tabs { --tab-h: 34px; left: 13cqi; }
  .case-tab { font-size: .78rem; }
  /* The stickers leave the edge for a row on the paper, above the cards. */
  .results-count { top: calc(3.7cqi + 10px); left: 10%; font-size: .78rem; line-height: 36px; }
  .archive-index { top: calc(3.7cqi + 10px); right: 10%; bottom: auto; left: auto; flex-direction: row; gap: var(--space-8); }
  .sort-strip { --strip-h: 36px; width: 46px; padding-left: 0; clip-path: none; }
  .sort-strip::before { display: none; }
  .archive-inner { padding-top: 30px; }
  .archive-inner { margin-top: -22%; }
  .all-rooms-grid { grid-template-columns: minmax(0, 1fr); }
  .pager-list { gap: 0; }
  .pager-list a, .pager-here, .pager-off { min-width: 36px; padding: 0 var(--space-4); }
  .pager-word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .pager-prev, .pager-next { margin: 0; }
  .pager-far { display: none; }
  .pager-phone { display: inline-flex; }
}

@media (max-width: 400px) {
  .case-tab { font-size: .72rem; }
}

@media (max-width: 360px) {
  .case-tab { font-size: .68rem; letter-spacing: 0; }
}

/* ---- the room popup: an open case folder over the page ---- */
/* /rooms/<slug> renders it open; landing.js opens it in place from a card,
   so choosing a room never leaves the library. */
body.has-room-modal { overflow: hidden; }
.room-modal { position: fixed; inset: 0; z-index: 30; display: grid; place-items: center; padding: var(--space-24); }
.room-modal-backdrop { position: absolute; inset: 0; background: color-mix(in srgb, var(--void-black) 80%, transparent); backdrop-filter: blur(6px); }
/* The leaves sit on the folder's two pages by percentage GRID TRACKS, which
   resolve against the poster itself — padding percentages would resolve
   against the modal, and drift once the folder is capped or height-bound
   (Codex review). */
.room-poster { --folder-paper: color-mix(in srgb, var(--aged-paper) 55%, var(--paper-dim)); position: relative; display: grid; grid-template-columns: 5% minmax(0, 1fr) 6% minmax(0, 1fr) 7%; grid-template-rows: 7.5% minmax(0, 1fr) 5.5%; column-gap: 0; width: min(1120px, 100%, calc((100vh - 3rem) * 1600 / 1174)); aspect-ratio: 1600 / 1174; background: url(/web/images/folder-open.webp) center / 100% 100% no-repeat; color: var(--ink); filter: drop-shadow(0 30px 60px var(--scrim)); }
.room-close { position: absolute; top: 5%; right: 4.5%; z-index: 3; display: grid; place-items: center; width: 48px; height: 48px; border-radius: var(--radius-full); color: var(--brand-mark-red); }
.room-close svg { width: 34px; height: 34px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.room-close:hover { color: var(--ink); }
.room-close:focus-visible, .poster-play:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }

/* The popup's torn labels are headings (the case number, "Mission
   details", "Top escapes"): components.css draws them like every label,
   8px by 24px around the word; here they are smaller and bold, and being
   no button, they neither lift nor point. */
.room-poster .label-red { display: inline-block; margin: 0; font-size: var(--text-sm); font-weight: 700; letter-spacing: .1em; line-height: 1.2; cursor: default; }
.room-poster .label-red:hover { transform: none; }

/* Left leaf: one black slip, the room's picture filling it inside an 8px
   mount, the case label pinned over its corner. The name of the room sits
   ON the picture, on a .glass strip at its foot. */
.poster-art { grid-area: 2 / 2; position: relative; display: flex; flex-direction: column; justify-content: flex-end; min-height: 0; margin-top: var(--space-20); padding: var(--space-8); background: var(--void-black); color: var(--aged-paper); }
.poster-art .case-label { position: absolute; top: -1.1em; left: -.6em; z-index: 1; rotate: -2deg; }
/* The cover is 4:3 with the narrator left of centre. */
.poster-art img { position: absolute; inset: var(--space-8); width: calc(100% - 2 * var(--space-8)); height: calc(100% - 2 * var(--space-8)); object-fit: cover; object-position: 30% 35%; }
.poster-titles { position: relative; z-index: 1; display: grid; gap: var(--space-8); padding: var(--space-16) var(--space-24) var(--space-20); }
.poster-titles .card-meta { display: flex; align-items: center; gap: var(--space-8); color: var(--aged-paper); }
.poster-titles .gauge { width: 1.1rem; height: 1.1rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.poster-titles .gauge circle { fill: currentColor; }
.poster-titles .is-free { color: var(--aged-paper); font-weight: 800; }
.poster-titles h2 { display: -webkit-box; justify-self: start; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 4; padding-bottom: .15em; border-bottom: 2px solid var(--brand-mark-red); color: var(--aged-paper); font-size: clamp(1.8rem, 2.6vw, 2.4rem); line-height: 1.05; overflow-wrap: anywhere; }
.poster-narrator { margin: 0; color: var(--aged-paper); font-size: var(--text-sm); }

/* Right leaf: the mission details, scrolling under the sticky Start. */
.poster-body { grid-area: 2 / 4; display: flex; flex-direction: column; gap: var(--space-20); min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--paper-dim) transparent; }
.poster-body > .label-red { align-self: flex-start; font-size: var(--text-lg); }
.poster-stats { display: flex; flex-wrap: wrap; gap: var(--space-16); margin: 0; }
.poster-stats > div { display: flex; flex-direction: column-reverse; justify-content: flex-end; gap: var(--space-4); }
.poster-stats > div + div { padding-left: var(--space-16); border-left: 1px solid var(--ink); }
.poster-stats dd { display: flex; align-items: center; gap: .35rem; margin: 0; color: var(--ink); font-size: var(--text-xl); font-weight: 600; font-variant-numeric: tabular-nums; }
.poster-stats dd svg { width: .95rem; height: .95rem; fill: var(--brand-mark-red); }
/* The small labels are black tags, as the board draws them. */
.poster-stats dt { align-self: flex-start; padding: .15em .5em; background: var(--void-black); color: var(--aged-paper); font-size: var(--text-xs); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; white-space: nowrap; }
.poster-story { margin: 0; color: var(--ink); font-size: .95rem; line-height: 1.6; }
/* The objective, title and text, inside the red brush frame. */
.poster-task { display: grid; gap: var(--space-4); padding: var(--space-16) var(--space-24) var(--space-20); background: url(/web/images/frame-objective-v2.svg) center / 100% 100% no-repeat; text-align: center; }
.poster-task p { margin: 0; }
.poster-task .kicker { color: var(--brand-mark-red); font-size: var(--text-base); letter-spacing: .12em; }
.poster-task p:last-child { color: var(--ink); font-size: var(--text-base); font-weight: 400; line-height: 1.4; }
/* The board: a numbered list on the paper. */
.poster-board { border-radius: var(--radius-md); }
.poster-board header { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: var(--space-8); }
.poster-board h3 { color: var(--paper-dim); }
.poster-board header span { color: var(--ink); font-size: var(--text-sm); }
.board-row { display: grid; grid-template-columns: 1.4rem minmax(0, 1fr) auto; align-items: center; gap: var(--space-8); padding: var(--space-4) 0; }
.board-rank { color: var(--ink); font-size: .75rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.board-row.is-first .board-rank { color: var(--brand-mark-red); }
.board-avatar { display: none; }
.board-name { min-width: 0; }
.board-name strong, .board-name small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-name strong { color: var(--ink); font-size: .875rem; font-weight: 700; }
.board-name small { color: var(--ink); font-size: .75rem; opacity: .7; }
.board-time { color: var(--ink); font-size: .75rem; font-weight: 400; font-variant-numeric: tabular-nums; }
.board-empty { margin: 0; color: var(--ink); font-size: .85rem; }
/* Play never scrolls away: a long story runs under it. */
.poster-cta { position: sticky; bottom: 0; display: grid; gap: var(--space-8); margin-top: auto; padding: var(--space-16) 0 var(--space-8); background: linear-gradient(180deg, transparent 0%, var(--folder-paper) 1.25rem); }
.poster-cta p { margin: 0; color: var(--ink); font-size: .75rem; line-height: 1.4; text-align: center; }
/* What the plans mean for THIS room (D39), said before Play: a paper note,
   a fact about the room and not a warning colour. */
.poster-cta .poster-plan { padding: .7rem .85rem; border: 1px dashed var(--ink); border-radius: var(--radius-md); background: var(--aged-paper); color: var(--ink); font-size: .82rem; line-height: 1.45; text-align: left; }
.poster-plan strong { color: var(--ink); font-weight: 800; }
.poster-plan a { color: var(--ink); text-decoration: underline; text-underline-offset: .15em; }
/* START: a big torn red label, centred, padded like every label. The link
   is a .button for its focus ring; this is everything else. */
.poster-play { min-height: auto; margin-inline: auto; padding: var(--space-8) var(--space-24); border: 0; border-radius: 0; background: url(/web/images/label-red-39-v2.svg) center / 100% 100% no-repeat; color: var(--aged-paper); font-family: var(--font-display); font-size: var(--text-2xl); font-weight: 400; letter-spacing: .14em; line-height: 1.2; text-transform: uppercase; }
/* .button:hover repaints the background: the label stays the label. */
.poster-play:hover { background: url(/web/images/label-red-39-v2.svg) center / 100% 100% no-repeat; }
.poster-play svg { width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }

@media (prefers-reduced-motion: no-preference) {
  .room-poster { animation: poster-in 220ms ease-out; }
  .room-modal-backdrop { animation: backdrop-in 220ms ease-out; }
}
@keyframes poster-in { from { opacity: 0; transform: translateY(12px) scale(.985); } }
@keyframes backdrop-in { from { opacity: 0; } }

/* Tablet and phone: one scrolling sheet of paper, Start pinned at the
   bottom while the rest scrolls under it — 900px, since the folder's two
   leaves need ~820px. */
@media (max-width: 900px) {
  .room-modal { padding: 0; }
  /* Fade only: a transform on the poster would make the pinned Play bar and
     the close button position against it, not the screen, mid-animation. */
  .room-poster { display: block; width: 100%; height: 100%; aspect-ratio: auto; overflow-y: auto; background: var(--folder-paper) url(/web/images/paper-sheet.webp) top center / 100% auto repeat-y; filter: none; animation-name: backdrop-in; }
  .room-close { position: fixed; top: .6rem; right: .6rem; background: var(--folder-paper); }
  /* The slip needs a height of its own here: the picture is behind it,
     and the titles alone would leave it a strip. */
  .poster-art { min-height: min(118vw, 560px); margin: 4.5rem 1.25rem 0; }
  .poster-titles h2 { font-size: 2rem; -webkit-line-clamp: 3; }
  .poster-body { padding: 1.25rem 1.25rem 9.5rem; overflow: visible; }
  /* The CTA is fixed here, and the plans note makes it taller than the
     9.5rem the body reserves: room for the note at its tallest, four lines
     on a 320px phone. */
  .poster-body:has(.poster-plan) { padding-bottom: 16.5rem; }
  .poster-cta { position: fixed; right: 0; bottom: 0; left: 0; padding: 1rem 1.25rem 1.25rem; background: linear-gradient(180deg, transparent 0%, var(--folder-paper) 26%); }
}
