/* ============================================================================
   ЖАБА ДУШИТЬ — styles.
   Ontology, and everything follows from it:
     PAGE      = paper. Screened white. Chrome, margins, gutters.
     PANEL     = the drawn world. Ink + cel colour. Any palette, incl. night.
     CHARACTER = the frog. Not printed. Stands ON the page, in the gutter.
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }
/* Belt and braces: this design bleeds panels off the page edge and hangs the
   frog over gutters, so a stray overhang must never hand the reader a
   horizontal scrollbar. */
/* scroll-behavior: the nav's «Ігри» is a same-page jump to the shelf on the
   front page, and an instant teleport there reads as a broken page load.
   §17 already forces this back to auto under reduced motion. */
html { -webkit-text-size-adjust: 100%; overflow-x: hidden; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--face-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  overflow-x: hidden;
}
/* height:auto matters — every <img> here carries width/height attributes for
   layout stability, so a CSS width without it KEEPS the attribute height and
   stretches the picture (the letter's frog shipped 3× taller than drawn). */
img { max-width: 100%; height: auto; display: block; }
a { color: var(--sky-ink); }
:focus-visible {
  outline: 3px solid var(--ink);      /* 19.99:1. Amber would be 2.49:1 — it
                                          cannot carry a focus ring either.   */
  outline-offset: 3px;
  border-radius: 4px;
}
.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.svg-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ============================================================
   1. THE PAPER — and its colour is the average of the dot screen
   ============================================================
   There is no fill anywhere producing the faintly-cool white you see. Cyan,
   magenta and yellow dot grids at 15°/75°/0°, each offset a hair, optically
   average to ~#F5F6F8. That is how printing works; it is the one texture the
   defaults cannot fake. It sits BEHIND the panels — bare paper shows its
   screen; a panel's ink covers it. The ontology, rendered.                  */
.paper-screen {
  position: fixed;
  inset: 0;
  width: 100%;            /* an <svg> is a replaced element — inset:0 alone
                             leaves it at its intrinsic 300x150             */
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: var(--screen-alpha);
  mix-blend-mode: multiply;
}
.page { position: relative; z-index: 1; }

/* Registration marks — the page corners of a printed sheet. */
.reg-mark {
  position: absolute;
  width: 22px; height: 22px;
  color: var(--ink-40);
  pointer-events: none;
}
.reg-mark--tl { top: 8px;  left: 8px;  }
.reg-mark--tr { top: 8px;  right: 8px; transform: rotate(90deg);  }
.reg-mark--bl { bottom: 8px; left: 8px; transform: rotate(-90deg); }
.reg-mark--br { bottom: 8px; right: 8px; transform: rotate(180deg); }

/* ============================================================
   2. THE INK — the boil
   ============================================================
   Every component's outline lives on a dedicated PLATE behind the content, so
   the displacement filter never touches an element containing text. That is
   how "the wobble is on the ink, never on the layout" survives contact with
   feDisplacementMap: nothing reflows, no baseline moves, ever.              */
.plate {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--paper);
  border: var(--ink-w) solid var(--ink);
  border-radius: var(--radius-panel);
  box-shadow: var(--drop) var(--drop) 0 var(--ink);   /* solid. no blur.     */
  filter: url(#boil-0);
  pointer-events: none;
}
.plate--heavy { border-width: var(--ink-w-heavy); box-shadow: var(--drop-lg) var(--drop-lg) 0 var(--ink); }
.plate--flat  { box-shadow: none; }

@keyframes boil3 {
  0%,     33.32% { filter: url(#boil-0); }
  33.33%, 66.65% { filter: url(#boil-1); }
  66.66%, 100%   { filter: url(#boil-2); }
}
/* The interface is redrawn because it genuinely is redrawn — 3 alternate ink
   outlines swapped at ~8fps. Not a hover-lift. Not a Material shadow.       */
.boils:hover      > .plate,
.boils:focus-within > .plate { animation: boil3 var(--boil-dur) steps(1, end) infinite; }

/* ============================================================
   3. TYPE
   ============================================================ */
.masthead__word,
.display { font-family: var(--face-display); font-weight: 900; line-height: var(--lh-tight); }

/* Balloon = what he SAYS: performed. Caption = what he KNOWS: not.
   The typeface's own informality axis carries the site's core distinction.  */
.say {
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' var(--infm-say), 'BNCE' var(--bnce-say), 'SPAC' 0;
  font-weight: 700;
}
.know {
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' var(--infm-know), 'BNCE' var(--bnce-know), 'SPAC' 0;
  font-weight: 600;
}
.util { font-family: var(--face-util); font-weight: 500; }
.micro {
  font-family: var(--face-util);
  font-size: var(--t-micro);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 600;
}

/* ============================================================
   4. PAGE CHROME — masthead, nav pill, house mark
   ============================================================ */
.page-head {
  position: relative;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--s6) var(--page-pad) var(--s5);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s5);
  flex-wrap: wrap;
}
/* Masthead lockup, strapline and nav are ONE flex block that decides
   internally whether the nav fits beside the masthead (inner pages, 2.5rem)
   or drops under it (the front page's bigger lockup) — one rule, no
   per-page overrides. flex-basis 0 stays load-bearing from the era when a
   standalone mark held the opposite edge; it still keeps the block from
   quoting its content width and overflowing the row. */
.page-head__id {
  display: flex;
  /* flex-start, not flex-end: the ctrls column (nav pill + lang pill) is
     taller than the lockup on every page, so bottom-aligning hung the logo
     off the LANG row with dead air above it. The ident rides the top line,
     level with the nav; the pills run below it. */
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s5);
  flex-wrap: wrap;
  flex: 1 1 0;
  min-width: 0;
}
/* The lockup — badge, then name. The classic magazine arrangement: the
   publisher's seal small, the title set in the publisher's own gold.
   The badge is a circular cover-crop of the emblem centred on its ring
   (the ring is the middle ~47% of the art, so a square crop at 49% 44%
   keeps the whole ring and a breath of its ground). Its background is a
   LITERAL #0B0907, never var(--ink): the den inverts --ink to paper-white
   and the gold cannot take a light ground. Contour and shadow DO ride the
   token, so on the den the badge draws light lines like every plate there. */
.masthead {
  text-decoration: none;
  color: inherit;
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: fit-content;
}
.masthead__badge {
  flex: none;
  width: clamp(48px, 4.6vw, 62px);
  aspect-ratio: 1;
  object-fit: cover;
  object-position: 49% 44%;
  /* Tighter than cover can go: the ring is Ø~700px centred at (735, 440) of
     1495×909, and plain cover leaves it at 73% of the disc with the arc copy
     mushing the top. This view-box is a 760px square on the ring's centre —
     ring at 92% of the disc, whole. Engines without object-view-box keep the
     cover crop above: same badge, just looser. */
  object-view-box: inset(6.6% 25.4% 9.8% 23.7%);
  border-radius: 50%;
  background: #0B0907;
  border: var(--ink-w) solid var(--ink);
  box-shadow: 4px 4px 0 var(--ink);
}
.masthead__badge--lead { width: clamp(64px, 7vw, 96px); }
.masthead__word {
  position: relative;
  font-size: var(--t-masthead);
  letter-spacing: -0.015em;
  margin: 0;
  isolation: isolate;
}
.masthead__word span { display: block; position: relative; }
/* Gold lettering. The gradient is the emblem's own three-step gold ramp and
   the INK carries legibility: a centred stroke on every edge plus a solid
   offset copy behind — the ::before that used to hold the cyan misreg plate.
   (Misregistration retired with the Cyrillic masthead: CMY plates under gold
   read as mud, and gold leaf was never printed in four colours.) Gold fill
   alone is ~2.5:1 on paper — the contour is load-bearing, the same law that
   lets ink sit on amber. Strokes are in em so the ink scales with the word:
   0.032em is 2.8px on the 5.4rem front masthead, 1.3px on inner pages.
   The @supports gate is not decoration: without background-clip:text the
   span would render transparent, so outside it the masthead stays plain ink. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .masthead__word span {
    background: linear-gradient(178deg, var(--gold-hi) 6%, var(--gold) 54%, var(--gold-sh) 97%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-stroke: 0.032em var(--ink);
  }
  .masthead__word span::before {
    content: attr(data-w);
    position: absolute;
    inset: 0;
    z-index: -1;
    color: var(--ink);
    -webkit-text-fill-color: var(--ink);
    transform: translate(0.052em, 0.052em);
  }
}
.masthead__sub {
  font-family: var(--face-util);
  font-size: var(--t-small);
  color: var(--ink-70);
  margin: var(--s2) 0 0;
  font-weight: 500;
}

/* Nav — a floating pill, outlined. Not a bar. */
.nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s1);
  padding: var(--s2);
  isolation: isolate;
}
.nav > .plate { border-radius: 999px; }
.nav a {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 var(--s4);
  border-radius: 999px;
  font-family: var(--face-util);
  font-weight: 600;
  font-size: var(--t-small);
  color: var(--ink);
  text-decoration: none;
}
.nav a:hover { background: var(--ink-07); }
.nav a[aria-current='page'] { background: var(--ink); color: var(--paper); }

/* The language row — the nav's pill one size down. Three editions of the
   same issue: УКРАЇНСЬКА is the paper's own voice, ХАРКІВСЬКА is what this
   publication calls Russian, ENGLISH is for guests. The active edition is
   STAMPED the way the nav marks the current page — ink plate, paper type —
   so the reader already knows how to read it. The row hides until i18n.js
   boots: without JS the buttons would be furniture. */
.page-head__ctrls {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s2);
}
.lang {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s1);
  padding: var(--s1);
  isolation: isolate;
}
/* display:flex above would beat the UA's [hidden] rule — restate it */
.lang[hidden] { display: none; }
.lang > .plate { border-radius: 999px; }
.lang__opt {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 0 var(--s3);
  border: 0;
  border-radius: 999px;
  background: none;
  font-family: var(--face-util);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: var(--ink);
  cursor: pointer;
}
.lang__opt:hover { background: var(--ink-07); }
.lang__opt.is-on { background: var(--ink); color: var(--paper); }

/* The big standalone house-mark plate (2026-07-19, lived one morning) is
   retired: the emblem moved INTO the masthead as the badge above, and the
   name it carried is now the wordmark itself. */

/* ============================================================
   5. TIERS, PANELS, THE GUTTER
   ============================================================
   Panel AREA is hierarchy. Games and posts share every tier, so you cannot
   look at one without the other. LAW: no two panels in a tier share a width
   — if they do, it's a card grid and I lied about it.                      */
.tiers {
  position: relative;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--page-pad);
}
.tier {
  position: relative;
  display: grid;
  gap: var(--gutter);
  margin-bottom: calc(var(--gutter) + var(--drop));
}
/* Tier 1 leads with TWO featured panels — the latest post and the latest game,
   side by side, unequal. Both in focus, neither behind the other, and nothing
   standing between them. There is no third column: the host's margin went with
   the host. Variant B below still keeps one, on the other edge. */
.tier--1 { grid-template-columns: minmax(0, 1.44fr) minmax(0, 1fr); }
/* Variant B — the host's margin is on the LEFT, because empty-4's own crop is
   on its left and bottom. The plate chooses the edge, not me. */
.tier--1b {
  grid-template-columns: var(--host-col) minmax(0, 1.44fr) minmax(0, 1fr);
  /* real value, not a var() fallback — his balloon has to be anchored to HIS
     box, and his box changes width with the viewport */
  --frog-w: clamp(240px, calc(var(--bleed) + 250px), 560px);
}
/* clear his palm — it crosses the border on purpose, but it may not eat a word */
.tier--1b > .panel--feature:nth-of-type(1) .panel__body { padding-left: calc(var(--s5) + 62px); }
.tier--2 { grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr) minmax(0, 1.5fr); }
.tier--3 { grid-template-columns: minmax(0, 1.68fr) minmax(0, 0.88fr) minmax(0, 1.2fr); }
.tier__gutter, .tier__host { position: relative; }

/* The two leads. Area is hierarchy — these are simply bigger.
   .panel.panel--feature, not .panel--feature: `.panel { min-height: 180px }`
   below has identical specificity and comes later, so for the whole life of
   this file the clamp never applied. Above 900px `.tier--1 { min-height:420px }`
   masked it; below 900px it did not, and both leads collapsed to content
   height with the title standing in the scrim's clear zone. */
.panel.panel--feature { min-height: clamp(340px, 42vh, 430px); }
.panel--feature .panel__title { font-size: var(--t-splash); }

/* The lead game's cover is the multiplier. It IS the game — no art needed. */
.game-mult {
  font-family: var(--face-display);
  font-weight: 900;
  font-size: clamp(2.6rem, 5.4vw, 4.6rem);
  color: var(--paper);
  -webkit-text-stroke: 6px var(--ink);
  paint-order: stroke fill;
  text-shadow: 7px 8px 0 var(--ink);
  transform: rotate(-5deg);
  line-height: 1;
}

/* ---- The NEW flash — a ribbon tag hooked over the picture's edge.
   PRINTED, not stuck on: ink face, paper die-cut, hard offset shadow. It is
   deliberately not a §11 sticker — those quote reality and take a soft real
   shadow, and this one is the publication's own ink talking about its own
   game.
   It hangs off the RIGHT edge, and that is measured rather than preferred.
   Above this panel there are exactly 24px (.page-head's bottom padding) and
   the nav pill spends 6 of them on its own drop shadow, so the entire budget
   over the top edge is 18px — enough to float a tag inside the frame, not
   enough to hang one across the line and still leave air under the nav. The
   right edge has --bleed, which is 120px at 1440 and never less than the page
   padding. ---- */
.flash {
  --flash-cut: polygon(0 0, 100% 0, calc(100% - 9px) 50%, 100% 100%, 0 100%);
  position: absolute;
  z-index: 5;
  top: 26px;
  /* Overhang as far as the margin allows and never past the screen edge.
     --bleed measures screen edge → content box, and 10 pays for the 5px
     shadow plus air. It does NOT also have to pay for the scrollbar that
     100vw refuses to subtract: below 1280 the max(0,…) term is zero and
     --bleed collapses to --page-pad, which is exact. Above 1280 the error is
     ~7px and the clamp is already sitting on its 30px ceiling. */
  right: calc(-1 * clamp(0px, var(--bleed) - 10px, 30px));
  clip-path: var(--flash-cut);
  background: var(--paper);
  padding: 3px;
  transform: rotate(-3.5deg);
  /* drop-shadow, not box-shadow: filters run AFTER the clip, so the shadow
     follows the notch. A box-shadow would be cut away with the corner. */
  filter: drop-shadow(5px 6px 0 var(--ink));
  pointer-events: none;      /* the panel under it is one big link (§5) */
}
/* Amber on ink, which is the marquee's own pairing and the one ground amber
   is legible on — 7.97:1, against 2.49:1 on paper. It also keeps the tag from
   reading as one more .caption box: those are ink on white, unturned. */
.flash__in {
  display: block;
  clip-path: var(--flash-cut);        /* inherited custom property */
  background: var(--ink);
  color: var(--amber);
  padding: 6px 19px 6px 13px;         /* +6 right: the notch eats into it */
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' 74, 'BNCE' 34;
  font-weight: 800;
  font-size: 0.86rem;
  line-height: 1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.panel {
  position: relative;
  isolation: isolate;
  min-height: 180px;
  display: flex;
}
.panel__body {
  position: relative;
  z-index: 1;
  padding: var(--s5);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  width: 100%;
}
.panel__art {
  position: relative;
  z-index: 1;
  margin: var(--ink-w);
  border-radius: 2px;
  overflow: hidden;
  flex: 1;
  min-height: 120px;
}

/* ---- The art cover — a panel whose whole interior is a picture.
   Promoted out of games-sections.html's local <style>, where the idiom was
   specced against 8 cards. That page re-declares every rule below using the
   `background` SHORTHAND, which resets position/size/repeat wholesale, so it
   keeps its own crop and its own -7% bleed and renders unchanged.
   Art lives INSIDE the ink contour: inset --ink-w, inner radius 5 − 3 = 2.
   Never inside .plate — .plate carries filter:url(#boil-0) unconditionally
   (§2), and a boiling photograph reads as a codec failing, not as a hand.
   The boil belongs to the ink. ---- */
.artfill { position: absolute; inset: var(--ink-w); z-index: 0; border-radius: 2px; overflow: hidden; }
.artfill__img {
  position: absolute;
  inset: 0;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}
/* Overscan for the plane's travel, and nothing else. app.js clamps nothing:
   d = ((r.top + r.height/2) − vh/2)/vh keeps being written until r.top passes
   vh + 200, so |d| is not bounded to ±0.5. MEASURED by sweeping the panel
   through an 845×391 landscape viewport: |d| peaks at 1.67, i.e. 10.0px of
   travel at rate 0.10 — and at -4% the worst clearance came back 6.5px, which
   is a pass by luck rather than by margin. -6% of the shortest art box we ship
   (374px) is 22px, so ~12px of slack survives the worst frame.
   A PERCENTAGE inset grows both axes by the same fraction of their own
   dimension, so the box aspect is untouched and `cover` picks the same window,
   only larger; a px inset would skew the aspect and move the crop.
   Raising any data-plane here above 0.16 voids this number. */
.artfill__img.plane { inset: -6%; }
/* The splash's ramp, not the game panel's old .66@42% — that one was tuned
   for flat colour under type. A photograph needs the extra 6 points under the
   title band, because a photograph has a bright end and a flat plate has not. */
.artfill__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(11,9,7,.94) 4%, rgba(11,9,7,.72) 38%, rgba(11,9,7,0) 72%);
}
/* In the den the night is coloured, so the scrim has to be too — otherwise the
   frame lies in one night and the band around it in another. Hardcoded rgba
   like every scrim on the site: --ink inverts to paper-white in the den, and a
   photograph does not invert with it. */
.is-den .artfill__scrim {
  background: linear-gradient(to top,
    rgba(var(--den-scrim, 31,13,47), .97) 8%,
    rgba(var(--den-scrim, 31,13,47), .64) 46%,
    rgba(var(--den-scrim, 31,13,47), 0) 80%);
}
/* The lead's multiplier frame. Deliberately NOT .artfill__mult — that name is
   live on 8 corner-anchored spans in games-sections.html, and adding inset /
   display / padding under it would leak past that page's partial override and
   move all eight.
   top:-24px is headroom, not position: the number rides data-plane 0.2 and
   .artfill clips. The padding gives the same 24px back, so what governs
   clipping is the RESTING offset (24px) against the travel, not the size of
   this box — move both by the same amount and the glyph does not shift at all.
   MEASURED on an 845×391 landscape viewport, sweeping the whole scroll range:
   travel tops out near 20px, so the glyph's worst approach to the frame edge
   is ~4px and it never crosses. That worst frame is only reached with the card
   5% on screen — app.js stops updating past vh+200 and the transform freezes
   there. While the card is actually being looked at (≥60% visible) the
   clearance is 14px. The rotate(-5deg) lift is inside those numbers already,
   because they were measured rather than derived. */
.artfill__num {
  position: absolute;
  inset: -24px 0 0 0;
  display: grid;
  place-items: start start;
  padding: 48px 0 0 var(--s5);
  pointer-events: none;
}
/* The lead game's cover. 55% 26% is derived, not taste: 55% is the only
   horizontal value holding the toad whole from a 1.16 art box down to a 0.90
   one, and 26% is what keeps both eyes and the sweat drops in frame once the
   panel goes letterbox below 900px. The file itself is cut to aspect 1.153
   against a 1.154 desktop art box, so on desktop the overflow is half a pixel
   and this line does nothing — which is the point of cropping the file rather
   than steering it from CSS. */
.artfill__img--catch {
  background-image: url(assets/bus-catch-cover.webp);
  background-position: 55% 26%;
}
/* The text stack over a cover. position:STATIC on purpose — a flex item takes
   z-index and forms a stacking context without being positioned, so the stack
   still paints above the art, but `.panel__title a::after { inset: 0 }` now
   resolves against .panel__body instead of this wrapper, and the WHOLE card
   becomes the link. Before this, roughly half of a feature panel was inert:
   tolerable over a flat plate, misleading over art that invites the click. */
.panel__stack {
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  align-items: flex-start;
}
.panel--sky   > .plate { background: var(--sky); }
.panel--sky   { color: var(--paper); }
.panel--flesh > .plate { background: var(--flesh); }
.panel--amber > .plate { background: var(--amber); }  /* ink-on-amber 8.02:1 */

/* The splash bleeds off the left edge — runs to the paper's edge with no
   margin. One bleed says "comic page" louder than any amount of halftone.  */
.panel--splash {
  margin-left: calc(-1 * var(--page-pad));
}
.panel--splash > .plate { border-left: 0; border-radius: 0 var(--radius-panel) var(--radius-panel) 0; }

.panel__kicker { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }

.panel__title {
  font-family: var(--face-display);
  font-weight: 900;
  line-height: var(--lh-tight);
  font-size: var(--t-h3);
  margin: 0;
  letter-spacing: -0.01em;
}
.panel--splash .panel__title { font-size: var(--t-splash); }
.panel__title a { color: inherit; text-decoration: none; }
.panel__title a::after { content: ''; position: absolute; inset: 0; z-index: 3; }
.panel__dek { margin: 0; color: var(--ink-70); font-size: 1rem; }

/* ---- Caption box = what he KNOWS. Posts wear one. ---- */
.caption {
  position: relative;
  align-self: flex-start;
  padding: var(--s2) var(--s3);
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 2px;
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' var(--infm-know), 'BNCE' 0;
  font-weight: 600;
  font-size: var(--t-micro);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
}

/* ---- Balloon = what he SAYS. Games wear one. The tail is load-bearing. -- */
.balloon {
  position: relative;
  align-self: flex-start;
  max-width: 30ch;
  padding: var(--s3) var(--s4);
  background: var(--paper);
  border: var(--ink-w) solid var(--ink);
  border-radius: var(--radius-balloon);
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' var(--infm-say), 'BNCE' var(--bnce-say);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1.35;
  color: var(--ink);
}
/* The tail. Without it a balloon is a box, so it is never optional and it is
   never subtle — a comic tail is a chunky wedge, not a hairline notch. */
.balloon::after {
  content: '';
  position: absolute;
  left: 30px; bottom: -21px;
  width: 34px; height: 26px;
  background: var(--paper);
  border-right: var(--ink-w) solid var(--ink);
  border-bottom: var(--ink-w) solid var(--ink);
  transform: skewX(-26deg) rotate(5deg);
  border-radius: 0 0 10px 0;
}
.balloon--right::after { left: auto; right: 30px; transform: skewX(26deg) rotate(-5deg); border-radius: 0 0 0 10px;
  border-right: 0; border-left: var(--ink-w) solid var(--ink); }
.balloon--up::after { bottom: auto; top: -21px; transform: skewX(26deg) rotate(5deg);
  border-bottom: 0; border-top: var(--ink-w) solid var(--ink); border-radius: 10px 0 0 0; }

/* ============================================================
   5b. THE BOOTH — the shelf, and it holds one game
   ============================================================
   Promoted out of games-sections.html Г5, where the idiom was drawn against
   three stalls standing in a row. Here there is one, so the row is gone and
   what survives is the STRUCTURE: an awning wider than the box it shades,
   festoons hanging off its lip, a counter ruled off along the bottom. That
   still reads as a stall at a count of one — which is the point, because the
   site genuinely ships one game and a grid of three would be a lie about the
   catalogue. The word for what three of these make is never printed on the
   page: the apparatus says it, or it doesn't get said.                     */
.shelf {
  --shelf-w: 780px;
  margin: 0 0 calc(var(--gutter) + var(--drop));
  /* the anchor target for every game click on the site — land with air over
     the heading, not with the awning against the viewport edge */
  scroll-margin-top: var(--s6);
}
.shelf__head,
.shelf__row,
.shelf__foot { max-width: var(--shelf-w); margin-inline: auto; }
.shelf__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s3) var(--s4);
  margin-bottom: var(--s6);
}
.shelf__h {
  font-family: var(--face-display);
  font-weight: 900;
  font-size: var(--t-h2);
  line-height: var(--lh-tight);
  margin: 0;
}
.shelf__foot { margin: var(--s5) auto 0; }
/* .caption ships align-self:flex-start for panel stacks; in a headline row it
   has to sit on the headline's baseline like a word would. */
.shelf__head .caption { align-self: baseline; }

/* border-top:0 — the awning IS the top edge. A booth that draws its own lid
   underneath its awning is a box wearing a hat. The margin-top is the room
   the awning needs to be both taller and wider than the thing it shades. */
.booth {
  position: relative;
  isolation: isolate;
  margin-top: 26px;
  background: var(--paper);
  border: var(--ink-w) solid var(--ink);
  border-top: 0;
  border-radius: 0 0 var(--radius-panel) var(--radius-panel);
  box-shadow: var(--drop) var(--drop) 0 var(--ink);
}
.booth__awn {
  position: relative;
  height: 38px;
  margin: -20px -12px 0;
  background: repeating-linear-gradient(90deg, var(--ink) 0 22px, var(--paper) 22px 44px);
  border: var(--ink-w) solid var(--ink);
  border-radius: 4px;
  box-shadow: 3px 3px 0 var(--ink);
}
.booth__awn::after {                      /* фестони — the scalloped lip */
  content: '';
  position: absolute;
  left: 6px; right: 6px; bottom: -12px; height: 12px;
  background-image: radial-gradient(circle 11px at 11px 0, var(--ink) 10px, transparent 10.8px);
  background-size: 22px 12px;
  background-repeat: repeat-x;
}
/* Two columns: the barker on the left, the poster nailed up beside him. Both
   fr — a fixed art track starves the text column into a gutter on the way
   down to the 560px breakpoint, and that is where this section spends most
   of its life. */
/* center, not start: the poster runs ~90px taller than the three lines beside
   it, and top-aligning them hangs the text off the ceiling of its own half. */
.booth__body {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--s5);
  align-items: center;
  padding: calc(var(--s5) + 10px) var(--s5) var(--s6);
}
/* --s6 at the foot, not --s4: the balloon is last in this column and its tail
   hangs 21px below its own box. At --s4 the tail lands on the counter rule. */
.booth__say {
  display: grid;
  gap: var(--s3);
  justify-items: start;
  align-content: start;
}
/* z-index, because the title's ::after covers the whole booth on purpose and
   the button underneath still has to behave like a button. Both lead to the
   same page, so this buys hover and focus, not a second destination. */
.booth__counter {
  position: relative;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) var(--s5);
  border-top: var(--ink-w) solid var(--ink);
  background: var(--ink-07);
  border-radius: 0 0 3px 3px;
}

/* ---- Art in its OWN frame — the other half of the .artfill idea, for when
   the text stands under the picture on paper instead of on a scrim over it.
   No background on the base class: games-sections.html re-declares .artbox
   with the `background` SHORTHAND and its own crop, and the shorthand would
   reset whatever was put here anyway. ---- */
.artbox {
  position: relative;
  width: 100%;
  border: var(--ink-w) solid var(--ink);
  border-radius: 5px;
  overflow: hidden;
}
.artbox--poster {
  aspect-ratio: 3 / 2;
  border-radius: 3px;
  box-shadow: 4px 4px 0 var(--ink);
  transform: rotate(-1.6deg);
}
/* The lead's own cover file, one crop higher: 24% rather than 26%, because a
   3:2 window is shallower than the lead's 1.15 one and the sweat drops are
   the first thing to leave frame. */
.artbox--catch { background: url(assets/bus-catch-cover.webp) 50% 24% / cover no-repeat; }
.artbox__mult { position: absolute; right: 9px; bottom: 9px; font-size: 1.35rem; }

/* .game-mult at shelf size, and it survives for the same reason: paper fill
   inside an ink stroke brackets the whole luminance axis, so the number holds
   against any frame of the art it happens to land on. */
.mult-mini {
  font-family: var(--face-display);
  font-weight: 900;
  line-height: 1;
  color: var(--paper);
  -webkit-text-stroke: 4px var(--ink);
  paint-order: stroke fill;
  text-shadow: 4px 5px 0 var(--ink);
  display: inline-block;
  transform: rotate(-3deg);
}

/* ============================================================
   6. BUTTONS
   ============================================================ */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: var(--tap);
  padding: 0 var(--s5);
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' 40, 'BNCE' 8;
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: 0.02em;
  color: var(--ink);
  text-decoration: none;
  background: none;
  border: 0;
  cursor: pointer;
  align-self: flex-start;
}
.btn > .plate { border-radius: 999px; background: var(--amber); box-shadow: 5px 5px 0 var(--ink); }
.btn:active > .plate { transform: translate(3px, 3px); box-shadow: 2px 2px 0 var(--ink); }
.btn__label { position: relative; z-index: 1; }
.btn--ghost > .plate { background: var(--paper); }

/* ============================================================
   7. DIVIDERS — torn paper, never a hairline rule
   ============================================================ */
.tear {
  display: block;
  width: 100%;
  height: 22px;
  color: var(--ink);
  margin: var(--s6) 0 0;
}
.tear svg { width: 100%; height: 100%; display: block; }

/* The marquee carries the HOUSE CIPHER, not the wordmark. A scrolling
   wordmark is a ticker, and a ticker is what a coin has instead of a
   publication. This is his monogram, escaped onto the page.               */
.marquee {
  position: relative;
  overflow: hidden;
  border-top: var(--ink-w) solid var(--ink);
  border-bottom: var(--ink-w) solid var(--ink);
  background: var(--ink);
  padding: var(--s2) 0;
  margin-bottom: var(--s7);
}
.marquee__track {
  display: flex;
  width: max-content;
  animation: marquee 42s linear infinite;
}
.marquee__group { display: flex; align-items: center; gap: var(--s5); padding-right: var(--s5); }
.marquee__group svg { width: 26px; height: 26px; flex: none; color: var(--amber); }
@keyframes marquee { to { transform: translateX(-50%); } }

/* ============================================================
   8. THE FROG — the signature
   ============================================================
   He is the only element not contained by a border. The gutter is where the
   paper shows through; he stands in the gutter and overlaps the panels on
   both sides. He is standing ON the paper, because the gutter IS the paper.
   Depth is not decoration — depth is authorship.                          */
/* He lives at an EDGE, never between two things.
   The plan rejected the double-page spread on exactly one argument — "the frog
   on the centrefold is a wall between the two things that must be seen
   together" — and then put him in the middle of tier 1, which is the same
   mistake in a different layout. The margin is paper too: standing in it is
   still standing on the page, and it costs the reader nothing. */
.frog {
  position: absolute;
  z-index: 6;                       /* nearest plane. above every panel.   */
  display: block;
  text-decoration: none;
  /* No aura. §10: he stands on paper, UNHALOED — a soft airbrushed shadow was
     the one blur on a site whose whole grammar is hard ink. Grounding is the
     drawn ellipse under his soles (below), not an atmosphere around him. */
}
/* frog-host (empty-4) — arms wide, 1.043, and the plate is already cropped at
   its own left and bottom (margins L=0 B=0). So the source's crop becomes the
   page's crop: he hangs off the bottom-left and his open right palm is the
   only hand we see, gesturing into the content. */
.frog--host {
  /* Tuned against the plate's own anatomy: at 372px his head and body sit in
     the margin and only his OPEN PALM crosses into the lead panel — which is
     the refs' one real lesson, "he physically holds what he presents". Any
     bigger and he eats the headline, which was the whole complaint. */
  left: var(--frog-left, calc(-1 * var(--bleed) - 46px));
  bottom: var(--frog-bottom, -22px);
  /* His width tracks the margin, so he SPANS it: cropped by the screen at one
     end, palm crossing the panel border at the other, with a constant ~78px
     overlap at any viewport. Anchoring him to the edge without this just moved
     the void from behind him to in front of him. */
  width: var(--frog-w, clamp(240px, calc(var(--bleed) + 250px), 560px));
  aspect-ratio: 953 / 868;
}
.frog img { width: 100%; height: auto; pointer-events: none; }
.frog__plane { display: block; position: relative; will-change: transform; }
/* REMOVED — the one accessory: he used to lift and tilt on hover. §7 of the
   plan cuts hover-lift by name ("not a device — it's a Material shadow"), and
   then I gave one to the signature element. He already answers you when you
   hover: his balloon speaks and his tail moves. Making him bob as well was the
   design admiring itself. */

/* his balloon, live. It sits above his head in the gutter and overhangs the
   panels either side — he breaks borders, so his voice may too. */
/* His balloon sits ABOVE and BESIDE his head — never over his face. It is a
   real balloon: boiled ink outline, organic corners, and a live tail. */
.frog-say {
  position: absolute;
  z-index: 7;
  pointer-events: none;
  width: clamp(178px, 20vw, 240px);
}
/* ABOVE his head, not on it. When the balloon sat over his face the tail was a
   stub buried inside his silhouette — which is exactly why it read as a box. */
/* Measured off HIS left edge and HIS width, so it clears his head and never
   covers the cipher on his chest — which is the best thing on this plate. */
.frog-say--host {
  left: var(--say-left, calc(-1 * var(--bleed) - 46px + var(--frog-w) * 0.54));
  bottom: var(--say-bottom, calc(var(--frog-w) * 0.50));
}
.frog-say .balloon { width: 100%; max-width: none; margin: 0; }
.frog-say .balloon::after { display: none; }   /* the live SVG tail replaces it */
.balloon--live { isolation: isolate; background: none; border: 0; padding: var(--s4) var(--s5); }
.balloon--live > .plate {
  border-radius: var(--radius-balloon);
  box-shadow: 5px 5px 0 var(--ink);
}
.balloon__t { position: relative; z-index: 1; display: block; }

.tier--1 { min-height: 420px; }
.tail-layer {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  overflow: visible;
}
.tail-layer path {
  fill: var(--paper);
  stroke: var(--ink);
  stroke-width: 3;
  stroke-linejoin: round;
  transition: opacity .18s var(--ease);
}

/* ============================================================
   9. THE READING COLUMN — paper with ink. Always. It never moves.
   ============================================================
   Not a preference: ink on a sky panel is 4.49:1 and paper-white on it is
   4.45:1 — it is the perfectly ambiguous midtone, so a coloured panel cannot
   hold body text in either direction. The maths chose this, not me.        */
.article { max-width: var(--page-max); margin: 0 auto; padding: 0 var(--page-pad); }
.column {
  max-width: var(--measure);
  margin: 0 auto;
  font-family: var(--face-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  color: var(--ink);
}
/* One rule owns vertical rhythm, and the reset above it must not out-specify
   that rule. `.column p` did: it is (0,1,1) against the flow rule's (0,1,0),
   so `margin: 0` won on every paragraph and EVERY post shipped with its body
   copy set solid — no space between paragraphs anywhere on the site. Scoping
   the reset to `> *` puts both rules at (0,1,0), so source order decides and
   the flow rule wins, which is what it was always for. Anything that wants
   its own spacing (h2, .caption-block, .balloon, .quote) still out-ranks it. */
.column > * { margin: 0; }
.column > * + * { margin-top: 1.15em; }
.column h2 {
  font-family: var(--face-display);
  font-weight: 900;
  font-size: var(--t-h2);
  line-height: var(--lh-tight);
  margin: 1.9em 0 0;
  letter-spacing: -0.01em;
}
.column strong { font-weight: 800; }
.column a { color: var(--sky-ink); text-underline-offset: 3px; }

/* the beat number — a comic's panel number */
.beat {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  margin-right: var(--s2);
  border: 2px solid var(--ink);
  border-radius: 50%;
  font-family: var(--face-util);
  font-size: var(--t-micro);
  font-weight: 700;
  vertical-align: 2px;
}
.drop-cap::first-letter {
  float: left;
  font-family: var(--face-display);
  font-size: 3.9em;
  line-height: 0.82;
  padding: 0.06em 0.08em 0 0;
  color: var(--ink);
}

/* Panels inside the article bleed wider than the column. */
.column-wide {
  max-width: calc(var(--measure) + 14vw);
  margin-inline: auto;
}

/* pull-quote = his voice = a balloon */
.column .balloon { max-width: none; margin-block: 1.6em; font-size: 1.15rem; }

/* caption box in the article = what he knows */
.column .caption-block {
  position: relative;
  padding: var(--s4) var(--s5);
  border: var(--ink-w) solid var(--ink);
  background: var(--paper);
  box-shadow: var(--drop) var(--drop) 0 var(--ink);
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' var(--infm-know), 'BNCE' 0;
  font-weight: 700;
  font-size: 1.15rem;
  margin-block: 1.8em;
}

/* ============================================================
   10. SFX — onomatopoeia. The ONLY place a radial burst survives.
   ============================================================
   A burst behind an IMPACT is a comics device. A burst behind a MASCOT is a
   memecoin halo — see PLAN §10. So: never behind the frog.                 */
.sfx {
  position: relative;
  display: grid;
  place-items: center;
  margin-block: 2em;
  min-height: 190px;
  isolation: isolate;
}
/* Sized and centred rather than inset negatively — a negative inset pushed it
   past the column and gave the whole page a horizontal scrollbar on a phone. */
.sfx__burst {
  position: absolute;
  width: min(100%, 430px);
  aspect-ratio: 1;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 0;
  color: var(--amber);
}
.sfx__word {
  position: relative;
  z-index: 1;
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' 92, 'BNCE' 60;
  font-weight: 800;
  font-size: clamp(3rem, 11vw, 6.5rem);
  line-height: 1;
  color: var(--paper);
  letter-spacing: -0.02em;
  -webkit-text-stroke: 7px var(--ink);
  paint-order: stroke fill;
  transform: rotate(-6deg);
  text-shadow: 8px 9px 0 var(--ink);
}

/* ============================================================
   11. STICKERS — quoted objects, stuck onto the drawn world
   ============================================================
   Physical objects on the page: die-cut white border, a few degrees of
   rotation, a real drop shadow, sitting above the panel. They quote OBJECTS,
   never people — see PLAN §9.1.                                            */
.sticker {
  position: relative;
  display: block;
  width: 190px;
  padding: 9px;
  background: #fff;
  border-radius: 7px;
  box-shadow: 0 10px 18px rgba(11, 9, 7, 0.28), 0 2px 4px rgba(11, 9, 7, 0.2);
  transform: rotate(var(--tilt, -4deg));
  z-index: 4;
}
.sticker__in {
  border-radius: 3px;
  overflow: hidden;
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  background: var(--ink-07);
  text-align: center;
  padding: var(--s3);
}
.sticker__say {
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' 74, 'BNCE' 30;
  font-weight: 800;
  font-size: 1.5rem;
  color: var(--ink);
  line-height: 1.15;
}
.sticker__cap {
  font-family: var(--face-util);
  font-size: 10px;
  color: var(--ink-40);
  text-align: center;
  margin-top: 6px;
  letter-spacing: 0.04em;
}
/* Desktop: stickers live in the margin, never inside the measure — they are
   interruptions from reality, not part of the reading. Floated (not absolute)
   so each one sits at its own beat rather than stacking at the top. */
.column-holder { position: relative; }
.sticker--margin { float: right; width: 200px; margin: 0 -232px var(--s5) var(--s5); }

/* ============================================================
   11b. QUOTED REALITY — other people's words, other people's pictures
   ============================================================
   The sticker above quotes an OBJECT. Everything here quotes a PERSON, which
   is a different act and needs its own grammar. Two laws, both borrowed from
   §12 because they do the same job: these surfaces are FLAT and STILL — no
   boil, no parallax, no wobble — and not one of them ships without a visible
   source. The stillness is how the page says "this part isn't mine", exactly
   as the ad's stillness says "this part isn't the story".
   And nothing here is a balloon. A balloon is the frog's voice; none of
   these words are his.                                                     */
.quote {
  margin: 1.9em 0;
  padding-left: var(--s5);
  border-left: var(--ink-w) solid var(--ink);
}
.quote__t {
  margin: 0;
  font-family: var(--face-display);
  font-weight: 900;
  font-size: clamp(1.2rem, 2.2vw, 1.55rem);
  line-height: 1.26;
  letter-spacing: -0.01em;
}
.quote__src {
  display: block;
  margin-top: var(--s3);
  font-family: var(--face-util);
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--ink-70);
}

/* A video here is a CITATION that happens to be playable, so it is shaped
   like a citation: the words at the size the reader came for, the screen only
   once they ask. The first cut of this was a 16:9 facade with a play badge on
   black — right as a player, wrong as a page. Two of them put ~850px of void
   through the middle of an essay, and the only dark surfaces this site allows
   itself are the den and the marquee.
   It is an <a> to YouTube, not a <button>: with JS the click is intercepted
   and the iframe takes its place; without JS it simply goes to the video. So
   the no-JS path needs no duplicate link, and nothing loads from a third
   party until the reader has actually decided to watch — the rest of the site
   opens straight off the filesystem with nobody else in it.
   clear:both is not cosmetic: the «я не піду» sticker floats into the right
   margin a few paragraphs above and would otherwise shoulder the first card
   sideways.                                                                 */
.vid { margin: var(--s6) auto; max-width: var(--measure); clear: both; }
.vid__btn {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s4) var(--s5);
  background: var(--paper);
  border: var(--ink-w) solid var(--ink);
  border-radius: var(--radius-panel);
  box-shadow: var(--drop) var(--drop) 0 var(--ink);
  color: var(--ink);
  text-decoration: none;
  transition: transform .16s var(--ease), box-shadow .16s var(--ease);
}
.vid__btn:hover {
  transform: translate(-2px, -2px);
  box-shadow: calc(var(--drop) + 2px) calc(var(--drop) + 2px) 0 var(--ink);
}
.vid__btn:active { transform: translate(3px, 3px); box-shadow: 2px 2px 0 var(--ink); }
.vid__play {
  flex: none;
  display: grid;
  place-items: center;
  width: clamp(46px, 5.4vw, 58px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--amber);            /* ink on amber is 8.02:1 */
  border: var(--ink-w) solid var(--ink);
}
.vid__play svg { width: 38%; height: 38%; margin-left: 9%; color: var(--ink); }
/* display:block matters twice: as a flex item it is blockified anyway, but
   after the swap it is re-parented into a plain div and would go inline. */
.vid__cap { display: block; min-width: 0; }
.vid__q {
  display: block;
  font-family: var(--face-display);
  font-weight: 900;
  font-size: clamp(1rem, 1.6vw, 1.22rem);
  line-height: 1.3;
  letter-spacing: -0.01em;
}
.vid__src {
  display: block;
  margin-top: var(--s2);
  font-family: var(--face-util);
  font-size: var(--t-small);
  color: var(--ink-70);
}
/* Once the iframe is in, the same caption node is re-parented beneath it —
   the quote outlives the click rather than being swallowed by the player. */
.vid__cap--after { margin-top: var(--s4); }
.vid__frame {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: var(--ink-w) solid var(--ink);
  border-radius: var(--radius-panel);
  box-shadow: var(--drop) var(--drop) 0 var(--ink);
}

/* A photograph pinned to the page — the sticker's physics at full width.
   White pad, a degree of tilt, a real shadow. The caption sits OUTSIDE the
   tilt so it stays typeset rather than drawn, and it is not optional: the
   caption is the entire difference between quoting a picture and claiming
   one.                                                                     */
.still { margin: var(--s7) auto; width: fit-content; max-width: min(100%, 880px); clear: both; }
.still__pad {
  padding: 10px;
  background: #fff;
  border-radius: 6px;
  box-shadow: 0 12px 22px rgba(11, 9, 7, 0.26), 0 2px 5px rgba(11, 9, 7, 0.2);
  transform: rotate(var(--tilt, -1.1deg));
}
.still__pad img { border-radius: 2px; }
/* --t-small/--ink-70, not micro/40: this caption is doing legal work, not
   decorative work — it is the line that says the picture is a quotation and
   names whose watermark is on it. It has to be readable at a glance. */
.still__cap {
  margin: var(--s5) 0 0;
  max-width: 62ch;
  font-family: var(--face-util);
  font-size: var(--t-small);
  line-height: 1.6;
  color: var(--ink-70);
}
.still__cap strong { color: var(--ink); }

/* The editorial note. Flat, still, and quiet — it belongs to §12's family,
   not to the story. */
.notice {
  max-width: var(--measure);
  margin: var(--s7) auto 0;
  padding: var(--s4) var(--s5);
  border: 2px solid var(--ink-15);
  border-left: var(--ink-w) solid var(--ink);
  font-family: var(--face-util);
  font-size: var(--t-small);
  line-height: 1.62;
  color: var(--ink-70);
}
.notice > * { margin: 0; }
.notice > * + * { margin-top: var(--s3); }
.notice strong { color: var(--ink); }

/* Two panels at the foot of an article. Unequal, because §5's law does not
   stop applying just because there are only two of them. */
.pair {
  display: grid;
  gap: var(--gutter);
  grid-template-columns: minmax(0, 1.18fr) minmax(0, 0.92fr);
  max-width: calc(var(--measure) + 14vw);
  margin: 0 auto;
}

/* ============================================================
   12. COMPLIANCE — the only surfaces that never boil, parallax or wobble
   ============================================================
   Everything else here is hand-drawn and alive. These are flat, still and
   legible. The stillness is what marks them as the parts that aren't kidding. */
.ad {
  max-width: var(--page-max);
  margin: var(--s9) auto var(--s7);
  padding: 0 var(--page-pad);
}
.ad__inner {
  position: relative;
  border: 4px double var(--ink);
  background: var(--paper);
  padding: var(--s7) var(--s5);
  text-align: center;
  display: grid;
  gap: var(--s4);
  justify-items: center;
}
.ad__mark {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -50%);
  background: var(--paper);
  padding: 0 var(--s3);
  font-family: var(--face-util);
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--ink);
}
.ad__title { font-family: var(--face-display); font-weight: 900; font-size: var(--t-h2); margin: 0; }
.ad__body { max-width: 54ch; margin: 0; color: var(--ink-70); font-size: 1rem; }

.rg {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--s6) var(--page-pad);
  border-top: 2px solid var(--ink-15);
}
.rg__inner {
  display: grid;
  gap: var(--s3);
  max-width: 74ch;
  font-family: var(--face-util);
  font-size: var(--t-small);
  color: var(--ink-70);
  line-height: 1.6;
}
.rg h2 { font-family: var(--face-util); font-size: 1rem; font-weight: 700; color: var(--ink); margin: 0; }
.rg a { color: var(--sky-ink); }

/* The indicia. Comics have always carried "all characters are fictitious" —
   word for word the fiction disclaimer this site needs.                     */
.indicia {
  border-top: 2px solid var(--ink-15);
  background: var(--paper);
}
.indicia__inner {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--s6) var(--page-pad) var(--s8);
  font-family: var(--face-util);
  font-size: var(--t-micro);
  line-height: 1.72;
  color: var(--ink-70);
  columns: 2;
  column-gap: var(--s7);
}
.indicia__inner p { margin: 0 0 var(--s3); break-inside: avoid; }
.indicia strong { color: var(--ink); }
.indicia a { color: var(--sky-ink); }

/* ============================================================
   12b. CONTACTS — the author's channels, as gamerail rows
   ============================================================
   The row is the games showcase's `.gamerail__row`, ported here from
   games-sections.html so the homepage can wear the same pattern: a 44px icon
   plate, a two-line label, and the boil on the plate on hover. An icon-plus-
   label pill is the same object whether it points at a game or at a mailbox. */
.gamerail__row {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: var(--s3);
  align-items: center;
  padding: var(--s3) var(--s4);
  color: inherit;
  text-decoration: none;
}
.gamerail__row > .plate { border-width: 2px; box-shadow: 4px 4px 0 var(--ink); }
.gamerail__row > svg { width: 44px; height: 44px; color: var(--ink); position: relative; z-index: 1; }
.gamerail__name { position: relative; z-index: 1; min-width: 0; }
.gamerail__name b {
  display: block;
  font-family: var(--face-display);
  font-weight: 900;
  font-size: 1.02rem;
  line-height: 1.1;
}
/* overflow-wrap: the mailbox address is longer than any game name and must
   wrap inside its pill rather than push the grid wide. */
.gamerail__name span {
  display: block;
  font-family: var(--face-util);
  font-size: var(--t-micro);
  color: var(--ink-70);
  overflow-wrap: anywhere;
}

.contacts {
  max-width: 880px;
  margin: var(--s7) auto var(--s6);
  padding: 0 var(--page-pad);
}
.contacts__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s3) var(--s4);
  margin-bottom: var(--s5);
}
.contacts__h {
  font-family: var(--face-display);
  font-weight: 900;
  font-size: var(--t-h2);
  line-height: var(--lh-tight);
  margin: 0;
}
.contacts__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--s4);
}
/* one uniform height for all six, so a two-line address (it wraps at the <wbr>
   before the @) doesn't make its row taller than the others. */
.contacts__grid .gamerail__row { min-height: 82px; }

/* --- ONE FOOTER — three columns over a thin credits line ------------------
   Scoped to `.site-footer` so the paper pages' standalone `.rg`/`.indicia`
   (post/about) keep their own layout untouched. The colophon used to be a slab
   of fine print under a dividing rule, which read as a SECOND footer; its
   disclaimers are now the MIDDLE column and only the fonts/issue line sits on a
   hairline baseline — so the whole thing reads as one footer: responsible
   gaming · about · connect, over a credits strip. */
.site-footer {
  max-width: var(--page-max);
  margin: var(--s8) auto 0;
  padding: 0 var(--page-pad) var(--s6);
  border-top: 2px solid var(--ink-15);
}
.film-ground .site-footer { border-top-color: rgba(233, 196, 106, 0.22); }
.foot-band {
  display: grid;
  /* the responsible-gaming note is short and the disclaimers are long, so the
     first column is narrower (runs taller) and the middle wider (runs shorter)
     — the three columns then finish closer to level. The connect column stays
     wide enough that the mail address wraps at its <wbr> (before the @), not
     mid-word. */
  grid-template-columns: minmax(0, 0.88fr) minmax(0, 1.05fr) minmax(0, 1.3fr);
  gap: var(--s6) clamp(var(--s6), 4vw, var(--s8));
  padding: var(--s7) 0;
  align-items: start;
}
/* every column drops its own page framing — the band owns the column */
.site-footer .rg,
.site-footer .contacts,
.site-footer .foot-about {
  max-width: none;
  margin: 0;
  padding: 0;
  border-top: 0;
}
.site-footer .rg__inner { max-width: none; }
/* the about column — the disclaimers, in the utility face of the old colophon */
.foot-about {
  display: grid;
  gap: var(--s3);
  align-content: start;
  font-family: var(--face-util);
  font-size: var(--t-small);
  line-height: 1.6;
  color: var(--ink-70);
}
.foot-about p { margin: 0; }
.foot-about strong { color: var(--ink); }
/* matched column headers on all three columns */
.site-footer .rg h2,
.site-footer .foot-h,
.site-footer .contacts__h {
  font-family: var(--face-display);
  font-weight: 900;
  font-size: 1.35rem;
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
  margin: 0;
}
.site-footer .contacts__head { margin-bottom: var(--s5); }
/* two columns of pills within the connect column */
.site-footer .contacts__grid { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
/* the baseline — brand + font credits as one dim hairline row: a credits strip,
   not a second footer */
.foot-base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--s2) var(--s6);
  padding-top: var(--s5);
  border-top: 1px solid var(--ink-15);
  font-family: var(--face-util);
  font-size: var(--t-micro);
  line-height: 1.6;
  color: var(--ink-40);
}
.foot-base p { margin: 0; }
.foot-base strong { color: var(--ink-70); }
.foot-base__fonts { max-width: 62ch; }

/* film: relight the new footer type off the dark ground */
.film-ground .site-footer .foot-h { color: var(--cream); }
.film-ground .foot-about { color: rgba(244, 234, 212, 0.7); }
.film-ground .foot-about strong { color: var(--cream); }
.film-ground .foot-base { color: rgba(244, 234, 212, 0.44); border-top-color: rgba(233, 196, 106, 0.16); }
.film-ground .foot-base strong { color: rgba(244, 234, 212, 0.72); }

/* below the leads' breakpoint the columns stack and the baseline stacks too */
@media (max-width: 900px) {
  .foot-band { grid-template-columns: minmax(0, 1fr); gap: var(--s7); }
  .foot-base { gap: var(--s2); }
}

/* ============================================================
   13. THE DEN — games.html. Night. The only violet on the site.
   ============================================================ */
.den {
  position: relative;
  background: var(--violet-sh);
  color: #F2ECE4;
  overflow: hidden;
  isolation: isolate;
}
.den__plates { position: absolute; inset: 0; z-index: 0; }
.den__plate { position: absolute; inset: 0; background-size: cover; background-position: center; will-change: transform; }
.den__inner { position: relative; z-index: 2; }

/* ============================================================
   13b. THE FILM GROUND — the cover and the author pages lie on a dark film table
   ============================================================
   Borrowed wholesale from the sibling project's «Думка автора» hero: the page
   substrate stops being screened white and becomes near-black warm film with
   one amber bloom near the top — the studio-ident look. This is NOT the den
   (that inverts --ink to cream and takes light type everywhere). Here the
   PANELS stay paper: a comic page still printed on white stock, only now the
   stock is lying on a dark table instead of more paper. So --paper is left
   alone and only the bare-substrate surfaces — the header, the gutters, the
   two compliance strips that carried no paper of their own — are re-dressed.
   Opt-in, like every dark surface on this site: it rides `body.film-ground`,
   so the shared stylesheet still serves the paper pages unchanged.
   The dark film kills the hard offset shadows (near-black on near-black), but
   nothing is lost: a WHITE panel on a dark ground separates by its own
   luminance, which is exactly what the drop shadow bought on white paper. */
body.film-ground {
  --film:    #0C0B06;
  --film-2:  #17140C;
  --cream:   #F4EAD4;
  --gold-lite: #E9C46A;    /* the ident gold — lighter than tokens' --gold */
  --gold-lite-hi: #FFDC8A;
  background:
    radial-gradient(84% 66% at 32% -4%, rgba(240, 172, 44, 0.16), transparent 60%),
    linear-gradient(180deg, #17140C 0%, #0C0B06 46%, #0A0904 100%);
  background-color: #0C0B06;
  background-attachment: fixed;
}
/* the CMYK dot screen is white's texture; on film it multiplies to nothing.
   Retire it here and lay a film grain over the whole viewport instead. */
.film-ground .paper-screen { display: none; }
.film-ground::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.13;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* corner registration marks: dim gold instead of ink so they still read */
.film-ground .reg-mark { color: rgba(233, 196, 106, 0.36); }

/* --- the header becomes the ident band. It is transparent, so it already
   sits on the film; only its type has to move off ink. The nav pill is a
   paper plate and needs nothing — a white pill on film reads fine. --- */
.film-ground .masthead__sub { color: rgba(244, 234, 212, 0.72); }
.film-ground .masthead__sub a { color: var(--gold-lite) !important; }

/* --- the logo: the sibling project's glowing, UNCROPPED ident, at the small
   size the badge held. The light lives ON the artwork, never around it — the
   earlier build floated a blurred amber bloom behind the emblem and swept the
   shine through an ELLIPSE, so both lit the transparent die-cut area and read
   as an aura in the box. Now there is no bloom, and the shine is a copy of the
   emblem itself: an <img> carries its own die-cut alpha, so the gleam is
   clipped to the medallion for free and stops exactly where the picture does.
   Two <img>s of the same file, stacked and isolated. --- */
.masthead__logo {
  position: relative;
  flex: none;
  width: clamp(104px, 12vw, 164px);   /* emblem is 1.65:1 → ~63–99px tall */
  isolation: isolate;                 /* contain the screen-blend to the group */
}
.masthead__emblem {
  display: block;
  width: 100%;
  height: auto;
  position: relative;
  z-index: 1;
  /* a DARK drop-shadow only — it follows the die-cut alpha and lifts the logo
     off the film with depth, not light. The gentle brightness pulse is the
     "glow": it rides the emblem's own pixels, so it too stays inside the art. */
  filter: drop-shadow(0 5px 13px rgba(0, 0, 0, 0.55)) brightness(1);
  animation: mast-breathe 7s ease-in-out infinite;
}
/* The travelling gleam. It is the emblem again, brightened to a highlight and
   narrowed to a diagonal band by a moving linear-gradient MASK. The band is the
   only thing the gradient controls; the SHAPE is the image's own alpha, so the
   light can never reach the film. `screen` adds the highlight to the gold
   rather than greying it. No local-image fetch anywhere, so the file:// CORS
   block on mask images never applies — the mask is a gradient, the alpha is
   intrinsic to the <img>. */
.masthead__shine {
  position: absolute;
  inset: 0;
  width: 100%;
  height: auto;
  z-index: 2;
  pointer-events: none;
  filter: brightness(2.1) contrast(1.05) saturate(0.7);
  mix-blend-mode: screen;
  -webkit-mask: linear-gradient(104deg, transparent 42%, #000 50%, transparent 58%);
          mask: linear-gradient(104deg, transparent 42%, #000 50%, transparent 58%);
  -webkit-mask-size: 260% 100%;
          mask-size: 260% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: 150% 0;
          mask-position: 150% 0;
  animation: mast-shine 6.6s cubic-bezier(0.45, 0, 0.5, 1) infinite;
}
/* the "glow" — a gentle brightness breathe on the gold, clipped to the art by
   definition (it is a filter on the emblem's own pixels) */
@keyframes mast-breathe {
  0%, 100% { filter: drop-shadow(0 5px 13px rgba(0, 0, 0, 0.55)) brightness(0.96); }
  50%      { filter: drop-shadow(0 5px 13px rgba(0, 0, 0, 0.55)) brightness(1.12); }
}
/* the gleam sweeps left→right and rests off-frame the rest of the cycle */
@keyframes mast-shine {
  0%, 55%   { -webkit-mask-position: 150% 0; mask-position: 150% 0; }
  90%, 100% { -webkit-mask-position: -60% 0; mask-position: -60% 0; }
}

/* --- bare-substrate type elsewhere on the page --- */
/* the torn divider is now a torn PAPER edge on the dark table, so it takes a
   paper tone rather than ink */
.film-ground .tear { color: #E8E2D4; }
/* a gold hairline gives the marquee band an edge against the film it would
   otherwise melt into */
.film-ground .marquee { border-color: rgba(233, 196, 106, 0.30); }
.film-ground .shelf__h { color: var(--cream); }
/* .shelf__head .caption is a paper box with its own ink — unchanged.
   .shelf__foot and .masthead__sub carry inline colours in the markup, so the
   override has to out-weight an inline style: hence !important, the one place
   on this page it is load-bearing rather than lazy. */
.film-ground .shelf__foot { color: rgba(244, 234, 212, 0.5) !important; }
/* Responsible-gaming strip: ink → cream on the film, and its gold top hairline
   opens the footer zone below the ad. */
.film-ground .rg { border-top-color: rgba(233, 196, 106, 0.22); }
.film-ground .rg__inner { color: rgba(244, 234, 212, 0.74); }
.film-ground .rg h2 { color: var(--cream); }
.film-ground .rg a { color: var(--gold-lite); }

/* --- THE LIFT — cards read as objects on the dark table -------------------
   The signature hard offset shadow is ink-on-ink here, i.e. invisible on the
   near-black film. It returns in the ident's GOLD: same hard edge, no blur,
   same throw, so the "sticker lifting off the page" grammar survives the move
   to the dark ground and every surface catches the same warm light.
   The two feature panels are dark ART, not white cards — their ink border
   dissolves into the film and the dark edges with it, so they also take a warm
   keyline to draw all four sides. The booth and the ad are white-filled: their
   own luminance is the edge, so they need the lift only, not the keyline. */
.film-ground .panel--feature > .plate {
  border-color: rgba(233, 196, 106, 0.55);
  box-shadow: var(--drop) var(--drop) 0 rgba(233, 196, 106, 0.15);
}
.film-ground .booth,
.film-ground .ad__inner {
  box-shadow: var(--drop) var(--drop) 0 rgba(233, 196, 106, 0.15);
}
/* The awning's ink stripes are #0B0907 on a #0C0B06 film — they vanish, and the
   stall reads as a row of floating white bars with no top edge. On the film the
   dark stripe warms to a charcoal that clears the ground and the light stripe to
   cream (the awning is chrome, so it warms like the tear and the marquee; the
   booth BODY is paper and stays white). A gold keyline draws the top edge, and
   the festoon lip takes the same charcoal so it belongs to the awning. */
.film-ground .booth__awn {
  background: repeating-linear-gradient(90deg, #2A2216 0 22px, var(--cream) 22px 44px);
  border-color: rgba(233, 196, 106, 0.55);
  box-shadow: 3px 3px 0 rgba(233, 196, 106, 0.15);
}
.film-ground .booth__awn::after {
  background-image: radial-gradient(circle 11px at 11px 0, #2A2216 10px, transparent 10.8px);
}
/* the contact pills are white-filled plates like the booth — the heading takes
   cream off the film, the plates take the same warm lift. */
.film-ground .contacts__h { color: var(--cream); }
.film-ground .gamerail__row > .plate { box-shadow: 4px 4px 0 rgba(233, 196, 106, 0.15); }

/* --- THE COLOPHON recedes into the dark -----------------------------------
   White ad → dark responsible-gaming → white indicia was a bright-dark-bright
   sandwich that made the legalese shout. Now the ad is the ONE bright card —
   the "this part is real" break — and the fine print settles into the same
   quiet dark footer as the responsible-gaming strip, split from it by one gold
   hairline. Its ink text is relit to cream; the font-credits line carries an
   inline --ink-40, so that one override needs !important to get past it. */
.film-ground .indicia {
  background: transparent;
  border-top: 2px solid rgba(233, 196, 106, 0.22);
}
.film-ground .indicia__inner { color: rgba(244, 234, 212, 0.66); }
.film-ground .indicia strong { color: var(--cream); }
.film-ground .indicia a { color: var(--gold-lite); }
.film-ground .indicia__inner p:last-child { color: rgba(244, 234, 212, 0.52) !important; }

/* --- THE READING PAGES on the film ----------------------------------------
   bus-catch-post.html and about.html lie on the same table as the cover. Their columns
   are bare-substrate type, and this section's rule for bare-substrate type is
   re-dress, not box-in: the essay's ink relights to cream exactly as the
   colophon's did. §9 does not object — its law banned body text on MIDTONES,
   and the film is not a midtone: cream on it clears 14:1.
   Two metals, one job each. CREAM is relit ink — running text, hairline
   rules, the torn edge. GOLD is the drawn keyline — accent edges, rings,
   links — at the same 0.55 / 0.22 / 0.15 steps the panels and the colophon
   already use. Paper boxes inside the column (captions, balloons, stickers,
   the video card) keep their own ink and need nothing. */
.film-ground .column { color: rgba(244, 234, 212, 0.92); }
.film-ground .column h2,
.film-ground .column strong { color: var(--cream); }
.film-ground .column a { color: var(--gold-lite); }
.film-ground .drop-cap::first-letter { color: var(--gold-lite); }
.film-ground .beat { border-color: rgba(233, 196, 106, 0.55); }
.film-ground .quote { border-left-color: rgba(233, 196, 106, 0.55); }
.film-ground .quote__src { color: rgba(244, 234, 212, 0.6); }
.film-ground .still__cap { color: rgba(244, 234, 212, 0.6); }
.film-ground .still__cap strong { color: var(--cream); }
.film-ground .sticker__cap { color: rgba(244, 234, 212, 0.5); }
/* the editorial note: quiet cream hairlines, and the one accent edge it has
   always had joins the keylines */
.film-ground .notice {
  border-color: rgba(244, 234, 212, 0.18);
  border-left-color: rgba(233, 196, 106, 0.55);
  color: rgba(244, 234, 212, 0.66);
}
.film-ground .notice strong { color: var(--cream); }
/* The video citation is a paper card like the nav pill, so it takes only the
   lift. Its caption is the exception: the iframe swap re-parents it onto bare
   film, so the quote that outlives the player has to be relit to survive it. */
.film-ground .vid__btn { box-shadow: var(--drop) var(--drop) 0 rgba(233, 196, 106, 0.15); }
.film-ground .vid__btn:hover { box-shadow: calc(var(--drop) + 2px) calc(var(--drop) + 2px) 0 rgba(233, 196, 106, 0.22); }
.film-ground .vid__btn:active { box-shadow: 2px 2px 0 rgba(233, 196, 106, 0.15); }
.film-ground .vid__frame {
  border-color: rgba(233, 196, 106, 0.55);
  box-shadow: var(--drop) var(--drop) 0 rgba(233, 196, 106, 0.15);
}
.film-ground .vid__cap--after { color: var(--cream); }
.film-ground .vid__cap--after .vid__src { color: rgba(244, 234, 212, 0.6); }
/* white plates separate by their own luminance (the booth's argument above),
   so like the booth they take the lift and no keyline */
.film-ground .panel > .plate { box-shadow: var(--drop) var(--drop) 0 rgba(233, 196, 106, 0.15); }
/* ink focus rings vanish on the film with the rest of the ink; in the relit
   zones the ring relights with the type it belongs to */
.film-ground .column :focus-visible,
.film-ground .column-holder :focus-visible,
.film-ground .rg :focus-visible,
.film-ground .indicia :focus-visible { outline-color: rgba(233, 196, 106, 0.9); }

@media (prefers-reduced-motion: reduce) {
  /* the emblem rests at full brightness; the shine rests off-frame (its base
     mask-position parks the band past the edge), so the logo sits still and
     clean — lit, not gleaming */
  .masthead__emblem, .masthead__shine { animation: none; }
}

/* ============================================================
   14. THE LETTER — the one page where the apparatus is REMOVED
   ============================================================
   No panels, no borders, no balloons, no boil. The absence is the design.
   It lands because everything around it is funny.                          */
.letter { max-width: 74ch; margin: 0 auto; padding: var(--s9) var(--page-pad) var(--s8); }
.letter__frog { width: 210px; margin: 0 0 var(--s6); filter: grayscale(0.15); }
.letter .caption-block { box-shadow: none; border-width: 2px; }
.letter__sign {
  font-family: var(--face-letter);
  font-variation-settings: 'INFM' 30, 'BNCE' 6;
  font-weight: 700;
  font-size: 1.5rem;
  margin-top: var(--s7);
  color: var(--ink-70);
}

/* ============================================================
   15. PARALLAX PLANES
   ============================================================ */
.plane { will-change: transform; }
.diorama {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  aspect-ratio: 3 / 2;
  background: var(--sky);
}
.diorama__plate {
  position: absolute;
  inset: -6%;
  background-size: cover;
  background-position: center;
}

/* ============================================================
   16. RESPONSIVE — mobile is a webtoon, not a downgraded grid
   ============================================================ */
@media (max-width: 900px) {
  .tier--1, .tier--2, .tier--3 { grid-template-columns: minmax(0, 1fr); }
  .tier--1 { gap: var(--gutter); }
  .tier { margin-bottom: calc(var(--gutter) + var(--drop)); }

  /* The tiers collapse to one column — a webtoon, not a broken grid. His
     margin becomes a full-width band between the leads, and he still hangs
     off a page edge rather than standing in the middle of it. */
  .tier__host { min-height: 235px; }
  .frog--host  { --frog-w: clamp(250px, 84vw, 400px); --frog-left: -76px; --frog-bottom: -10px; }
  .frog-say--host  { --say-left: auto; right: 6px; --say-bottom: 200px; }
  /* the palm-clearance indent is desktop-only — he is nowhere near it here */
  .tier--1b > .panel--feature:nth-of-type(1) .panel__body { padding-left: var(--s4); }

  .panel--splash { margin-left: calc(-1 * var(--page-pad)); margin-right: calc(-1 * var(--page-pad)); }
  .panel--splash > .plate { border-right: 0; border-radius: 0; }

  /* The awning is 12px wider than the booth on each side and drops a 3px
     shadow, and below ~858px the booth is exactly as wide as the page's own
     content box — so the overhang lands in --page-pad, whose floor is 14px.
     28 ≥ 27 means it technically fits, by one pixel, which is not a margin:
     it renders as an awning bolted to both edges of the screen. The indent
     goes on .shelf so the head, the stall and the footnote all move together
     and the section keeps one left edge. */
  .shelf { padding-inline: var(--s4); }

  /* One column: both leads go full-bleed and their frames turn letterbox, so
     the art needs height the 340px floor will not give it. Applied to BOTH
     leads — .tier--1 is 1.44fr/1fr and area is hierarchy, so raising only the
     game would make it the tallest panel on the page and invert the tier.
     46vw, not vh: a 390×844 phone reads 42vh as 354px, and the panel would go
     portrait again at exactly the width where the frame needs to stay wide. */
  .panel.panel--feature { min-height: clamp(380px, 46vw, 460px); }

  /* float:none matters — there is no margin to live in on a phone, and a
     floated sticker squeezes the reading column into a gutter. */
  .sticker--margin { float: none; margin: var(--s6) auto; width: 210px; }
  .pair { grid-template-columns: minmax(0, 1fr); }
  /* The tilt is a desktop luxury: a rotated full-width photo on a 390px
     screen clips its own corners against the page padding. */
  .still__pad { transform: none; }
  .indicia__inner { columns: 1; }
  .page-head { padding-top: var(--s5); gap: var(--s4); flex-direction: column; align-items: flex-start; }
  .page-head__id { width: 100%; }
}
@media (max-width: 560px) {
  :root { --drop: 4px; --drop-lg: 6px; --t-body: 1.125rem; }
  .panel__body { padding: var(--s4); }
  /* .panel__body drops to --s4 above, and the number has to follow it or the
     HUD and the text dock disagree about the left margin. 40 = the 24px clip
     bleed plus --s4. */
  .artfill__num { padding: 40px 0 0 var(--s4); }
  /* The barker and his poster stop being two columns some way before they
     become two gutters: at 560 the text track is already under 26ch. */
  .booth__body { grid-template-columns: minmax(0, 1fr); padding: var(--s6) var(--s4) var(--s5); }
  .booth__counter { padding: var(--s3) var(--s4); }
  .flash__in { font-size: 0.74rem; padding: 4px 15px 4px 10px; }
  .nav { width: 100%; justify-content: center; flex-wrap: wrap; }
  .nav a { padding: 0 var(--s3); }
  .page-head__ctrls { width: 100%; align-items: center; }
  .lang { justify-content: center; flex-wrap: wrap; }
}

/* ============================================================
   17. REDUCED MOTION — every plate collapses to its composed still and the
   boil freezes on FRAME 0, which is drawn to be the best-looking frame,
   because half the reviewers will only ever see this.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .boils:hover > .plate, .boils:focus-within > .plate { animation: none; filter: url(#boil-0); }
  .marquee__track { animation: none; }
  .plane, .den__plate, .diorama__plate, .frog__plane { transform: none !important; }
  .masthead__word span::before,
  .masthead__word span::after { transform: none; }
}
