/* ═══════════════════════════════════════════════════════════════════════════
   INTERNETCOVERPAGE — structure only.
   Not one colour, font or effect is decided here: everything visual comes from
   a skin file as a custom property, so a new skin is a new stylesheet and
   nothing else. Load order is base.css, then one skin.
   ═══════════════════════════════════════════════════════════════════════════ */*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  font-family: var(--sans);
  /* the ground is painted by .desk, which sits behind everything — if body
     painted too it would cover the pattern and the branding */
  background: var(--desktop-bg);
  color: var(--fg);
  overflow: hidden;                      /* the story sequence owns the screen */
  -webkit-text-size-adjust: 100%;
}
body.scrolling { overflow: hidden; }
img, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* ── the magazine: one portrait frame, centred ───────────────────────────── */.mag {
  position: fixed; inset: 0; margin: auto; z-index: 2; overflow: hidden;
  width: 100vw; height: 100dvh;
  background: var(--paper-bg); overflow: hidden;
  display: flex; flex-direction: column;
}
@media (min-width: 720px) {
  .mag {
    height: 100dvh; width: min(100vw, calc(100dvh / 1.618));
    box-shadow: var(--mag-shadow);
  }
}


/* ── progress rail: one bar per screen, filling over its 15 seconds ──────── *//* The rail counts the screens, so it belongs on top of everything the screen
   itself draws — the picture, and the grid over it. It gets its own ground as
   well, because a translucent bar over a dot grid reads as though the grid were
   painted on top of it. */.rail { position: absolute; top: 0; left: 0; right: 0; z-index: 60;
  display: flex; align-items: center; gap: 3px; padding: 7px 8px; height: var(--rail-h);
  background: var(--rail-ground); }

/* THE RAIL READS ITSELF OFF WHATEVER IS BEHIND IT.
   
   A fixed white bar over a fixed dark scrim is fine until the picture behind it
   is black, or white, or the video cuts between the two — and then the segments
   either vanish or sit in a grey band that has nothing to do with the artwork.
   `difference` inverts against the backdrop: white over black stays white, and
   over white it goes black. It cannot be invisible, on any frame of any video,
   without the scrim that was there to guarantee it.
   
   Guarded, because a browser without blend modes has to keep the scrim. *//* THE THREE STATES HAVE TO BE TELLABLE APART ON ANY PICTURE.
   
   Blending the rail against the artwork was wrong for a translucent track: with
   `difference`, a 34% white over a mid-grey frame comes out mid-grey, and the
   segments disappeared into the picture exactly where a video spends most of
   its time. The rail gets a scrim of its own instead — three pixels of gradient
   at the very top of the screen, which no artwork has to survive — and on it
   the three states are plainly different:
   
     to come    white at 28%     you can see there are more
     played     white at 72%     you can see you passed them
     running    solid white      you can see it filling
   
   Each state is defined ONCE, below with the geometry. Declaring them here as
   well left two rules for `.done`, and the later one — the old one — won, so
   a passed segment and a running one were the same colour again. */.rail::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(0,0,0,.55), rgba(0,0,0,.28) 60%, transparent);
}
@media (max-width: 560px) {
  .rail { gap: 2px; padding: 8px 7px; height: calc(var(--rail-h) + 4px); }
  .rail .bar { height: calc(var(--bar-h) + 1px); }
}
/* Fifteen segments across a narrow frame is about two centimetres each, and a
   fill that takes fifteen seconds to cross one is invisible. The live segment
   takes four times the width of the others so the movement can actually be
   seen, and settles back when it is done. */.rail .bar { flex: 1; height: var(--bar-h); background: rgba(255,255,255,.28); overflow: hidden;
  transition: flex-grow .3s ease; }
/* INSTAGRAM'S BAR, and it has to be seen moving. Four was not enough on a
   phone: sixty-nine pixels crossed over fifteen seconds is four pixels a
   second, which is movement you can measure and not movement you can see. *//* Thirty seconds against fifteen, drawn as twice the segment — but only while
   it is waiting its turn. This has to sit ABOVE the live rule: same specificity,
   so whichever comes last wins, and a live video segment growing by two instead
   of by eight made the one screen that holds longest the shortest bar on the
   rail. At fourteen it did the opposite and squeezed the other seventeen down
   to eleven pixels each. Live is live; the width says the rest. */.rail .bar.long { flex-grow: 2; }
.rail .bar.live { flex-grow: 8; }
.rail .bar i { display: block; height: 100%; width: 0; background: #fff; }
.rail .bar.done { background: rgba(255,255,255,.72); opacity: 1; }
.rail .bar.done i { width: 100%; }
.rail .bar.live i { animation: fill var(--dwell) linear forwards; }
/* Fixed width and one line, because that is what broke it: at 8px in a flex row
   with no floor under it, 01/16 wrapped and took the rail with it. */.rail .folio { flex: none; width: 34px; text-align: right; white-space: nowrap;
  margin-left: 5px; font-family: var(--mono); font-size: 8px;
  font-weight: 700; letter-spacing: .16em; color: var(--bar-fill); opacity: .75; }
body.paused .rail .bar.live i { animation-play-state: paused; }
@keyframes fill { to { width: 100%; } }
/* NO reduced-motion rule on the progress bar either, and for the same reason
   as the marquee: it filled the segment instantly and sat there, so on any
   phone with battery saver or Reduce Motion on the story never appeared to
   progress — which is exactly what it was reported as, working on a desktop
   and dead on a phone. A progress bar that does not progress is not a calmer
   progress bar. It is a bar that is lying about where you are. *//* ── the stage holds the cover, with the reel closed over it on demand ───── */.stage { position: relative; flex: 1; min-height: 0; }

/* the cover is not part of the sequence — it sits there until you pick a story */.cover { position: absolute; inset: 0; display: flex; flex-direction: column; overflow: hidden; }
.cover > .bg { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: cover; filter: var(--art-filter); }
.cover > .veil { position: absolute; inset: 0; z-index: 2; background: var(--overlay); }

/* ── the reel: opens on a coverline, runs the issue, closes back to the cover ─ */.reel { position: absolute; inset: 0; z-index: 20; background: var(--paper-bg); }
.reel[hidden] { display: none; }
.reel-x {
  position: absolute; z-index: 42; width: 26px; height: 26px;
  top: calc(var(--ctl-mid) - 13px); right: var(--ctl-inset);
  display: grid; place-items: center; color: var(--reel-x-fg); font-size: 17px; line-height: 1;
  text-shadow: var(--shadow-s);
}


/* THE CONTROLS OVER THE ARTWORK READ THEMSELVES OFF IT, exactly as the progress
   rail does. A fixed colour cannot work here: the mute button and the close
   both sit over a photograph that is dark on one skin, pale on another, and
   cuts between the two inside a video. `difference` inverts against whatever
   is actually behind them, so neither can disappear on any frame of any film
   in any theme. *//* ALL FOUR CONTROLS OVER THE ARTWORK ARE ONE SET.
   The mute, the close, the heart and the bookmark sit on top of a picture that
   is dark on one skin, pale on another, and cuts between the two mid-film.
   `difference` inverts against whatever is actually behind them: white over
   black stays white, over white it goes black. None of them can disappear on
   any frame, in any theme, and — this is the point — they cannot disappear
   DIFFERENTLY from each other, which is what a drop-shadow on one and a blend
   on another was doing. */@supports (mix-blend-mode: difference) {
  .sound, .reel-x, .heart, .save, .wide--stage {
    mix-blend-mode: difference; color: #fff; text-shadow: none; filter: none;
  }
  .sound svg, .reel-x svg, .heart svg, .save svg, .wide--stage svg { filter: none; }

  /* EXCEPT FOR THE MOMENT OF THE PRESS. The heart is white and goes back to
     white: it is not a state you are in, it is something you did. For as long
     as the hearts are flying it is red — it steps out of the blend to be red,
     because `difference` has no colour of its own and turns a red into cyan
     over anything pale — and then it is white again. Same behaviour on every
     site, because it is one component. */
  .heart.hit { mix-blend-mode: normal; color: var(--like);
    filter: drop-shadow(0 1px 3px rgba(0,0,0,.6)); }
  .heart.hit svg { filter: none; }
}
@supports not (mix-blend-mode: difference) {
  .sound svg, .reel-x svg, .heart svg, .save svg, .wide--stage svg {
    filter: drop-shadow(0 1px 2px rgba(0,0,0,.55));
  }
}

/* THE COLOUR OF A LIKE. One token, so the heart, the hearts it throws and
   anything that ever counts them are the same red and stay the same red. */:root { --like: #ff2d46; }







.deck { position: absolute; inset: 0; }
.screen { position: absolute; inset: 0; display: none; flex-direction: column; overflow: hidden; }
.screen.on { display: flex; }
/* the multi-story screens are grids, but only once they are the live screen —
   these have to out-specify .screen.on, or they would show all at once */.screen.on.duo { display: grid; }
.screen.on.quad { display: grid; }
.screen > .bg { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: cover; filter: var(--art-filter); }
.screen > .veil { position: absolute; inset: 0; z-index: 2; background: var(--overlay); }

/* ── what a story is doing: views and clicks, top right of every screen ────
   Set on its own chip rather than floating on the art, so it reads over a pale
   shard as well as a dark one, and clear of the progress rail on the top row. *//* THE RIGHT EDGE IS WHERE THESE LIVE. They were all inset 42px to clear the
   close button, but the X is one button at the top of the reel — it is over the
   top row and nothing else, so every counter below it was being held off an
   edge by a button that is nowhere near it. */.counts {
  position: absolute; z-index: 12;
  top: calc(var(--ctl-mid) - 9px); right: var(--ctl-inset); height: 18px;
  display: flex; gap: 8px; align-items: center; box-sizing: border-box;
  padding: 3px 7px; background: var(--counts-bg); border: var(--counts-border);
  font-family: var(--mono); font-size: 8px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--counts-fg);}
.counts b { color: var(--counts-strong); }

/* only the top row of a split screen sits under the rail */.duo article:nth-of-type(n+2) .counts,
.quad article:nth-of-type(n+3) .counts { top: 5px; }
/* The top row is the one the X is over, so that row alone goes to the left. */.screen.story > .counts,
.duo article:nth-of-type(1) .counts,
.quad article:nth-of-type(-n+2) .counts { left: 10px; right: auto; }
.duo .counts, .quad .counts { font-size: 7.5px; gap: 6px; padding: 2px 5px; }

/* ── the two things you can do with a story ─────────────────────────────── */.acts { position: relative; display: flex; align-items: stretch;
  gap: 6px; flex-wrap: wrap; margin-top: 12px; }
/* every control in the row is the same box, so the link and the votes line up */.more, .vote {
  cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
  border: var(--more-border); background: var(--more-bg); color: var(--more-fg);
  padding: 7px 12px; font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: var(--more-track); text-transform: uppercase; line-height: 1;}
/* One arrow and one number. There is no second arrow: a position on this page
   was paid for, and the crowd does not get to punish something somebody bought.
   A vote can be taken back, which is the only way it ever goes down. */.score { display: inline-flex; align-items: stretch; border: var(--more-border); }
.score .vote { border: 0; padding: 7px 11px; line-height: 1; }
.score .n {
  display: inline-flex; align-items: center; justify-content: center; min-width: 46px;
  padding: 0 4px; font-family: var(--mono); font-size: 13px; font-weight: 700;
  color: var(--fg); border-left: var(--more-border);
}
.vote .caret { font-size: 12px; line-height: 1; }
.vote:hover { background: var(--more-bg-hover); color: var(--more-fg-hover); }
.vote[aria-pressed="true"] { background: var(--voted-bg); color: var(--voted-fg); }
/* Cast, but waiting on an address before anybody else sees it. */.score.pending { border-style: dashed; }
.score.pending .vote[aria-pressed="true"] { background: var(--pending-bg); color: var(--pending-fg); }
.score.pending .n { border-left-style: dashed; opacity: .72; }
.duo .score .vote, .quad .score .vote { padding: 5px 8px; }
.quad .score .n { min-width: 34px; font-size: 11px; }
.duo .acts, .quad .acts { margin-top: 8px; }
.duo .more { padding: 5px 9px; font-size: 9px; }


/* Tap zones sit above the picture and below the text. They used to be at 30,
   over everything — and because .body carries z-index 10 it makes its own
   stacking context, so nothing inside it could ever be raised past them. Every
   control in a story was underneath a transparent div. */.tap { position: absolute; top: 0; bottom: 0; z-index: 5; width: 34%; }
.tap.prev { left: 0; }
.tap.next { right: 0; width: 66%; }

/* ── screen 1: the cover ─────────────────────────────────────────────────── */.mast { position: relative; z-index: 10; flex: none; padding: var(--mast-pad); }
.mast svg { width: 100%; display: block; filter: var(--mast-shadow); }
.mast svg text { font-family: var(--display); fill: var(--mast-fill); font-weight: var(--mast-weight); letter-spacing: var(--mast-track); }
/* ONE LINE. Never two.

   The old cover's strip is 17px tall with 2px of padding and 9.5px type, and it
   stays that height because nothing in it is allowed to wrap. Ours was taking a
   long dateline, wrapping it, and growing to two rows — which moved everything
   under it and made the page taller than the one it is copying.

   The middle item is the one that gives: it clips with an ellipsis rather than
   pushing the row open. */.issue-line {
  display: flex; align-items: center; justify-content: var(--issue-justify, space-between);
  gap: var(--issue-gap, 10px); margin-top: 4px; padding: 2px 6px;
  background: var(--issue-bg); color: var(--issue-fg); border: var(--issue-border);
  font-family: var(--mono); font-size: 9.5px; font-weight: 700; line-height: 1.3;
  letter-spacing: var(--issue-track); text-transform: uppercase;
  white-space: nowrap; overflow: hidden;
}
.issue-line > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.issue-line > span:first-child, .issue-line > span:last-child { flex: none; }

/* THE COVER IS THREE QUARTERS AND ONE QUARTER.

   The eight other headlines take the top three quarters; the cover story takes
   the bottom quarter and is always in the same place. A third gave the story
   more room than it needed and left a hole under it. Fixed position, variable treatment — a reader looking for the
   day's story should never have to hunt for it, and the eight above it are the
   part that is allowed to be different every day.

   Set LEFT, because the right of that quarter belongs to the mark that opens
   the issue, and to the square above it. */.lines {
  position: relative; z-index: 10; flex: 3; min-height: 0; overflow: hidden;
  display: flex; justify-content: space-between; gap: 12px; padding: 1.6dvh 16px 3.4dvh;
}
/* Each column keeps to its own three quarters. The last headline was hanging
   eight pixels past the bottom edge and getting cut in half there, which reads
   as a broken page rather than as a column that ends. On a short screen a
   headline gets two lines instead of three and the offset column starts higher
   — the three quarters are the fixed thing, and the type fits into them. */.col { overflow: hidden; }
@media (max-height: 760px) {
  .line .t { -webkit-line-clamp: 2; }
  .line.m .t { -webkit-line-clamp: 2; }
  .col.offset { padding-top: 1.6dvh; padding-bottom: .6dvh; }
}
/* On the smallest screens six headlines fit and eight do not. Six whole ones
   beat seven and a half — the eighth was being sliced through the middle. */@media (max-height: 620px) { .col .line:last-child { display: none; } }
/* The plate behind a headline is the headline's background, so it ran out to
   wherever the text ran to — which on both sides was the edge of the screen.
   The section holds it off; a plate that touches the frame reads as a mistake
   rather than as a plate. */.col { display: flex; flex-direction: column; justify-content: space-between; gap: 1dvh; width: 47%; min-width: 0; }
.col.offset { padding-top: 3dvh; padding-bottom: 1dvh; }
.col.right { text-align: right; align-items: flex-end; }
.col.right .line .k { margin-left: auto; }
/* THE PLATE IS STRUCTURE. Every coverline has one, on every skin, with the same
   geometry — a skin says what COLOUR it is and nothing else. It used to carry
   its own padding in each skin file, so three skins had no plate and two had
   one, and the cover was a different shape depending on which colours somebody
   had picked. The negative margin keeps the type on the same left edge it sat
   on before the plate existed. */.col.right .line .k, .col.right .line .t { margin-left: 0; margin-right: -6px; }
.col.right .line .k { margin-left: auto; }

.line { display: block; max-width: 100%; }
.line .k {
  display: block; margin-bottom: 2px; width: fit-content; color: var(--kick-fg);
  background: var(--line-plate, transparent); padding: 2px 6px; margin-left: -6px;
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: var(--kick-track); text-transform: uppercase; text-shadow: var(--shadow-s);
}
.line .k::before { content: var(--kick-prefix); }
.col.right .line .k::before { content: ""; }
.col.right .line .k::after { content: var(--kick-suffix); }
.line .t {
  background: var(--line-plate, transparent); padding: 2px 6px; margin-left: -6px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  color: var(--c); font-family: var(--sans); font-weight: var(--line-weight);
  font-size: clamp(13px, 2.05dvh, 19px); line-height: 1.14; letter-spacing: -.01em;
  text-shadow: var(--shadow-m);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.line.m .t { font-size: clamp(17px, 2.7dvh, 26px); }
.line.s .t { font-size: clamp(11px, 1.65dvh, 15px); -webkit-line-clamp: 2; }
.line.s .k { font-size: 8px; }
.line:hover .t { text-decoration: underline; }
/* four editorial variants, so no two coverlines are set the same way */.line.v0 .t { text-decoration: var(--v0-decoration); }
.line.v1 .t { font-family: var(--serif); font-style: italic; }
.line.v2 .t { font-family: var(--display); font-weight: 400; text-transform: uppercase; letter-spacing: .02em; }
.line.v3 .t { text-shadow: none; }
.line.v3 .hl {
  background: var(--hl-bg); color: var(--hl-fg); padding: 1px 4px;
  font-family: var(--hl-font); font-weight: var(--hl-weight);
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.new { display: inline-block; background: var(--new-bg); color: var(--new-fg); padding: 0 3px;
  font-family: var(--sans); font-size: 8px; font-weight: 700; letter-spacing: var(--new-track);
  text-shadow: none; transform: var(--new-rotate); }
.blink { animation: var(--blink-anim); }
@keyframes blink { to { visibility: hidden; } }

/* Pushed to the floor. The coverlines take what they need at the top and the
   story takes the rest — it was sitting wherever the lines happened to end,
   with the whole bottom of the page empty under it. *//* THE CORNER, and it is one number.
   
   It was 108px flat, which is right on a desktop and wrong on a phone: the
   bottom quarter of a 664-tall screen is 122px, and a 108 square with a 36
   button under it is 150 — the mark hung out of its own quarter and over the
   picture. The quarter is about 19dvh, so the square is what is left of that
   after the button and the gaps, and never more than the 108 it was drawn at. *//* The floor is 72 and not less, because ALL 25 and the arrow have to sit on one
   line inside it. Narrower and the label wraps, which is how the foot came to
   read ALL / 25 in two rows with the arrow beside them. *//* ── STRUCTURE ────────────────────────────────────────────────────────────
   The frame's own measurements. These used to live in the skin files, where
   each of the five had its own — so the progress bar was 2px on modern and 4px
   on retro, the rail 16 or 18, and the issue line's gap anywhere from 8 to 22.
   Picking a colour scheme was moving the furniture.
   
   A skin says what things are MADE OF. This says how big they are. */:root {
  --rail-h: 17px;          /* the progress rail across the top of a screen */
  --bar-h: 3px;            /*   and the segments in it */
  --statbar-h: 28px;       /* the figures across the foot */

  /* ── WHERE A CONTROL OVER THE ARTWORK SITS ────────────────────────────
     Two numbers, and every control on the picture uses them instead of a
     value of its own. They were all inset differently — the mute and the
     close at 8, the keepers at 12, the switch at 10 — and vertically each
     was pinned by its own top, so three things meant to be on one line had
     three centres: 50.5, 51 and 53. Nothing looked aligned because nothing
     was aligned to anything.

     --ctl-inset  the distance from whichever edge the control is on
     --ctl-mid    the centre line of the top row, measured from the stage */
  /* THE MARQUEE'S HEIGHT, as a number anything else can read. It was written
     into `.ticker` and nowhere else, so a sheet that wants to stop underneath
     it had to repeat the twenty pixels and hope. */
  --ticker-h: 20px;
  --ctl-inset: 10px;
  --ctl-mid: calc(var(--rail-h) + 14px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --adbar-h: 58px;         /*   and the row above them */
  --mast-pad: 3px 8px 0;   /* the masthead's own inset */
  --issue-gap: 12px;       /* between the three parts of the issue line */
  --led-size: 3px;         /* the live dot */
}

/* THE FOOT IS WHERE A THUMB IS, and a thumb is not a mouse pointer.
   
   The comment and share buttons were 40 by 27 and sat sixteen pixels off the
   bottom of the screen — under the home indicator on a phone, and well below
   the size a finger can hit. They work in an emulator and not in a hand.
   
   Forty-four is the smallest target that is reliably hit, and the bar clears
   whatever the device keeps for itself at the bottom. */@media (max-width: 700px) {
  /* SHORTER, BUT NOT BELOW WHAT A FINGER NEEDS. The bar came down everywhere;
     on a phone it stops at 40 rather than 28, because the buttons in it keep
     their 38px height and 44 was chosen for the TARGET, not for the band. */
  :root { --statbar-h: 40px; }
  .statbar { padding-bottom: calc(3px + var(--safe-b)); }
  .statbar .act { min-width: 44px; min-height: 38px; }
  .nlpop { bottom: calc(var(--statbar-h, 40px) + var(--safe-b)); }
  .menu { bottom: calc(3px + var(--safe-b)); }
}


.cover { --corner: clamp(72px, calc(19dvh - 56px), 108px); }

.lead {
  position: relative; z-index: 10; flex: 1; min-height: 0;
  width: 100%; padding: 0 10px 1.4dvh; margin-top: 0;
  display: flex; flex-direction: column; justify-content: flex-start;
  text-align: left; overflow: hidden;
}
/* The mark sits in the right of this quarter, so the story keeps clear of it —
   the corner's own width plus a gap you can see rather than a hairline. It was
   a hardcoded 136 with a second guess at 380px wide, and both were wrong the
   moment the square stopped being one size. */.lead.has-mark { padding-right: calc(var(--corner) + 28px); padding-bottom: 1.4dvh; }
.lead .k { display: block; margin-bottom: 3px; color: var(--lead-k-fg); font-family: var(--mono);
  font-size: 10px; font-weight: 700; letter-spacing: var(--lead-k-track); text-transform: uppercase;
  text-shadow: var(--shadow-s); }
/* Kept inside its quarter. TWO lines, because three at this size ran past the
   floor and the standfirst printed over the last one — a fixed slot is only a
   fixed slot if what goes in it is made to fit. The size stays large; what
   gives is the number of lines. */.lead .t { color: var(--lead-fg); font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(20px, 3.5dvh, 38px); line-height: 1; text-transform: uppercase;
  letter-spacing: var(--lead-track); text-shadow: var(--shadow-l);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.lead .sub { display: block; margin-top: 5px; color: var(--sub-fg); font-family: var(--serif);
  font-style: italic; font-size: clamp(12px, 1.75dvh, 15px); line-height: 1.3;
  text-shadow: var(--shadow-s); -webkit-line-clamp: 2; min-height: 0; }

/* THE STANDFIRST IS WHAT GIVES WAY when the quarter is tight. The headline
   never does — and it was doing exactly that on a phone: the quarter is 122px
   at 664 tall, the four things in it wanted 165, and the only one that could
   shrink was the headline, so the cover printed a kicker, a standfirst and a
   price with NO STORY between them.
   
   These two rules have to sit after the declarations above them. The first one
   did not, which is why it never fired once. */.lead .t { flex: none; }
@media (max-height: 720px) { .lead .sub { display: none; } }
/* A long one drops a step and is allowed a third line. Anything set larger runs
   past the floor of the quarter, and what gives is the size, never the words. */.lead .t.long { font-size: clamp(17px, 2.7dvh, 29px); -webkit-line-clamp: 3; }
.cover.selling .lead .t.long { font-size: clamp(16px, 2.3dvh, 25px); }
.lead:hover .t { text-decoration: underline; }

.vid-tag { display: inline-block; margin-left: 8px; background: var(--tag-bg); color: var(--tag-fg);
  padding: 1px 6px; font-family: var(--mono); font-size: 8px; letter-spacing: .2em; }

/* ── story furniture ────────────────────────────────────────────────────────
   Shared by every screen that carries a story — one, two or six of them — so a
   headline is set the same way wherever it lands. The screen types below vary
   size and clamping only. */.kick { color: var(--kick-fg); font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: var(--kick-track); text-transform: uppercase; text-shadow: var(--shadow-s); margin: 0 0 6px; }
.ttl { margin: 0; color: var(--lead-fg); font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(21px, 3.6dvh, 36px); line-height: 1.06; letter-spacing: var(--lead-track);
  text-shadow: var(--shadow-l); }
.sum { margin: 8px 0 0; color: var(--sub-fg); font-family: var(--serif); font-style: italic;
  font-size: clamp(12px, 1.8dvh, 15px); line-height: 1.35; text-shadow: var(--shadow-s); }

/* ── screens 2–9: one story, full bleed ────────────────────────────────────
   A skin whose ink is dark cannot rely on a scrim: it gets a ground under the
   words instead, the same answer the coverlines take. *//* THE WORDS SIT ON THE BOTTOM EDGE. `4dvh` is twenty-six pixels on a phone and
   forty on a desktop, and it was reserving that under the standfirst for
   nothing — the foot bar is already below the screen, not inside it. */.story .body { position: relative; z-index: 10; margin-top: auto; padding: 8px 14px 12px;
  background: var(--body-plate, transparent); }
.duo .body, .quad .body { background: var(--body-plate, transparent); }
.more { position: relative; }
.more:hover { background: var(--more-bg-hover); color: var(--more-fg-hover); }

/* ── #10–17: two stories to a screen ─────────────────────────────────────── */.duo { grid-template-rows: 1fr 1fr; }
.duo .half { position: relative; overflow: hidden; display: flex; flex-direction: column; }
.duo .half + .half { border-top: var(--split-rule); }
.duo .half > .bg { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: cover; filter: var(--art-filter); }
.duo .half > .veil { position: absolute; inset: 0; z-index: 2; background: var(--overlay); }
.duo .body { position: relative; z-index: 10; margin-top: auto; padding: 0 12px 14px; }
.duo .ttl { font-size: clamp(15px, 2.4dvh, 23px); -webkit-line-clamp: 3; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.duo .sum { font-size: clamp(11px, 1.5dvh, 13px); -webkit-line-clamp: 2; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }

/* ── #18–25: four stories to a screen ────────────────────────────────────── */.quad { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.quad .cell { position: relative; overflow: hidden; display: flex; flex-direction: column; border: var(--split-rule); border-width: 0 0 1px 0; }
.quad .cell:nth-child(odd) { border-right-width: 1px; }
.quad .cell > .bg { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: cover; filter: var(--art-filter); }
.quad .cell > .veil { position: absolute; inset: 0; z-index: 2; background: var(--overlay); }
.quad .body { position: relative; z-index: 10; margin-top: auto; padding: 0 9px 10px; }
.quad .kick { font-size: 7.5px; margin-bottom: 3px; }
.quad .ttl { font-size: clamp(11px, 1.65dvh, 15px); line-height: 1.14; -webkit-line-clamp: 3; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.quad .sum { display: none; }

/* ── the marquee, across the top of the frame ────────────────────────────── */.ticker {
  position: relative; z-index: 41; flex: none; width: 0; min-width: 100%;
  height: var(--ticker-h); display: flex; align-items: center;
  overflow: hidden; background: var(--bar-bg); border-bottom: var(--bar-rule);
}
/* The run is the height of the strip and its words are centred in it. Setting
   the line-height to the strip's height instead put the type on the baseline of
   a twenty-pixel line, which is not the middle of a twenty-pixel strip — it sat
   on the floor. */.tk { display: inline-flex; align-items: center; height: 100%; white-space: nowrap;
  animation: tk var(--tk-speed, 45s) linear infinite; }
/* The strip centres what is in it. Three goes at this went through line-height,
   which sets type on a baseline and is not the same thing as putting it in the
   middle of a box — it kept landing on the floor. *//* SET LIKE THE ORIGINAL, because the original is right: eleven px, sentence
   case, and tracking you can barely see. It was shouting — uppercase at .22em,
   which is a headline's setting, not a wire's. A ticker is meant to be read
   past, and small letters read faster than capitals.

   Twenty tall, and line-height IS the height, so the type sits centred on the
   strip rather than padding pushing it open. */.tk span { font-family: var(--mono); font-size: 11px; font-weight: 700; line-height: 1;
  letter-spacing: .04em; text-transform: none; color: var(--tk-fg); padding: 0 1.15em; }
.tk span.hot { color: var(--tk-hot); }
@keyframes tk { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* NO reduced-motion rule, on purpose, and this is the second thing to go wrong
   here. It first said `animation: none`, which froze the headlines dead on any
   phone with battery saver or Reduce Motion on — while the same phone ran the
   marquee on internetcover.pages.dev, which has no such rule. Slowing it down
   instead was no better: the marquee then ran at one speed on a desktop and
   another on a phone, which is the same bug wearing a different hat.

   One speed, everywhere, the same as the original. *//* ── the chrome strips: ad slot and statistics bar, and the menu over both ── */.chrome { position: relative; flex: none; }

/* The label sits at the left edge and the slot takes the rest of the row, with
   the creative centred inside it.

   It used to be the label and a 320px slot centred together as one group, which
   left a margin of dead chrome down both sides and pushed the AD chip into the
   middle of nowhere. The old site does it the other way and that is why its ad
   row reads as a row rather than as a floating box. *//* THE ROW RESERVES ITS OWN HEIGHT. Fifty for the creative and four either
   side, stated rather than inferred from whatever is inside it — a row that
   measures its contents is a row that moves when they arrive. */.adbar { flex: none; display: flex; align-items: center; justify-content: flex-start; gap: 6px;
  min-height: var(--adbar-h, 58px);
  padding: 4px; background: var(--chrome); border-top: var(--chrome-rule); }
/* THE LIST BAR. It stands where the advertisement stands on every other page,
   at exactly the same height, because the row across the foot of the frame is
   one row and its height is part of the furniture. Tomorrow does not carry an
   advertisement; it carries the way into the board. */.listbar { text-decoration: none; gap: 8px; min-height: var(--adbar-h, 58px);
  padding-inline: 10px; }
/* The creative's own box, so the rotator cannot resize the row under it. */.adrot { flex: 1; min-width: 0; height: 50px; overflow: hidden; }
.adslide { height: 50px; }
.listbar .lb-t { font-family: var(--sans); font-size: 12px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--kick-fg); }
.listbar .lb-s { flex: 1; min-width: 0; font-family: var(--serif); font-size: 11px;
  font-style: italic; color: var(--sub-fg); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.listbar .lb-a { font-size: 15px; line-height: 1; color: var(--kick-fg);
  animation: va-nudge 2.4s ease-in-out infinite; }
.listbar:hover .lb-t { text-decoration: underline; }
.listbar:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
@media (max-width: 560px) { .listbar .lb-s { display: none; } }
@media (prefers-reduced-motion: reduce) { .listbar .lb-a { animation: none; } }

/* WHICH ROW IS ACROSS THE FOOT is the section's business, and both rows ship on
   every page so a swapped section can change it without the chrome being
   rebuilt. Tomorrow carries the way into its list; everything else carries the
   advertisement. */html[data-foot="ad"]   .listbar,
html[data-foot="list"] .adbar:not(.listbar),
html[data-foot="none"] .adbar { display: none; }

/* A PAGE THAT IS THE LIST has nothing to close back to, so it carries no X.
   That is true of /today as much as of /tomorrow-list, and it is said by the
   document — data-list — rather than by which kind of issue it happens to be. */[data-list] .allnews-x { display: none; }
[data-kind="list"] .adbar, [data-list] .adbar { display: flex; }

/* WHERE YOU ARE, in the menu. A rule on aria-current, so the marker follows the
   document rather than a class somebody has to keep in step with it. */.menu a[aria-current="page"] {
  color: var(--kick-fg);
  box-shadow: inset 3px 0 0 var(--kick-fg);
  padding-left: 13px;
}
.menu a[aria-current="page"]::after { content: " ·"; opacity: .6; }

.ad-lbl { font-family: var(--sans); font-size: 8px; font-weight: 700;
  background: var(--adlbl-bg); border: var(--adlbl-border); color: var(--adlbl-fg); padding: 0 3px; }
/* THE AD, FULL WIDTH.

   The height is fixed and the width is the row. A slot pinned to 320px leaves
   dead chrome on both sides of it, and I spent too long defending that because
   the old cover does it — the old cover having the same gap is not a reason to
   keep it.

   --slot-h is the only dimension that matters now. Everything that appears in
   this row — both advertisements and the letter — is one full-width band of the
   same height, so the row never changes size and nothing in it floats. */:root { --slot-h: 50px; }
.mag { container-type: inline-size; container-name: mag; }

.adrot { flex: 1; min-width: 0; height: var(--slot-h); overflow: hidden; }
.adslide { flex: 0 0 100%; height: 100%; }
.adslide .subscribe { width: 100%; height: 100%; }
.adtrack { display: flex; height: 100%; transition: transform .45s ease; }
@media (prefers-reduced-motion: reduce) { .adtrack { transition: none; } }

.banner { width: 100%; height: 100%; padding: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; overflow: hidden; background: var(--banner-bg); border: var(--banner-border);
  animation: var(--banner-anim); }
@keyframes bflash { to { border-color: var(--banner-flash); } }
/* the creative has to fit the slot it was sold — nothing clips, nothing bleeds */.banner { gap: 2px; }
.banner .b1 {
  max-width: 100%; color: var(--banner-b1); font-family: var(--sans); font-weight: 700;
  font-size: clamp(8px, 2.4vw, 11px); line-height: 1.2; letter-spacing: .02em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.banner .b2 {
  max-width: 100%; margin-top: 2px; color: var(--banner-b2); font-family: var(--display);
  font-size: clamp(11px, 3.1vw, 15px); line-height: 1.15; letter-spacing: .02em;
  text-decoration: underline; text-underline-offset: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (min-width: 620px) {
  .banner .b1 { font-size: 11px; }
  .banner .b2 { font-size: 17px; }
}
@media (min-width: 900px) {

  .banner .b1 { font-size: 13px; letter-spacing: .06em; }
  .banner .b2 { margin-top: 5px; font-size: 24px; }
}
/* the newsletter fills the same slot, whatever size it is */:is(.adslide, .nlpop, .encore-nl) .subscribe { height: 100%; padding: 6px 10px; }
@media (min-width: 900px) {
  :is(.adslide, .nlpop, .encore-nl) .subscribe { padding: 10px 14px; gap: 10px; }
  :is(.adslide, .nlpop, .encore-nl) .subscribe .lbl { font-size: 11px; max-width: 150px; }
  :is(.adslide, .nlpop, .encore-nl) .subscribe input { font-size: 13px; padding: 9px 11px;}
  :is(.adslide, .nlpop, .encore-nl) .subscribe button { font-size: 11px; padding: 10px 20px;}
}


/* ── the statistics bar — and the menu, which is exactly its height ──────── */.statbar {
  flex: none; display: flex; gap: 3px;
  padding: 2px 3px; background: var(--chrome); border-top: var(--chrome-rule);
  font-size: 9.5px; min-height: var(--statbar-h);
}
.statbar .cell {
  display: flex; align-items: center; border: var(--cell-border); padding: 1px 7px;
  color: var(--cell-fg); font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; overflow: hidden;}
.statbar .cell.grow { flex: 1; }
.statbar .cell b { color: var(--led); }
/* NOTHING is dropped from the bar on a phone. READING NOW carries the `wide`
   class and this rule hid it under 560px — on the one screen where it is the
   only live number on the page. The strip scrolls; that is what it is for, and
   it is why there is nothing here to hide. */.burger {
  position: relative; z-index: 46; flex: none; width: var(--statbar-h);
  display: grid; place-items: center; border: var(--cell-border);
  background: var(--chrome); color: var(--cell-fg);}
.burger i {
  position: absolute; left: 50%; width: 14px; height: 2px; margin-left: -7px;
  background: currentColor; transition: transform .16s ease, opacity .16s ease;
}
.burger i:nth-child(1) { transform: translateY(-5px); }
.burger i:nth-child(3) { transform: translateY(5px); }
/* open: the top and bottom rules cross, the middle one goes */.burger[aria-expanded="true"] { background: var(--menu-hover-bg); color: var(--menu-hover-fg); }
.burger[aria-expanded="true"] i:nth-child(1) { transform: rotate(45deg); }
.burger[aria-expanded="true"] i:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] i:nth-child(3) { transform: rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .burger i { transition: none; } }

/* the menu takes the statistics bar; the ad slot above it turns into the
   newsletter for as long as the menu is open. Six entries rarely fit a frame
   this narrow, so it scrolls rather than squeezing them into initials. */.menu {
  position: absolute; z-index: 45; left: calc(var(--statbar-h) + 6px); right: 3px;
  bottom: 3px; height: calc(var(--statbar-h) - 4px);
  display: flex; gap: 3px; background: var(--chrome);
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  overscroll-behavior-x: contain; scroll-snap-type: x proximity;
  mask-image: linear-gradient(90deg, transparent 0, #000 6px,
    #000 calc(100% - 6px), transparent 100%);
}
.menu::-webkit-scrollbar { display: none; }
.menu[hidden] { display: none; }
.menu a, .menu button[data-drawer] {
  /* never shrink an entry to fit — the strip scrolls instead */
  flex: none; scroll-snap-align: start; white-space: nowrap; padding: 0 15px;
  display: flex; align-items: center; justify-content: center; text-align: center;
  border: var(--cell-border); background: var(--menu-bg); color: var(--menu-fg);
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: var(--menu-track); text-transform: uppercase;}
.menu a:hover, .menu a[aria-current],
.menu button[data-drawer]:hover { background: var(--menu-hover-bg); color: var(--menu-hover-fg); }


/* ── newsletter: on the cover, and at the head of the list ───────────────── *//* The newsletter is the same object wherever it appears; only the box it has to
   fill changes. In the ad slot it fills the slot; in the list it is a strip. */.subscribe {
  width: 100%; display: flex; align-items: center; gap: 7px;
  border: var(--nl-border); padding: 6px 8px; background: var(--nl-bg);
}
:is(.adslide, .nlpop, .encore-nl) .subscribe { height: 100%; padding: 5px 8px; }
.allnews .subscribe { max-width: 460px; margin: 0 auto; }
.allnews .subscribe .lbl { max-width: 120px; }
.subscribe .lbl {
  flex: none; font-family: var(--mono); font-size: 9px; font-weight: 700; line-height: 1.2;
  letter-spacing: .12em; text-transform: uppercase; color: var(--nl-fg); max-width: 104px;
}
@media (max-width: 560px) { .subscribe .lbl { max-width: 68px; font-size: 8px; } }
.subscribe input {
  flex: 1; min-width: 0; font-family: var(--mono); font-size: 11px; font-weight: 700;
  background: var(--nl-field-bg); color: var(--nl-fg);
  border: 1px solid var(--nl-field-border); padding: 6px 8px;
}
.subscribe input::placeholder { color: var(--nl-placeholder); font-weight: 400; }
.subscribe input:focus-visible { outline: 2px solid var(--nl-btn-bg); outline-offset: 0; }
.subscribe button {
  flex: none; font-family: var(--mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  background: var(--nl-btn-bg); color: var(--nl-btn-fg); border: 0; padding: 7px 14px;
}
.subscribe button:hover { filter: brightness(1.25); }
/* the slot announces itself when it is carrying the newsletter rather than an ad *//* ── the foot of the cover ───────────────────────────────────────────────────
   A mark in the bottom right, not a bar across the bottom. Full width made it
   compete with the headlines it was supposed to point past; at this size it
   reads as "there is more this way", which is all it ever had to say. *//* Over the cover, not under it. As a row of its own it pushed the cover story
   up and left the whole bottom-left corner empty — the mark only occupies the
   right, so the text should have the rest.

   Absolutely positioned with no width, so it shrink-wraps to the widest thing
   in it, which is the mark. */.cover-foot {
  position: absolute; right: 10px; bottom: 1.2dvh; z-index: 14;
  display: flex; justify-content: flex-end;
}
.viewall {
  display: inline-flex; align-items: center; justify-content: space-between;
  width: var(--corner); gap: 6px; cursor: pointer;
  padding: 8px 9px 8px 11px;
  background: var(--raise-bg, var(--kick-fg)); color: var(--raise-fg, var(--paper-bg));
  border: 0; text-decoration: none;
}
.va-t {
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase; line-height: 1;
  white-space: nowrap;
}
/* At the floor width the tracking is what gives, not the line. */@media (max-height: 760px) { .va-t { letter-spacing: .1em; } }
/* An arrow, set in the label's own colour. It was a filled square with a play
   triangle in it, which on a phone read as a video control on a page whose
   cover is sometimes a video. An arrow says "this way" and nothing else. */.va-a {
  font-style: normal; font-size: 15px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  width: auto; height: auto; background: none;
  color: var(--raise-fg, var(--paper-bg));
  animation: va-nudge 2.4s ease-in-out infinite;
}
/* A small movement rather than a big button: the eye finds motion at the edge
   of a still page without the page having to shout. */@keyframes va-nudge {
  0%, 72%, 100% { transform: translateX(0); }
  80%           { transform: translateX(3px); }
  88%           { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) { .va-a { animation: none; } }
.viewall:hover { filter: brightness(1.15); }
.viewall:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ── where the issue ends: the weekly and the archive ───────────────────── */.endlinks { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 14px; }

/* ── all news: the same frame, scrolling inside it ────────────────────────
   Not another page and not another design — the marquee, the ad slot and the
   statistics bar stay exactly where they are; only this panel scrolls. */.allnews {
  position: absolute; inset: 0; z-index: 22; overflow-y: auto; overscroll-behavior: contain;
  background: var(--paper-bg); padding: 14px 12px 30px;
  /* what the head pulls back out to */
  --panel-pad: 12px;
}
.allnews[hidden] { display: none; }








.allnews-x {
  flex: none; align-self: center; z-index: 3; width: 26px; height: 26px;
  display: grid; place-items: center; color: var(--reel-x-fg); font-size: 17px; line-height: 1;
}
.allnews .band { display: flex; align-items: center; gap: 12px; margin: 26px 0 12px;
  font-family: var(--mono); font-size: 9px; letter-spacing: var(--kick-track);
  text-transform: uppercase; color: var(--kick-fg); }
.allnews .band::after { content: ''; flex: 1; height: 1px; background: var(--rule-color); }
.feedrow { display: grid; grid-template-columns: 26px 62px 1fr; gap: 10px; align-items: start;
  padding: 10px 0; border-bottom: 1px solid var(--rule-color); }
.feedrow .no { font-family: var(--mono); font-size: 11px; color: var(--kick-fg); }
.feedrow img { width: 62px; height: 46px; object-fit: cover; filter: var(--art-filter); }
.feedrow h3 { margin: 0; font-family: var(--display); font-weight: var(--lead-weight);
  font-size: 15px; line-height: 1.2; color: var(--fg); }
.feedrow p { margin: 4px 0 0; font-family: var(--mono); font-size: 9px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--kick-fg); }
.endmark { text-align: center; padding: 30px 0 6px; clear: both; }
.endmark .subscribe { margin: 18px auto 0; text-align: left; }
.endmark p { font-family: var(--mono); font-size: 10px; letter-spacing: var(--kick-track);
  text-transform: uppercase; color: var(--kick-fg); margin: 0 0 8px; }
.endmark b { font-family: var(--display); font-weight: var(--lead-weight); font-size: 26px; color: var(--fg); }
.backtop { display: inline-block; margin-top: 18px; border: var(--more-border);
  padding: 8px 16px; font-family: var(--mono); font-size: 10px;
  letter-spacing: var(--more-track); text-transform: uppercase; color: var(--fg); }

.sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* keyboard users must always see where they are */a:focus-visible, button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE DESK — everything outside the magazine frame.
   Desktop only: on a phone the frame is the whole screen and none of this has
   anywhere to live. All of it is driven by data-attributes on <html>, so the
   switcher only ever sets an attribute and the stylesheet does the rest.
   ═══════════════════════════════════════════════════════════════════════════ *//* ── desk patterns ───────────────────────────────────────────────────────── */.desk { position: fixed; inset: 0; z-index: 0; background: var(--desk-bg); }
.desk::after { content: ''; position: absolute; inset: 0; opacity: var(--desk-ink, .5); }

[data-desk="plain"] .desk::after { background: none; }

[data-desk="dots"] .desk::after {
  background-image: radial-gradient(var(--desk-fg) 1px, transparent 1px);
  background-size: 14px 14px;
}
[data-desk="grid"] .desk::after {
  background-image: linear-gradient(var(--desk-fg) 1px, transparent 1px),
                    linear-gradient(90deg, var(--desk-fg) 1px, transparent 1px);
  background-size: 32px 32px;
}
[data-desk="pinstripe"] .desk::after {
  background-image: repeating-linear-gradient(45deg,
    var(--desk-fg) 0 1px, transparent 1px 9px);
}
[data-desk="rule"] .desk::after {
  background-image: repeating-linear-gradient(0deg,
    var(--desk-fg) 0 1px, transparent 1px 7px);
}
[data-desk="halftone"] .desk::after {
  background-image: radial-gradient(var(--desk-fg) 22%, transparent 23%),
                    radial-gradient(var(--desk-fg) 22%, transparent 23%);
  background-size: 18px 18px;
  background-position: 0 0, 9px 9px;
}
[data-desk="vignette"] .desk::after {
  background: radial-gradient(ellipse at center, transparent 40%, var(--desk-fg) 140%);
  opacity: .55;
}

/* ── branding takeover: the gutters either side, fading toward the page ──── */.deskbrand { position: fixed; inset: 0; z-index: 1; display: none; pointer-events: none; }
[data-brand="on"] .deskbrand { display: block; }
.deskbrand i {
  position: absolute; top: 0; bottom: 0; width: 50vw;
  background-image: var(--brand-art);
  background-size: cover;
}
/* one campaign photograph, read from both edges and falling away toward the
   frame — the way a desktop takeover always worked */.deskbrand .l { left: 0;  background-position: left center;
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 46%, transparent 98%);
          mask-image: linear-gradient(90deg, #000 0%, #000 46%, transparent 98%); }
.deskbrand .r { right: 0; background-position: right center;
  -webkit-mask-image: linear-gradient(270deg, #000 0%, #000 46%, transparent 98%);
          mask-image: linear-gradient(270deg, #000 0%, #000 46%, transparent 98%); }
/* only the wordmark is clickable — a takeover should not swallow every stray
   click on the desk, which is exactly what a full-width anchor was doing */.deskbrand .word {
  position: absolute; left: 3vw; top: 50%; transform: translateY(-50%);
  max-width: min(24vw, 300px); color: var(--brand-word-fg);
  text-shadow: 0 2px 20px rgba(0,0,0,.7); pointer-events: auto;
  display: block; padding: 10px 12px; border: 1px solid transparent;
}
.deskbrand .word:hover { border-color: var(--brand-word-fg); }
.deskbrand .word b {
  display: block; font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(22px, 2.4vw, 40px); line-height: 1.02; letter-spacing: var(--lead-track);
}
.deskbrand .word span {
  display: block; margin-top: 8px; font-family: var(--mono); font-size: 10px;
  font-weight: 700; letter-spacing: .26em; text-transform: uppercase;
}
@media (max-width: 1200px) { .deskbrand .word { display: none; } }
.deskbrand .tag {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
  font-family: var(--mono); font-size: 8px; font-weight: 700; letter-spacing: .3em;
  text-transform: uppercase; color: var(--brand-tag-fg); background: var(--brand-tag-bg);
  padding: 2px 8px;
}

/* ── reading modes ───────────────────────────────────────────────────────────
   Both modes work on the TYPE — face, size, measure, weight, position and how
   much of it there is. Neither one touches the artwork: somebody paid for that
   image or that video, and dimming it is not ours to do. What changes is the
   text sitting on it.

   focus  — less text. The headline, and nothing else competing with the picture.
   reader — more text. A solid sheet for the words to sit on, at reading size.  *//* ── The two levels ──────────────────────────────────────────────────────────
   Normal → Focus → Reader, and they stack: Reader does everything Focus does
   and then goes further. Focus takes the furniture away; Reader also resets the
   type for reading. Neither touches the artwork.                              *//* LEVEL 1 — Focus. Everything competing with the story steps back. */[data-mode="focus"] .ticker, [data-mode="reader"] .ticker,
[data-mode="focus"] .chrome, [data-mode="reader"] .chrome {
  opacity: .22; transition: opacity .3s ease;
}
[data-mode="focus"] .ticker:hover, [data-mode="reader"] .ticker:hover,
[data-mode="focus"] .chrome:hover, [data-mode="reader"] .chrome:hover,
[data-mode="focus"] .chrome:focus-within, [data-mode="reader"] .chrome:focus-within {
  opacity: 1;
}
[data-mode="focus"] .rail, [data-mode="reader"] .rail,
[data-mode="focus"] .counts, [data-mode="reader"] .counts,
[data-mode="focus"] .desk::after, [data-mode="reader"] .desk::after,
[data-mode="focus"] .deskbrand, [data-mode="reader"] .deskbrand { display: none; }
[data-mode="focus"] .kick, [data-mode="reader"] .kick,
[data-mode="focus"] .line .k, [data-mode="reader"] .line .k,
[data-mode="focus"] .lead .k, [data-mode="reader"] .lead .k { opacity: .72; }
[data-mode="focus"] .mag, [data-mode="reader"] .mag { box-shadow: 0 0 120px rgba(0,0,0,.6); }

/* LEVEL 2 — Reader. On top of all of that, the words are reset to be read. */[data-mode="reader"] .story .body,
[data-mode="reader"] .duo .body,
[data-mode="reader"] .quad .body,
[data-mode="reader"] .cover .lead {
  background: var(--reader-sheet); padding: 14px 16px;
  border-top: var(--reader-sheet-rule);
}
[data-mode="reader"] .cover .lead { width: 100%; margin: 0; }
[data-mode="reader"] .ttl,
[data-mode="reader"] .lead .t {
  font-family: var(--serif); font-weight: 400; text-transform: none;
  letter-spacing: 0; line-height: 1.22; text-shadow: none;
  font-size: clamp(17px, 2.5dvh, 25px); -webkit-line-clamp: unset; display: block; overflow: visible;
}
[data-mode="reader"] .sum {
  font-style: normal; font-family: var(--serif); color: var(--reader-body-fg);
  font-size: clamp(13px, 1.85dvh, 16px); line-height: 1.6; max-width: 62ch;
  text-shadow: none; -webkit-line-clamp: unset; display: block; overflow: visible;
}
[data-mode="reader"] .quad .sum { display: block; font-size: 11px; line-height: 1.45; }
[data-mode="reader"] .line .t {
  font-family: var(--serif); font-weight: 400; text-transform: none;
  letter-spacing: 0; line-height: 1.28; -webkit-line-clamp: 4; text-shadow: none;
  background: var(--reader-sheet); padding: 3px 6px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
[data-mode="reader"] .lead .sub { font-style: normal; font-size: clamp(12px, 1.7dvh, 15px); line-height: 1.55; }

@media (prefers-reduced-motion: reduce) {
  [data-mode="focus"] .ticker, [data-mode="reader"] .ticker,
  [data-mode="focus"] .chrome, [data-mode="reader"] .chrome { transition: none; }
}

/* ── the switcher, parked on the desk ────────────────────────────────────── */.switcher {
  position: fixed; left: 14px; bottom: 14px; z-index: 60;
  font-family: var(--mono); display: none;
}
@media (min-width: 1080px) { .switcher { display: block; } }

.switcher-btn {
  display: flex; align-items: center; gap: 7px;
  background: var(--sw-bg); color: var(--sw-fg); border: var(--sw-border);
  padding: 8px 12px; font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase;}
.switcher-btn .swatch {
  width: 11px; height: 11px; border: 1px solid currentColor;
  background: linear-gradient(135deg, var(--sw-a) 50%, var(--sw-b) 50%);
}
.switcher-panel {
  position: absolute; left: 0; bottom: calc(100% + 8px); width: 232px;
  background: var(--sw-bg); border: var(--sw-border); padding: 12px;
  display: grid; gap: 12px;}
.switcher-panel[hidden] { display: none; }
.switcher h4 {
  margin: 0 0 6px; font-size: 8px; font-weight: 700; letter-spacing: .28em;
  text-transform: uppercase; color: var(--sw-dim);
}
.opts { display: flex; flex-wrap: wrap; gap: 4px; }
.opts button {
  flex: 1 1 auto; padding: 6px 8px; font-family: var(--mono); font-size: 8.5px;
  font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  background: transparent; color: var(--sw-fg); border: var(--sw-border);}
.opts button:hover { background: var(--sw-hover-bg); color: var(--sw-hover-fg); }
.opts button[aria-pressed="true"] { background: var(--sw-on-bg); color: var(--sw-on-fg); border-color: var(--sw-on-bg); }

/* ── menu pages, swapped into the shell ─────────────────────────────────────
   Same frame, same chrome. htmx replaces only what is inside this box, so the
   marquee, the ad slot and the menu are never re-rendered or re-painted. *//* ── THE TITLE CARD ─────────────────────────────────────────────────────────
   A leader over the middle: the name, an empty field, and the foot. It is an
   overlay rather than a page of its own so the film behind it is loading the
   whole time it is up — reaching zero stops the wait, it does not start a
   download. */.leader {
  /* ABOVE EVERYTHING THE FEED DRAWS. At 30 it was under the player's own mute
     at 42 and under anything else the engine puts over its video, so a cover
     page was showing a feed's counters, a feed's mute and the playing post's
     headline through it. The card is the page while it is up. */
  position: absolute; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  background: var(--paper-bg); color: var(--fg);
  padding: var(--mast-pad, 10px 12px 0);
}
.leader-mid { flex: 1; min-height: 0; }
html[data-started] .leader { display: none; }

/* THE FOOT OF IT: the count at the left end, the mark and the arrow at the
   right, on the one line — the coverpage's corner, widened. */



/* THE CORNER IS THE COVERPAGE'S, UNTOUCHED. It was being flattened into a row
   here and given its own badge size, which is a second version of a component
   we already have — the whole reason this card exists is that it is made of
   the same parts. `.cover-foot` keeps its own position, its own width and its
   own dimensions; the count is its mirror image on the left, the same size and
   the same distance from the same edge. */.leader { --corner: clamp(72px, calc(19dvh - 56px), 108px); }
/* SHOWN ONLY WHEN THE PAGE IS ONE FILM. Everything else on the player is the
   channel's; this is the way out of a shared clip and it has no meaning
   anywhere else. */
.single-only { display: none; }
html[data-single] .single-only {
  display: flex; position: absolute; left: 0; right: 0; bottom: 0; z-index: 44;
}

.leader-count {
  position: absolute; left: 10px; bottom: 1.2dvh; z-index: 14;
  height: var(--corner); display: grid; place-items: center start;
  font-family: var(--mono); font-size: calc(var(--corner) * .42); font-weight: 700;
  line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -.02em;
  color: var(--fg);
}
/* THE LAST THREE SECONDS. A film leader tightens; this says the same thing
   without any new markup. */.leader-count[data-soon] { color: var(--kick-fg); }

/* On the title card the two controls are in the band, as they are on any
   cover — this page has no reel for the general rule to ask about. *//* WHILE THE CARD IS UP. It was `:has(.leader)`, and the card is not removed
   when it is dismissed — it is hidden — so the condition stayed true forever
   and the feed behind it had no full-screen switch at all. The card being
   PRESENT is not the card being SHOWN. */html:not([data-started]):has(.leader) .wide--mast,
html:not([data-started]):has(.leader) .sound--mast { display: inline-grid; }
html:not([data-started]):has(.leader) .wide--stage { display: none; }

/* ONE HEART ANIMATION, AND IT IS OURS.
   Their player throws a red-and-yellow ❤️ EMOJI at the body on every like —
   its own colour, its own size, its own easing, next to the one we draw. It
   carries no class, so it is matched by the only thing it does carry: the
   inline animation name their script writes into the style attribute. */div[style*="heartFloat"] { display: none !important; }

/* AND WHILE THE CARD IS UP, THE FEED'S CHROME DOES NOT EXIST.
   Stacking was not the answer and could not be: the rail, the counters, the
   byline and the keepers are drawn by the middle with z-indexes of their own,
   and beating all of them means picking a number higher than whichever one
   somebody adds next. A cover page has no rail, no view count and no headline
   from a post — not hidden behind something, not there. */html:not([data-started]):has(.leader) :is(.rail, .counts, .film-by, .acts-col, .sound--stage) {
  display: none;
}

/* ── THE ARCHIVE CALENDAR ───────────────────────────────────────────────────
   Seven columns, square cells, one grid line between them drawn as the grid's
   own background showing through a one-pixel gap — the same trick the split
   screens use, so there is one border and never two touching.

   A day that ran is a link and looks like one: it carries its issue number and
   what ran on it. A day that did not is the same square, unlit, and not
   clickable. The gaps are the point of the page. */.cal { margin-bottom: 22px; }
.cal-w {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px;
  margin-bottom: 1px;
  font-family: var(--mono); font-size: 8px; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase; color: var(--kick-fg);
}
.cal-w span { padding: 4px 6px; }
.cal-g {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px;
  background: var(--rule-color);
  border: 1px solid var(--rule-color);
}
.cal-d {
  position: relative; display: flex; flex-direction: column; gap: 2px;
  min-height: 62px; padding: 5px 6px; background: var(--paper-bg);
  color: var(--sub-fg); text-decoration: none;
}
.cal-d > b { font-family: var(--mono); font-size: 12px; font-weight: 700; line-height: 1;
  color: var(--counts-fg); }
.cal-pad { background: var(--depth-bg); }

/* THE DAYS THAT RAN. */.cal-on { background: var(--paper-bg); color: var(--fg); cursor: pointer; }
.cal-on > b { color: var(--fg); }
.cal-on::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--kick-fg);
}
.cal-on:hover { background: var(--depth-bg); }
.cal-on:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.cal-no { margin-top: auto; font-family: var(--mono); font-size: 8px; font-weight: 700;
  letter-spacing: .12em; color: var(--kick-fg); }
.cal-m { font-family: var(--mono); font-size: 8px; letter-spacing: .08em; color: var(--counts-fg); }

@media (max-width: 560px) {
  .cal-d { min-height: 46px; padding: 4px 4px; }
  .cal-d > b { font-size: 10px; }
  .cal-no, .cal-m { font-size: 7px; letter-spacing: .06em; }
}

.pagewin {
  position: absolute; inset: 0; z-index: 24; overflow-y: auto; overscroll-behavior: contain;
  background: var(--paper-bg); padding: 14px 16px 24px;
  /* what the head pulls back out to */
  --panel-pad: 16px;
}
.pagewin:empty { display: none; }
.page-x {
  position: sticky; top: 0; z-index: 3; float: right; width: 26px; height: 26px;
  display: grid; place-items: center; color: var(--reel-x-fg); font-size: 17px; line-height: 1;
}
.lede {
  font-family: var(--serif); font-size: 15px; line-height: 1.55;
  max-width: 58ch; color: var(--fg); margin: 0 0 12px;
}
.pagewin .band { display: flex; align-items: center; gap: 12px; margin: 18px 0 10px;
  font-family: var(--mono); font-size: 9px; letter-spacing: .32em;
  text-transform: uppercase; color: var(--kick-fg); }
.pagewin .band::after { content: ''; flex: 1; height: 1px; background: var(--rule-color); }
.pagewin .band:first-of-type { margin-top: 0; }

/* ── the auction ladder ─────────────────────────────────────────────────────
   Same furniture as the rest of the magazine: mono for every number, serif for
   every word, and the bar behind each row drawn to the size of the bid. */.codebar { border: 1px solid var(--rule-color); padding: 10px 12px; margin-bottom: 14px;}
.findform { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.findform label, .field label {
  display: block; font-family: var(--mono); font-size: 8px; font-weight: 700;
  letter-spacing: .24em; text-transform: uppercase; color: var(--kick-fg); margin-bottom: 4px;
}
.findform input, .field input {
  font-family: var(--mono); font-size: 12px; font-weight: 700; letter-spacing: .06em;
  background: var(--nl-field-bg); color: var(--nl-fg);
  border: 1px solid var(--nl-field-border); padding: 7px 9px; width: 100%;}
.findform input { min-width: 170px; }
.findform > form, .findform label + input { flex: 1; }
.field { margin-bottom: 8px; }
.two-up { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

.stats { display: flex; flex-wrap: wrap; gap: 1px; background: var(--rule-color);
  border: 1px solid var(--rule-color); margin-bottom: 12px; }
.stat { flex: 1 1 96px; background: var(--paper-bg); padding: 8px 10px;}
.stat b { display: block; font-family: var(--mono); font-size: 16px; color: var(--fg); }
.stat span { font-family: var(--mono); font-size: 7.5px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--kick-fg); }

.youare { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border: 2px solid var(--kick-fg); padding: 9px 11px; margin: 0 0 12px;
  font-family: var(--serif); font-size: 14px; color: var(--fg); }

.ladder { list-style: none; margin: 0; padding: 0; }
.tierband { display: flex; align-items: baseline; gap: 10px; padding: 14px 0 5px;
  font-family: var(--mono); font-size: 8.5px; letter-spacing: .26em; text-transform: uppercase;
  color: var(--kick-fg); border-bottom: 1px solid var(--rule-color); }
.tierband b { font-weight: 700; }
.tierband span { color: var(--counts-fg); }
.tierband.below { color: var(--counts-fg); }

.rung { position: relative; display: grid; gap: 10px; align-items: center;
  grid-template-columns: 30px 1fr 116px auto;
  padding: 9px 0; border-bottom: 1px solid var(--rule-color); }
.rung > * { position: relative; }
.rung .depth { position: absolute; left: 0; top: 0; bottom: 0;
  background: var(--depth-bg); border-right: 1px solid var(--depth-edge); }
.rung .folio { font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--kick-fg); }
.rung .what b { display: block; font-family: var(--serif); font-weight: 400;
  font-size: 14px; line-height: 1.2; color: var(--fg); }
.rung .who { display: block; margin-top: 2px; font-family: var(--mono); font-size: 8px;
  letter-spacing: .16em; text-transform: uppercase; color: var(--counts-fg); }
.rung .price { font-family: var(--mono); font-size: 12px; font-weight: 700;
  color: var(--fg); text-align: right; }
.rung .votes { font-family: var(--mono); font-size: 9px; color: var(--counts-fg); text-align: left; }
.rung small { display: block; font-weight: 400; font-size: 7.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--counts-fg); }
.rung.mine { box-shadow: inset 3px 0 0 var(--kick-fg); }
.rung.cut { opacity: .62; }

.btn.raise { padding: 5px 9px; font-size: 8.5px; background: transparent;
  color: var(--more-fg); border: var(--more-border);}
.btn.raise:hover { background: var(--more-bg-hover); color: var(--more-fg-hover); }
.btn.go { background: var(--nl-btn-bg); color: var(--nl-btn-fg); border: 0; }

.raise-form { border: 2px solid var(--kick-fg); padding: 12px; margin-bottom: 14px;
  background: var(--paper-bg);}
.raise-form h4 { margin: 0 0 10px; font-family: var(--serif); font-weight: 400; font-size: 15px; color: var(--fg); }
.raise-form .row { display: flex; gap: 8px; margin-top: 4px; }

.flash { border: 1px solid var(--kick-fg); padding: 9px 11px; margin-bottom: 12px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em; color: var(--kick-fg);}
.flash.bad { border-color: var(--flash-bad); color: var(--flash-bad); }
.flash.good b { font-size: 13px; }
.flash .code { display: block; margin-top: 6px; color: var(--fg); }
.flash .code b { letter-spacing: .2em; }

@media (max-width: 620px) {
  .rung { grid-template-columns: 26px 1fr auto; }
  .rung .price { grid-column: 2; text-align: left; }
  .two-up { grid-template-columns: 1fr; }
}

/* ── the archive: a run of days ─────────────────────────────────────────────── */.daylist { list-style: none; margin: 0 0 4px; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 4px; }
.daylist a { display: flex; align-items: baseline; gap: 7px;
  border: 1px solid var(--rule-color); padding: 7px 9px;}
.daylist a:hover { border-color: var(--kick-fg); background: var(--depth-bg); }
.daylist b { font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--fg); }
.daylist span { font-family: var(--mono); font-size: 8px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--counts-fg); }

/* ── the weekly cover: the daily furniture, driven by the week's numbers ───── */.weekcover { position: relative; aspect-ratio: 1 / 1.35; display: flex; flex-direction: column;
  overflow: hidden; margin-bottom: 16px;
  background: var(--paper-bg); border: 1px solid var(--rule-color); }
.weekcover .mast { padding: 10px 12px 0; }
.weekcover .lines { padding: 1.4dvh 12px 0; }
.weekcover .lead { width: 88%; padding: 0 12px 14px; }
.weekcover .line .t { text-shadow: var(--shadow-m); }
.weekcover .lead .t { font-size: clamp(17px, 2.6vw, 26px); }

.awards { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 8px; margin-bottom: 4px; }
.award { border: 1px solid var(--rule-color); padding: 11px;}
.award .cat { font-family: var(--mono); font-size: 8px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--kick-fg); }
.award h3 { margin: 7px 0 5px; font-family: var(--serif); font-weight: 400;
  font-size: 15px; line-height: 1.2; color: var(--fg); }
/* the switcher says when something is switched on */.switcher.changed .switcher-btn { background: var(--sw-on-bg); color: var(--sw-on-fg); }

/* ── the loader ─────────────────────────────────────────────────────────────
   CSS only, and there is nothing to dismiss: it sits *underneath* the picture.
   An image or a video is transparent until it has data, so the loader shows
   through; the moment the media paints it covers the loader completely. No
   script, no state, no class to remove, and nothing to leave stuck on screen
   if a file never arrives. */.loading {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-content: center; justify-items: center; gap: 12px;
  background: var(--paper-bg);
}
.loading .mark {
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .4em; text-transform: uppercase; color: var(--kick-fg);
}
/* The publisher's mark. It is drawn into the page rather than fetched, so it is
   a span round an svg where it used to be an img — and a span needs to be told
   it has a size. The circle is in the artwork; nothing here rounds anything. */.mark:has(svg) { display: inline-block; flex: none; line-height: 0; letter-spacing: 0; }
.mark > svg { display: block; width: 100%; height: 100%; }
.loading .bar { width: 150px; height: var(--bar-h); background: var(--bar-track); overflow: hidden; }
.loading .bar i { display: block; width: 38%; height: 100%; background: var(--bar-fill);
  animation: sweep 1.15s cubic-bezier(.65,0,.35,1) infinite; }
@keyframes sweep {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(375%); }
}
@media (prefers-reduced-motion: reduce) {
  .loading .bar i { animation: none; width: 100%; opacity: .5; }
  .loading { transition: none; }
}

/* ── the model: four bands, and what each one costs ────────────────────────── */.model { border: 1px solid var(--rule-color); padding: 12px 14px; margin-bottom: 14px; }
.model-head { font-family: var(--mono); font-size: 9px; letter-spacing: .26em;
  text-transform: uppercase; color: var(--kick-fg); margin-bottom: 10px; }
.bands { list-style: none; margin: 0; padding: 0; }
.bands li { display: grid; grid-template-columns: 62px 1fr 120px 150px; gap: 10px;
  align-items: baseline; padding: 7px 0; border-bottom: 1px solid var(--rule-color); }
.bands li:last-child { border-bottom: 0; }
.bands b { font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--kick-fg); }
.bands .what { font-family: var(--serif); font-size: 14px; color: var(--fg); }
.bands .cost, .bands .keep { font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--counts-fg); }
.bands .keep { color: var(--kick-fg); }
/* the further down the cover, the less of the bid is actually spent */.bands .band-video, .bands .band-full { opacity: 1; }
@media (max-width: 620px) {
  .bands li { grid-template-columns: 52px 1fr; }
  .bands .cost, .bands .keep { grid-column: 2; }
}

/* ── the auction, which is a magazine with a price on every position ────────
   Same cover, same reel, same list. The only thing a published issue does not
   have is this: what each position is holding, and a way to take it. It is the
   whole reason the page exists, so it is the loudest thing on every story. */.bidtag { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.bidtag b {
  font-family: var(--mono); font-size: 18px; font-weight: 700;
  letter-spacing: .04em; color: var(--fg); text-shadow: var(--shadow-m);
}
.col.right .bidtag { justify-content: flex-end; }

.btn.raise {
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  /* The loudest thing on a story wherever it sits. Most skins want their
     accent, which is what the kicker is set in — but a skin whose kicker is a
     quiet grey needs to say so, or the one button that takes money ends up the
     palest thing on the page. */
  background: var(--raise-bg, var(--kick-fg)); color: var(--raise-fg, var(--paper-bg));
  border: 0;
  padding: 11px 22px; cursor: pointer; white-space: nowrap;
}
.btn.raise:hover { filter: brightness(1.25); }
.btn.raise:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* THE COVER STORY'S PRICE AND BUTTON.

   These were set as though the quarter were a poster: twenty-four point on the
   price and a button 127 by 41. On a 900-tall desktop that left NINE pixels of
   slack under the standfirst, which is not a gap — it is a collision waiting
   for a headline one word longer, and it was reported as one three times.

   Smaller, and pinned to the floor with margin-top: auto, so the type above it
   keeps its own room and the button can never march up into it. */.lead .bidtag { flex: none; margin-top: auto; padding-top: 10px; }
.lead .bidtag b { font-size: 19px; }
.lead .btn.raise { font-size: 12px; padding: 10px 20px; }
/* On a phone that setting is 62px of button in a quarter 122px tall, and it
   printed over the standfirst above it. The 560px rule below could not reach
   it — `.lead .bidtag b` outranks `.bidtag b` — so the reach is here. */@media (max-height: 760px), (max-width: 560px) {
  .lead .bidtag { padding-top: 8px; gap: 8px; }
  .lead .bidtag b { font-size: 16px; }
  .lead .btn.raise { font-size: 11px; padding: 8px 15px; }
}
/* And a step further on a short screen, where a headline and a price and a
   button together still run past the floor of the quarter. Seven hundred, not
   620: the statistics bar grew ten pixels when its buttons were made big
   enough for a thumb, and the quarter is what paid for it. */@media (max-height: 700px) {
  .lead .t, .lead .t.long { -webkit-line-clamp: 2; }
  .lead .bidtag { padding-top: 6px; }
  .lead .bidtag b { font-size: 14px; }
  .lead .btn.raise { padding: 7px 13px; }
}

/* the split screens have less room, but it still has to be a real target */.duo .bidtag, .quad .bidtag { margin-top: 8px; gap: 8px; }
.duo .bidtag b { font-size: 15px; }
.quad .bidtag b { font-size: 13px; }
.duo .btn.raise { font-size: 11px; padding: 9px 16px; }
.quad .btn.raise { font-size: 10px; padding: 7px 12px; letter-spacing: .14em; }

@media (max-width: 560px) {
  .bidtag b { font-size: 15px; }
  .btn.raise { padding: 10px 16px; font-size: 11px; }
}

.raisewin { position: absolute; inset: 0; z-index: 26; display: grid; place-items: center;
  padding: 16px; background: rgba(0,0,0,.72); }
.raisewin:empty { display: none; }
.raisewin .raise-form { max-width: 420px; width: 100%; background: var(--paper-bg); margin: 0; }

/* an unsold position, which is still worth showing — it is the offer */.line.empty { cursor: default; }
.line.empty .t, .ttl.empty { opacity: .55; font-style: italic; }
/* THE COVERLINE'S TAG. Small enough that eight of them in a 168px column read
   as prices under headlines rather than as eight buttons with headlines
   attached. It wraps to two rows before it ever runs out of the column. */.line .bidtag.line-bid { display: flex; flex-wrap: wrap; align-items: center;
  gap: 4px 6px; margin-top: 5px; max-width: 100%; }
.line .bidtag.line-bid b { font-size: 12px; }
.line .bidtag.line-bid .btn.raise { font-size: 9px; letter-spacing: .1em;
  padding: 4px 8px; width: auto; flex: 0 1 auto; min-width: 0; }
.col.right .bidtag.line-bid { justify-content: flex-end; }

/* THE MARK NEEDS AIR. It was set hard against the first letter of whatever it
   stood in front of — a kicker, a byline, a section. */.mark.tiny, .mark.small { margin-right: 6px; }
.kick .mark, .byline .mark { margin-right: 6px; }

.bidtag.vacant b { color: var(--kick-fg); }
.bidtag.vacant .btn.raise { background: transparent; color: var(--kick-fg);
  border: 1px solid var(--kick-fg); }
.bidtag.vacant .btn.raise:hover { background: var(--kick-fg); color: var(--paper-bg); filter: none; }

/* ── the auction cover has to fit a price and a button under every coverline ─
   so the coverlines themselves give up a little room for them. */.cover.selling .line .t { -webkit-line-clamp: 2; }
.cover.selling .line.m .t { font-size: clamp(14px, 2.1dvh, 20px); }
.cover.selling .line .t { font-size: clamp(12px, 1.75dvh, 16px); }
.cover.selling .line.s .t { font-size: clamp(11px, 1.5dvh, 14px); }
.cover.selling .col { gap: .4dvh; }
.cover.selling .line .bidtag { margin-top: 4px; gap: 7px; }
.cover.selling .line .bidtag b { font-size: 13px; }
.cover.selling .line .btn.raise { font-size: 9.5px; padding: 6px 12px; letter-spacing: .14em; }
.cover.selling .lead .t { font-size: clamp(17px, 2.9dvh, 30px); }
.cover.selling .lead .bidtag { margin-top: 10px; }

/* the masthead is the way home, on every page */.mast-home { display: block; }
.mast-home:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* ── the weekly does not take votes: it reports where a story finished ────── */.ranktag { position: relative; display: inline-flex; align-items: baseline;
  gap: 7px; flex-wrap: wrap; border: 1px solid var(--rule-color); padding: 7px 12px; }
.ranktag b { font-family: var(--mono); font-size: 17px; font-weight: 700; color: var(--kick-fg); }
.ranktag > span { font-family: var(--mono); font-size: 8px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--counts-fg); }
.ranktag small { font-family: var(--mono); font-size: 9px; letter-spacing: .08em;
  color: var(--counts-fg); }
.quad .ranktag { padding: 5px 8px; gap: 5px; }
.quad .ranktag b { font-size: 13px; }
.quad .ranktag small { display: none; }

/* NO LOAD STATE. The magazine is drawn by the time it is on screen and there
   is nothing to fade in from — see the bootstrap in the page head. What is left
   here is the transition, for the one case that still moves: a skin swapped
   from the desk while somebody is looking at it. */.mag, .desk, .deskbrand, .switcher {
  transition: filter .5s cubic-bezier(.2,0,.2,1), opacity .4s ease;
}
@media (prefers-reduced-motion: reduce) {
  .mag, .desk, .deskbrand, .switcher { transition: opacity .2s ease; }
}}

/* ── picking a coverline ────────────────────────────────────────────────────
   Hovering one headline should say so. The line gets a ground to sit on and a
   rule in the skin's accent, and the others step back — the *text* steps back,
   never the picture behind it. Focus does the same, so a keyboard sees it too. */
.line .t, .lead .t, .lead .sub, .line .k, .lead .k { transition: opacity .18s ease; }

.lines:has(.line:hover) .line:not(:hover) .t,
.lines:has(.line:hover) .line:not(:hover) .k,
.lines:has(.line:focus-visible) .line:not(:focus-visible) .t,
.lines:has(.line:focus-visible) .line:not(:focus-visible) .k { opacity: .3; }

.paper:has(.lines .line:hover) .lead .t,
.paper:has(.lines .line:hover) .lead .sub,
.paper:has(.lines .line:hover) .lead .k { opacity: .3; }
.paper:has(.lead:hover) .lines .line .t,
.paper:has(.lead:hover) .lines .line .k { opacity: .3; }

.line:hover .t, .line:focus-visible .t,
.lead:hover .t, .lead:focus-visible .t {
  background: var(--hover-plate); color: var(--hover-ink);
  box-shadow: -6px 0 0 var(--hover-plate), 6px 0 0 var(--hover-plate);
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  text-shadow: none; text-decoration: none;
}
.line:hover .k, .line:focus-visible .k,
.lead:hover .k, .lead:focus-visible .k { color: var(--hover-rule); opacity: 1; }

/* a rule marking the one you are on */.line, .lead { position: relative; }
.line::before, .lead::before {
  content: ''; position: absolute; top: 0; bottom: 0; width: 3px;
  background: var(--hover-rule); opacity: 0; transition: opacity .18s ease;
}
.line::before, .lead::before { left: -10px; }
.col.right .line::before, .lead.right::before { left: auto; right: -10px; }
.line:hover::before, .line:focus-visible::before,
.lead:hover::before, .lead:focus-visible::before { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .line .t, .lead .t, .lead .sub, .line .k, .lead .k,
  .line::before, .lead::before { transition: none; }
}
/* a touch screen has no hover, and a sticky :hover state there is worse than none */@media (hover: none) {
  .lines:has(.line:hover) .line:not(:hover) .t,
  .lines:has(.line:hover) .line:not(:hover) .k { opacity: 1; }
  .line:hover .t, .lead:hover .t { background: none; color: var(--c); box-shadow: none; }
}

/* ── a row in the list ─────────────────────────────────────────────────────
   The same story a third time: what it did, what you can do, and a way in. */.rowbody { min-width: 0; }
.feedrow .byline { margin: 3px 0 0; font-family: var(--mono); font-size: 8.5px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--kick-fg); }
.rowfoot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.rowstat { font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--counts-fg); }
.rowstat b { font-size: 11px; color: var(--fg); }
.rowfoot .more { padding: 5px 10px; font-size: 9px; letter-spacing: .16em; }
.rowfoot .score .vote { padding: 4px 8px; }
.rowfoot .score .n { min-width: 36px; font-size: 11px; }
.rowfoot .ranktag { padding: 4px 9px; }
.rowfoot .ranktag b { font-size: 13px; }
.rowfoot .ranktag small { display: none; }
@media (max-width: 560px) { .rowfoot { gap: 7px; } .rowstat { font-size: 8px; } }

/* ── the encore: the winner again, and where the issue leads next ───────────
   Three quarters to the story that was paid for, one quarter to the week and
   to everything ever published. *//* ── THE BACK COVER ─────────────────────────────────────────────────────────

   Four bands down the page: the first story at half of it, the thin way out of
   the issue at the fold, the other five in the spiral, and tomorrow along the
   bottom. The strip is BETWEEN the first and the rest rather than after
   everything, because that is where somebody stops reading and decides whether
   to go on.

   No buttons and no counters anywhere on it. The back cover is a record of
   what the issue was, not a set of things to do with it. */.screen.on.encore { display: grid; grid-template-rows: 1fr auto 1fr auto; }

/* THE SPIRAL. Halve the space; halve what is left; halve what is left of that;
   cut the last piece in two. Written as areas on a four-by-four grid, so the
   division is one readable block instead of five sets of percentages:

     b b e d      b  the left half of what the first left
     b b f d      c  the bottom half of what b left
     b b c c      d  the right half of what c left
     b b c c      e, f  the last piece, cut in two            */.bc-grid {
  display: grid; min-height: 0; gap: 1px;
  background: var(--split-rule-color, var(--rule-color));
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(4, 1fr);
  grid-template-areas:
    "b b e d"
    "b b f d"
    "b b c c"
    "b b c c";
}
.bc-b { grid-area: b; } .bc-c { grid-area: c; }
.bc-d { grid-area: d; } .bc-e { grid-area: e; } .bc-f { grid-area: f; }

.bc-cell { position: relative; overflow: hidden; display: flex; flex-direction: column;
  min-width: 0; min-height: 0; }
.bc-cell > .bg { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%;
  object-fit: cover; filter: var(--art-filter); }
.bc-cell > .veil { position: absolute; inset: 0; z-index: 2; background: var(--overlay); }
.bc-cell .body { position: relative; z-index: 10; margin-top: auto; padding: 8px 11px 10px; }
.bc-first { border-bottom: var(--split-rule); }
.bc-first .body { padding: 10px 14px 14px; }
.bc-first .ttl { font-size: clamp(19px, 3.1dvh, 32px); }

/* SHOWN WHOLE, AND SHOWN SHORT. The first three keep their kicker, their
   headline and their standfirst; the last three are a quarter-panel each, and
   a standfirst in a quarter-panel is four lines of five words. */.bc-cell.whole .ttl { font-size: clamp(14px, 2.1dvh, 21px); line-height: 1.14;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
.bc-cell.whole .sum { font-size: clamp(11px, 1.5dvh, 13px);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
/* THE LAST THREE ARE PICTURES. A quarter-panel is not a place to read, so
   what is left on them is the photograph and a headline small enough to be a
   caption on it — no kicker, no standfirst, no line of figures. */.bc-cell.brief .body { padding: 5px 7px 6px; }
.bc-cell.brief .kick { display: none; }
.bc-cell.brief .ttl { font-size: clamp(8px, 1.05dvh, 10px); line-height: 1.2;
  letter-spacing: .01em; font-weight: 600;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }

/* The fold, and the foot. */.bc-list {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 14px; text-decoration: none;
  background: var(--raise-bg, var(--kick-fg)); color: var(--raise-fg, var(--paper-bg));
  border-block: var(--split-rule);
}
.bc-list-t { font-family: var(--sans); font-size: 11px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; }
.bc-list-a { font-size: 14px; line-height: 1; animation: va-nudge 2.4s ease-in-out infinite; }
.bc-list:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
@media (prefers-reduced-motion: reduce) { .bc-list-a { animation: none; } }

.bc-out { display: flex; flex-direction: column; justify-content: center; gap: 4px;
  padding: 11px 14px; text-decoration: none; background: var(--paper-bg);
  border-top: var(--split-rule); }
.bc-out:hover { background: var(--depth-bg); }
.bc-out-k { font-family: var(--mono); font-size: 8px; font-weight: 700; letter-spacing: .26em;
  text-transform: uppercase; color: var(--kick-fg); }
.bc-out b { font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(17px, 2.6dvh, 26px); letter-spacing: var(--lead-track); color: var(--fg);
  line-height: 1.08; }
.bc-out-s { font-family: var(--serif); font-style: italic; font-size: 12px; line-height: 1.35;
  color: var(--sub-fg); }
.encore .story-part { position: relative; overflow: hidden; display: flex; flex-direction: column; }
.encore .story-part > .bg { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%;
  object-fit: cover; filter: var(--art-filter); }
.encore .story-part > .veil { position: absolute; inset: 0; z-index: 2; background: var(--overlay); }
.encore .body { position: relative; z-index: 10; margin-top: auto; padding: 0 14px 3dvh; }
.encore .ttl { font-size: clamp(19px, 3.1dvh, 32px); }

/* min-width as well as min-height, and for the same reason: a grid item also
   defaults to auto, so without it the slot below pushed this — and the whole
   half-screen with it — wider than the magazine. */.encore-foot { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
.promos { flex: 1; min-height: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--split-rule-color, var(--rule-color)); border-top: var(--split-rule); }

/* The letter runs the full width under both, because it is a field and a
   button and needs the room that two columns would take away from it. *//* min-width: 0, and it matters. A flex item defaults to min-width: auto, so
   the slot inside was allowed to push this wider than the frame — 736px inside
   a 680px magazine, bleeding out of the page and making the same newsletter a
   different width here than in the other two places it appears. */.encore-nl { flex: none; min-width: 0; width: 100%;
  display: flex; align-items: center; justify-content: center;
  padding: 4px; background: var(--chrome); border-top: var(--split-rule); }
/* Set at the foot of the block, the way everything else on this site is set
   over a picture. Centred was right when the row was a quarter of the screen;
   in half of one it left two titles floating in the middle of nothing. */.promo { display: flex; flex-direction: column; justify-content: flex-end; gap: 5px;
  padding: 14px; background: var(--paper-bg); }
.promo:hover { background: var(--depth-bg); }
.promo-k { font-family: var(--mono); font-size: 8px; font-weight: 700; letter-spacing: .26em;
  text-transform: uppercase; color: var(--kick-fg); }
.promo b { font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(17px, 2.6dvh, 26px); letter-spacing: var(--lead-track); color: var(--fg);
  line-height: 1.08; }
.promo-s { font-family: var(--serif); font-style: italic; font-size: 12px; line-height: 1.35;
  color: var(--sub-fg); }
/* It used to be dropped on a narrow screen because the row was a quarter tall
   and there was nowhere to put it. There is now. */@media (max-height: 560px) { .promo-s { display: none; } }

/* ── the foot of the page ───────────────────────────────────────────────────
   Taller than a status strip, because it now carries the ticket count, who is
   reading, and the two things you can do with the page itself. */.statbar { min-height: var(--statbar-h); align-items: stretch; padding: 3px; gap: 3px;
  overflow: hidden; }
/* The burger and the two actions are pinned; the figures between them scroll.
   Nothing is dropped and nothing is cut in half — if the frame is too narrow
   for every figure you push them sideways and they are all still there. */.statbar-mid {
  flex: 1 1 auto; min-width: 0; display: flex; gap: 3px;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  overscroll-behavior-x: contain; scroll-snap-type: x proximity;
}
.statbar-mid::-webkit-scrollbar { display: none; }
.statbar-mid > * { scroll-snap-align: start; }
.statbar > .burger, .statbar > .act { flex: none; }
/* a hint that there is more, on whichever side there is more of it */.statbar-mid { mask-image: linear-gradient(90deg, transparent 0, #000 6px,
  #000 calc(100% - 6px), transparent 100%); }
.statbar .cell { flex: none; display: flex; align-items: center; gap: 0; padding: 0 10px;
  white-space: nowrap; }
.statbar .cell b { font-size: 11px; }
/* Icons, not words — but they are the two things you can do with the page, so
   they carry the accent rather than sitting in the chrome's own grey where
   nobody finds them. */.statbar .act {
  display: grid; place-items: center; width: 46px; padding: 0;
  border: var(--cell-border); background: var(--chrome); color: var(--cell-fg);
  cursor: pointer;
}
.statbar .act svg { width: 22px; height: 22px; display: block; }
.statbar .act + .act { margin-left: 1px; }
.statbar .act:hover { background: var(--menu-hover-bg); color: var(--menu-hover-fg); }
.statbar .act:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (max-width: 560px) { .statbar .act { width: 40px; } .statbar .act svg { width: 19px; height: 19px; } }
.statbar .act:hover { background: var(--menu-hover-bg); color: var(--menu-hover-fg); }
.statbar .tickets { cursor: pointer; }
.statbar .tickets:hover { background: var(--menu-hover-bg); color: var(--menu-hover-fg); }
.statbar .tickets b { color: var(--led); }
.statbar .tickets:hover b { color: inherit; }
.statbar .live .dot {
  width: 6px; height: 6px; margin-right: 7px; background: var(--live-dot);
  animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .statbar .live .dot { animation: none; } }
/* the bar drops what it cannot fit rather than squeezing every cell until the
   words are cut in half *//* below these the figures are dropped outright rather than left half-cut *//* READING NOW STAYS. Two more rules hid it — one on the container, one on the
   viewport — so on a phone the only number on the page that is actually live
   was the one that never showed. The strip scrolls; nothing has to be dropped
   to make room. */@media (max-width: 640px) {
  .statbar .cell { padding: 0 7px; font-size: 8.5px; }
}

/* ── the drawer ─────────────────────────────────────────────────────────────
   Share and comment come up from the foot rather than opening a page. *//* HALF THE PAGE, over everything below it — the ad row, the statistics bar and
   the menu included. At 62% of the stage it stopped above the chrome, which
   made it look bolted onto the furniture instead of drawn over the page. *//* ═══ THE STACK ═══════════════════════════════════════════════════════════
   This is a mobile design: things come up over other things, and the order
   they come up in has to be one order, decided once.
   
     70   share and comment      what you asked for last is on top
     62   the newsletter         it comes in with the menu
     60   the rail               over the artwork, under anything opened
     46   the statistics bar     the furniture
     45   the menu               in the bar's own row
     26   the raise window
     20   the reel
   
   The newsletter used to sit at 62 and the drawer at 55, so opening share with
   the menu open left the newsletter lying across it. Anything opened on
   purpose goes over anything that was already there. */.drawer {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 70;
  /* THE WHOLE MIDDLE, ON EVERY SITE. It was half the frame — which is what a
     sheet on a photograph wants, and wrong for everything that has been put in
     one since: a comment thread, a share code somebody points a second phone
     at, a list. Half a frame means a scroller inside a sheet inside a page,
     and two scrollbars on top of each other is nobody's design.

     So it stops where the middle stops: under the marquee, and at the very top
     when the marquee is gone, which is what full screen is. The height is what
     is left between those two edges rather than a percentage — a percentage of
     the frame does not know where the furniture is. */
  top: var(--ticker-h); height: auto; max-height: none;
  display: flex; flex-direction: column;
  background: var(--paper-bg); border-top: var(--drawer-rule);
  box-shadow: 0 -18px 40px rgba(0,0,0,.35);
  /* One property moves: the sheet comes up from the floor and goes back down.
     No fade, no scale, nothing behind it dimming. */
  /* THE SHEET STARTS ON THE FLOOR and is brought up by a class. It used to
     rely on @starting-style to animate out of display:none, which Safari only
     learned in 17.5 — so on a phone it did not slide, it appeared. One class,
     one property, and every browser can do it. */
  transform: translateY(100%); transition: transform .3s cubic-bezier(.2,0,.2,1);
  will-change: transform;
}
.drawer.open { transform: translateY(0); }
/* Full screen takes the marquee away, so the sheet takes its twenty pixels. */
html[data-wide] .drawer { top: 0; }

/* THE SHARE SHEET DOES NOT SCROLL. Its height is a definite number, and inside
   it the code takes everything the fixed rows below do not: the headline, the
   copy line and the strip are `flex: none` and the code is what is left. It
   stays square by taking its width from its height, so it can never push
   anything off the bottom — there is nothing to scroll to, ever. *//* The sheet is exactly as tall as what is in it. Nothing is cut off and there
   is nothing to scroll to. */.drawer:has(.sharewin) { height: auto; max-height: none; }
/* AND IT SITS IN THE MIDDLE OF WHAT IT WAS GIVEN. The sheet is the height of
   the middle now, and the code, the address and the strip are the same size
   they always were — so they are centred in it rather than stacked against the
   top with a third of a screen of nothing underneath. */
.drawer:has(.sharewin) .drawer-body {
  overflow: hidden; display: flex; flex-direction: column; justify-content: center;
  min-height: 0;
  padding: 0;                    /* the code goes to the edge; the rest pads itself */
}
.drawer[hidden] { display: none; }
.drawer-bar {
  flex: none; display: flex; align-items: center; justify-content: space-between;
  padding: 11px 14px; border-bottom: 1px solid var(--rule-color);
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .26em; text-transform: uppercase; color: var(--kick-fg);
}
.drawer-x { font-size: 15px; line-height: 1; color: var(--fg); cursor: pointer; }
.drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: 18px 14px 24px; }
.drawer-body .note { max-width: 52ch; }
/* THE SLIDE IS THE DRAWER. Killing the transition under reduced motion made it
   appear from nowhere on any phone with battery saver on — the fifth thing that
   rule was switching off, after the marquee, the story bar, the heart and the
   sense that anything was moving at all. It slides, faster. */@media (prefers-reduced-motion: reduce) { /* ═══ THE STACK ═══════════════════════════════════════════════════════════
   This is a mobile design: things come up over other things, and the order
   they come up in has to be one order, decided once.
   
     70   share and comment      what you asked for last is on top
     62   the newsletter         it comes in with the menu
     60   the rail               over the artwork, under anything opened
     46   the statistics bar     the furniture
     45   the menu               in the bar's own row
     26   the raise window
     20   the reel
   
   The newsletter used to sit at 62 and the drawer at 55, so opening share with
   the menu open left the newsletter lying across it. Anything opened on
   purpose goes over anything that was already there. */
.drawer { transition-duration: .12s; } }

/* The one way out of the encore that is not one of its four promos. */.tolist {
  flex: none;
  position: relative; z-index: 14; display: flex; align-items: center;
  justify-content: space-between; gap: 10px;
  padding: 9px 12px; text-decoration: none; border-bottom: var(--split-rule);
  background: var(--raise-bg, var(--kick-fg)); color: var(--raise-fg, var(--paper-bg));
}
.tolist-t { font-family: var(--sans); font-size: 12px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; }
.tolist-a { font-size: 15px; line-height: 1; animation: va-nudge 2.4s ease-in-out infinite; }
.tolist:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .tolist-a { animation: none; } }

/* BELOW THE FOLD. ONE LINE EACH.

   It was the published row's layout with the pictures taken out, which stacks
   a title over a byline over a footer — three rows of furniture for a position
   that did not run. These are a waiting list: a number, who, and what they are
   holding, on one line, quiet, with no accent colour on them. The magazine is
   above; this is the queue.

   Nothing is said about what happens to the money here. The rules page is
   where that belongs, not twenty-five repetitions of it down a list. *//* `.bt`, not `.ttl` — the third class collision in this file. A story screen's
   `.ttl` is set at clamp(…, 3dvh, …), which came out at 24px inside a row 40px
   tall. Every size here is stated, and none of it is inherited from a screen. */.belowrow {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px; border-bottom: 1px solid var(--rule-faint, rgba(128,128,128,.18));
  font-family: var(--sans); font-size: 11px; line-height: 1.25; color: var(--sub-fg);
}
.belowrow .no { flex: none; width: 20px; font-family: var(--mono); font-size: 10px;
  font-weight: 700; color: var(--sub-fg); opacity: .7; }
.belowrow .mark { flex: none; width: 12px; height: 12px; }
.belowrow .bt { flex: 1 1 auto; min-width: 0; font-size: 11.5px; font-weight: 500;
  line-height: 1.25; color: var(--sub-fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* `.by`, not `.who` — the weekly's ladder already owns `.who` and its rule
   threw this one 2,779 pixels off the top of the row. */.belowrow .by { flex: none; font-family: var(--mono); font-size: 9px;
  letter-spacing: .06em; opacity: .6; }
.belowrow .held { flex: none; font-family: var(--mono); font-size: 11px; font-weight: 700;
  color: var(--counts-strong, currentColor); }
.belowrow .btn.raise { flex: none; font-size: 8.5px; padding: 3px 8px; letter-spacing: .1em; }
@media (max-width: 480px) { .belowrow .by { display: none; } }






/* THE FOURTH THING prefers-reduced-motion was switching off, after the
   marquee, the story bar and — via those — the sense that the page was alive
   at all. `display: none` meant a phone with battery saver on gave no answer
   when you pressed the heart: no hearts, nothing. It is the whole feedback of
   the button. It rises faster now, and it still rises. */@media (prefers-reduced-motion: reduce) { .pop { animation-duration: .5s; } }



/* ── the LED screen ─────────────────────────────────────────────────────────
   Every picture and every video is shown as if it were on a panel: a fine dot
   grid laid over the media and blended with `screen`, which only ever adds
   light. Nothing is darkened, which is the point — the picture is what was
   paid for. Pitch, colour and strength are skin tokens, so a skin can make it
   coarse and loud or almost invisible.

   Pseudo-elements, so it costs no markup at all. */.cover::after,
.screen.story::after,
.encore .bc-cell::after,
.duo .half::after,
.quad .cell::after {
  content: ''; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  /* Two grids, one light and one dark, offset by half a step. A single light
     grid disappears the moment the picture behind it is bright; a pair reads on
     anything, and because the two cancel each other the picture is no darker
     overall than it was. That is the whole trick. */
  background-image:
    radial-gradient(var(--led-hi) 1px, transparent 1.2px),
    radial-gradient(var(--led-lo) 1px, transparent 1.2px);
  background-position: 0 0, calc(var(--led-size) / 2) calc(var(--led-size) / 2);
  background-size: var(--led-size) var(--led-size);
  opacity: calc(var(--led-opacity) * var(--led-scale, 1));
  mix-blend-mode: var(--led-blend);
}
/* a screen made of two or four panels gets a finer pitch, or it reads as noise */.duo .half::after, .quad .cell::after {
  background-size: var(--led-size-sm) var(--led-size-sm);
  background-position: 0 0, calc(var(--led-size-sm) / 2) calc(var(--led-size-sm) / 2);
}

/* ── the publisher's mark ───────────────────────────────────────────────────
   The one round thing on the page. Everything else here is square on purpose,
   but a mark of identity is a circle everywhere on the internet and reads wrong
   as anything else. Same file at every size. */.mark {
  display: inline-block; vertical-align: -3px; margin-right: 6px;
  width: 18px; height: 18px; border-radius: 50%;
  box-shadow: 0 0 0 1px var(--mark-ring, rgba(0,0,0,.25));
}
.mark.tiny { width: 12px; height: 12px; vertical-align: -2px; margin-right: 5px; }
.mark.small { width: 15px; height: 15px; vertical-align: -3px; }
.quad .mark { width: 13px; height: 13px; vertical-align: -2px; margin-right: 4px; }
/* #01 is the position everyone paid for, so its mark is a real one — a block
   of its own above the kicker rather than a favicon wedged into a line of type,
   and it sits on whichever edge the cover story is set against. */.mark.lead-mark {
  display: block; width: 46px; height: 46px; margin: 0 0 10px;
  vertical-align: baseline;
  box-shadow: 0 0 0 2px var(--mark-ring, rgba(0,0,0,.25)), 0 2px 14px rgba(0,0,0,.55);
}
.lead.right .mark.lead-mark { margin-left: auto; }
@media (max-width: 560px) { .mark.lead-mark { width: 36px; height: 36px; margin-bottom: 8px; } }


/* ── the sheet ───────────────────────────────────────────────────────────────
   The one screen that asks for something back. It is raised by an upvote and
   never by a heart: an upvote is a ticket in the draw and a ticket has to have
   somewhere to go, while a heart costs nothing and wins nothing.

   It lives inside the frame, over the stage, so it never covers the chrome. */.sheet {
  position: absolute; inset: 0; z-index: 70;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: var(--sheet-veil, rgba(0,0,0,.72));
  animation: sheet-in .2s ease both;
}
.sheet[hidden] { display: none; }
.sheet.closing { animation: sheet-out .22s ease both; }
@keyframes sheet-in  { from { opacity: 0 } to { opacity: 1 } }
@keyframes sheet-out { to { opacity: 0 } }

.sheet-in {
  position: relative; width: min(400px, 100%); max-height: 100%; overflow-y: auto;
  padding: 26px 24px 22px;
  background: var(--paper-bg); color: var(--fg); border: var(--sheet-border, 2px solid var(--fg));
  animation: sheet-rise .26s cubic-bezier(.2,0,.2,1) both;
}
@keyframes sheet-rise { from { transform: translateY(14px) } to { transform: none } }
.sheet-x {
  position: absolute; top: 8px; right: 10px;
  font-size: 15px; line-height: 1; color: var(--fg); cursor: pointer; padding: 4px;
}
.sheet-k {
  font-family: var(--mono); font-size: 9px; font-weight: 700; letter-spacing: .16em;
  text-transform: uppercase; color: var(--counts-fg); margin: 0 0 10px;
}
.sheet-k b { color: var(--accent, var(--fg)); }
.sheet-h { font-family: var(--display, var(--serif)); font-size: 27px; line-height: 1.05;
  margin: 0 0 10px; letter-spacing: -.01em; }
.sheet-p { font-size: 13px; line-height: 1.5; margin: 0 0 16px; max-width: 40ch; }
.sheet-fine { font-family: var(--mono); font-size: 9px; line-height: 1.6;
  color: var(--counts-fg); margin: 12px 0 0; }

.sheet-form { display: flex; gap: 6px; flex-wrap: wrap; }
.sheet-form input {
  flex: 1 1 180px; min-width: 0; padding: 11px 12px;
  font-family: var(--mono); font-size: 12px;
  color: var(--fg); background: var(--field-bg, transparent);
  border: var(--more-border); outline-offset: -2px;
}
.sheet-form input::placeholder { color: var(--counts-fg); }
.sheet-form .btn {
  flex: none; cursor: pointer; padding: 11px 15px;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: var(--more-track); text-transform: uppercase; line-height: 1;
  background: var(--nl-btn-bg); color: var(--nl-btn-fg); border: 0;
}
.sheet-form .btn:hover { filter: brightness(1.15); }
.sheet-form .btn:focus-visible, .sheet-form input:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px;
}
.sheet-form.busy { opacity: .55; pointer-events: none; }
.sheet-alt { font-family: var(--mono); font-size: 10px; color: var(--counts-fg); margin: 14px 0 0; }
.lnk { cursor: pointer; color: var(--fg); text-decoration: underline;
  text-underline-offset: 3px; font: inherit; }
.sheet-err {
  margin: 14px 0 0; padding: 9px 11px;
  font-family: var(--mono); font-size: 10px; line-height: 1.5;
  color: var(--pending-fg); background: var(--pending-bg);
}
.sheet-err[hidden] { display: none; }

@media (max-width: 460px) {
  .sheet { padding: 12px; }
  .sheet-in { padding: 22px 18px 18px; }
  .sheet-h { font-size: 23px; }
}

/* ── signed in, or not ───────────────────────────────────────────────────────
   Set by the head script before the first paint, so neither state ever flashes.
   The markup is identical either way — that is the whole point. */[data-auth] [data-anon] { display: none; }
html:not([data-auth]) [data-known] { display: none; }

/* ── where the magic link lands ────────────────────────────────────────────
   A page of its own, because it is the one address that arrives from outside.
   Still a static file: the token is in the query string and the exchange runs
   in the browser, so nothing here needed a server. */.plainpage { min-height: 100dvh; display: flex; background: var(--desk-bg, var(--bg)); }
.enter { flex: 1; display: flex; align-items: center; justify-content: center; padding: 24px; }
.enter-in {
  width: min(420px, 100%); padding: 30px 26px 24px;
  background: var(--paper-bg); color: var(--fg); border: var(--sheet-border, 2px solid var(--fg));
}
.enter-code {
  display: flex; flex-direction: column; gap: 5px; margin: 18px 0 0; padding: 12px 13px;
  border: var(--more-border);
}
.enter-code[hidden] { display: none; }
.enter-code span { font-family: var(--mono); font-size: 9px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--counts-fg); }
.enter-code b { font-family: var(--mono); font-size: 17px; letter-spacing: .08em; }

/* ── the screen stack ────────────────────────────────────────────────────────
   Screens come in over the magazine from the right and go back out the same
   way. The frame, the marquee and the statistics bar never move — what changes
   is what is on top of them. That is the difference between a stack and a site.

   Above everything (the rail is 60, the sheet 70) because a screen is the
   thing being looked at while it is up. */.stack { position: absolute; inset: 0; z-index: 80; pointer-events: none; }
.stack[hidden] { display: none; }

.screenpanel {
  position: absolute; inset: 0; pointer-events: auto;
  display: flex; flex-direction: column;
  background: var(--paper-bg); color: var(--fg);
  transform: translateX(0); opacity: 1;
  transition: transform .3s cubic-bezier(.2,0,.2,1), opacity .3s ease;
}
.screenpanel.entering, .screenpanel.leaving { transform: translateX(100%); opacity: .4; }

.panelbar {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 9px 10px; border-bottom: 1px solid var(--rule-color);
  background: var(--paper-bg);
}
.panelback, .panelx {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; cursor: pointer; color: var(--fg);
}
.panelback svg { width: 20px; height: 20px; }
.panelx { font-size: 14px; line-height: 1; }
.paneltitle {
  flex: 1; min-width: 0; text-align: center;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.panelbody { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }

/* ── a screen that is a form ───────────────────────────────────────────────── */.screenform, .screendone { padding: 18px 16px 32px; }
.screen-lead { font-size: 13px; line-height: 1.55; margin: 0 0 20px; max-width: 46ch;
  color: var(--fg); }
.screen-lead b { font-family: var(--mono); }

.fld { display: block; margin: 0 0 18px; }
.fld-k {
  display: block; margin: 0 0 6px;
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--counts-fg);
}
.fld-k i { font-style: normal; color: var(--accent, var(--fg)); margin-left: 4px; }
.fld-k i.tier {
  font-family: var(--mono); font-size: 8px; letter-spacing: .1em;
  color: var(--counts-fg); margin-left: 6px; text-transform: uppercase;
}
.fld input, .fld textarea, .screenform input, .screenform textarea {
  display: block; width: 100%; padding: 11px 12px;
  font-family: var(--mono); font-size: 12px; line-height: 1.45;
  color: var(--fg); background: var(--field-bg, transparent);
  border: var(--more-border); outline-offset: -2px; resize: vertical;
}
.fld input::placeholder, .fld textarea::placeholder { color: var(--counts-fg); }
.fld input:focus-visible, .fld textarea:focus-visible { outline: 2px solid var(--focus); }
.fld-h { display: block; margin: 6px 0 0; font-family: var(--mono);
  font-size: 9px; line-height: 1.6; color: var(--counts-fg); }
.fld.locked input { opacity: .45; cursor: not-allowed; }

/* Categories as squares you can see at once, rather than a wheel. */.cats { border: 0; padding: 0; margin: 0 0 20px; }
.catgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 6px; }
.cat { position: relative; display: block; padding: 10px 11px; cursor: pointer;
  border: var(--more-border); }
.cat input { position: absolute; opacity: 0; pointer-events: none; }
.cat-l { display: block; font-family: var(--mono); font-size: 11px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; }
.cat-b { display: block; margin-top: 3px; font-family: var(--mono); font-size: 8.5px;
  line-height: 1.5; color: var(--counts-fg); }
.cat:hover { background: var(--more-bg-hover); }
.cat:has(input:checked) { background: var(--voted-bg); color: var(--voted-fg); }
.cat:has(input:checked) .cat-b { color: inherit; opacity: .75; }
.cat:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }

.screenfoot { margin-top: 24px; padding-top: 18px; border-top: 1px solid var(--rule-color); }
.screenfoot .btn.go {
  display: block; width: 100%; padding: 14px; cursor: pointer; margin-bottom: 10px;
  font-family: var(--mono); font-size: 11px; font-weight: 700;
  letter-spacing: var(--more-track); text-transform: uppercase;
  background: var(--nl-btn-bg); color: var(--nl-btn-fg); border: 0;
}
.screenform.busy { opacity: .55; pointer-events: none; }

/* ── what you are given afterwards ─────────────────────────────────────────── */.screendone .enter-code { margin: 18px 0; }
.screendone .enter-code b { font-size: 11px; letter-spacing: .02em; word-break: break-all;
  line-height: 1.5; }
.keyacts { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.keyacts .btn.go {
  padding: 11px 15px; cursor: pointer; border: 0;
  font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: var(--more-track); text-transform: uppercase;
  background: var(--nl-btn-bg); color: var(--nl-btn-fg);
}
.sheet-form.keeper { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--rule-color); }


/* ── the ad row belongs to the cover ─────────────────────────────────────────
   A story is a full screen, and a banner across the foot of one is somebody
   else's magazine. So the slot is up on the cover and gone everywhere else —
   the reel, All News, a menu page, a pushed screen. `data-cover` is set on
   <html> by whatever changed what is showing.

   The row goes entirely rather than being emptied. An empty band between the
   story and the statistics bar is worse than either — it reads as something
   that failed to load. The statistics bar does move up by the height of it,
   and that is fine: the move follows a click, which is the one kind of shift
   that is a transition rather than instability. */html:not([data-cover]) .adbar { display: none; }
/* NOT ON THE BACK COVER, THOUGH — AND IT HAS TO SAY SO OUT LOUD.
   Taking the rule away was not enough: the line above only hides the row on a
   page that has no cover, and the magazine HAS one, so the row was showing on
   every screen by default and the back cover with it. The back cover now ends
   in TOMORROW of its own, and the ad row rotates a newsletter through it —
   that is the same two things twice, one under the other. */html:has(.encore.on) .adbar { display: none; }
/* And on the two breaks. They are our pages, not the day's — the slot belongs
   at the foot of them the same way it belongs at the foot of the cover, and
   because the letter rides in its rotation neither the break nor the back cover
   has to carry one of its own. */html:has(.moment.on) .adbar { display: flex; }
/* And on the list, which is a page rather than a screen — the same chrome the
   cover has. */html:has(.allnews:not([hidden])) .adbar { display: flex; }

/* ── the newsletter, which is the menu's and nothing else's ──────────────────
   It used to be one of the ads on a rotation. It is not an ad. It comes in
   with the menu, sits in the ad row — empty on a story, worth interrupting on
   the cover — and leaves when the menu does. */.nlpop {
  position: absolute; left: 0; right: 0; bottom: var(--statbar-h, 40px); z-index: 62;
  display: flex; align-items: center; justify-content: center;
  padding: 4px; background: var(--chrome); border-top: var(--chrome-rule);
  animation: nlpop-in .26s cubic-bezier(.2,0,.2,1) both;
}
.nlpop[hidden] { display: none; }
.nlpop.going { animation: nlpop-out .24s ease both; }
@keyframes nlpop-in  { from { transform: translateY(100%); opacity: 0 } }
@keyframes nlpop-out { to   { transform: translateY(100%); opacity: 0 } }
@media (prefers-reduced-motion: reduce) { .nlpop, .nlpop.going { animation: none; } }

/* Both of these are the ad slot's box, exactly: same width, same height, same
   type inside. Somebody who has seen it on the cover should not have to work
   out that this is the same thing. *//* The letter is the same object in the same box — one slot, filled, so the two
   things that ever appear in that row are the same size and neither of them
   floats. */:is(.nlpop, .encore-nl) .subscribe {
  width: 100%; height: var(--slot-h); padding: 0 12px;
}
/* Same geometry as the ad row it stands in for: a label's width of chrome on
   the left, then the slot. *//* The letter, once it has been left somewhere. */.subscribe .lbl.done { max-width: none; font-style: normal; }
.subscribe input[aria-invalid="true"] { outline: 2px solid var(--focus); }

/* ── /post.html ──────────────────────────────────────────────────────────────
   A page rather than a screen, because a post carries a picture and a video
   and an upload wants somewhere to navigate to. It borrows the panel's bar so
   it still reads as part of the magazine rather than a form somewhere else. */.postpage .mag { display: flex; flex-direction: column; }
.postpage .panelbar { flex: none; }
.postpage .panelbody { flex: 1; min-height: 0; overflow-y: auto; }
.postpage .panelback, .postpage .panelx { text-decoration: none; }

/* What a coupon came back with. Green is not in any skin here, so acceptance
   reads as the skin's own accent rather than a colour borrowed from nowhere. */.sheet-err.ok { color: var(--voted-fg); background: var(--voted-bg); }

/* Shut until a coupon opens it. */.fld.locked input { opacity: .45; cursor: not-allowed; }
.fld:not(.locked) .fld-k i.tier { color: var(--accent, var(--fg)); }


/* ── scrollbars ──────────────────────────────────────────────────────────────
   One treatment, everywhere. There were two — All News and the menu pages —
   and everything added since had whatever the browser felt like: the drawer,
   the sheet, the post page, the page itself. A scrollbar is chrome, and chrome
   that changes between two panes of the same frame is worse than no styling at
   all, because it looks like a mistake rather than a default.

   Square, like everything else. The track shows through a two-pixel border on
   the thumb rather than a radius, which is what gives it room without rounding
   anything. */* {
  scrollbar-width: thin;
  scrollbar-color: var(--scroll-thumb) var(--scroll-track, transparent);
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--scroll-track, transparent); }
::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border: 2px solid var(--scroll-track, transparent);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover, var(--scroll-thumb)); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: var(--scroll-track, transparent); }

/* The two that are deliberately invisible stay that way: both are horizontal
   strips of chrome where a bar would sit on top of the content. */.menu, .statbar-mid { scrollbar-width: none; }
.menu::-webkit-scrollbar, .statbar-mid::-webkit-scrollbar { display: none; }

/* ── the live moment ─────────────────────────────────────────────────────────
   The screen that breaks the reel after the fourth story. Not a story — every-
   thing the site is that this issue is not.

   Four layouts, one picked at random per load. The PROPORTIONS are the same in
   all four — all time takes half the screen, the week a quarter, the rest share
   what is left — so what matters never moves even though the furniture does. *//* No `position` here. `.screen` is already absolute with inset:0 — declaring
   `relative` took that away, the screen collapsed to the height of its content,
   and its only child is absolutely positioned, so the whole thing measured
   zero and painted nothing. */.screen.on.moment { display: block; padding: 0; }
/* Hugging the edges, and over the tiles rather than under them — the mosaic is
   inside the grid, which paints after. *//* Wide enough for a thumb. They were twenty-six pixels — technically present
   and impossible to hit — so this screen was the one place in the reel you
   could not move forward or back by tapping. *//* Over the mosaic, not beside it. Narrow enough that the tiles are still
   usable, wide enough for a thumb. */.moment .tap { width: 46px; z-index: 41; }
.moment .tap.next { width: 46px; }
/* Clear the rail and the close button at the top — a tile that starts at y=0
   sits under both of them. *//* Pinned, not sized in per cent. A percentage height needs a parent with a
   definite one, and a screen inside the deck is sized by its content — so the
   grid only filled as much as its tiles asked for and left the rest of the
   screen empty. *//* Edge to edge. It was inset 62px on each side to leave room for the tap zones
   and it read as a card floating in the middle of a screen — the tap zones can
   sit ON it instead, which is what they do everywhere else in the reel. */.moment .m-grid {
  position: absolute; z-index: 20;
  top: calc(var(--rail-h) + 30px); left: 0; right: 0; bottom: 0;
  height: auto; width: auto;
}

/* This screen has six things on it, not one headline, so it is given longer
   before the reel moves on. */html:has(.screen.on.moment) .rail .bar.live i { animation-duration: var(--dwell-long, 26s); }
/* THE MOVING COVER RUNS FOR THIRTY SECONDS. Fifteen is what a picture gets. */html:has(.screen.on[data-hold="30"]) .rail .bar.live i,
html:has(.screen.on[data-hold="30"]) .loading .bar i { animation-duration: var(--dwell-video, 30s); }
.moment .m-grid {
  display: grid; gap: 1px;
  grid-template-columns: repeat(4, 1fr);
  background: var(--split-rule-color, var(--rule-color));
}
/* space-between, so a tile is used rather than occupied: the eyebrow rides the
   top of its box and the substance sits on the floor of it. Centred or
   bottom-pinned, every tile had a band of nothing in it and the screen read as
   five small cards adrift on a large one. */.m-tile {
  min-width: 0; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column; justify-content: space-between; gap: 5px;
  padding: 14px 16px; background: var(--paper-bg);
}
/* Only the big one is set at the foot of its block, the way a story is over a
   picture. The small tiles are centred: bottom-pinning one line inside a tall
   empty box reads as a gap rather than as a decision. *//* Five tiles, four arrangements, and the arrangements are not free — each tile
   declares its span in story.mjs (MOMENT_SPANS) and every layout here honours
   it. The rule that costs something: a `wide` tile gets a ROW OF ITS OWN, full
   width and one row tall, in all four. The letter is one, because a field and a
   button need the run — boxed into a quarter they wrap and stop reading as a
   form at all.

/* Six boxes: one big and five small, laid out like a board rather than a page.
   Which arrangement, and which panel gets the big box, differ between the two
   breaks and again per build — the pair is never the same twice in a reading. *//* The stat rows are sized to their contents and the big box takes what is left.
   Six equal rows gave every number a hundred and fifty pixels to sit in and
   each one used a third of it — the boxes were not too empty, the rows were too
   tall. */.moment[data-mosaic="1"] .m-grid {
  grid-template-rows: 1fr auto auto auto;
  grid-template-areas: "h h h h" "b b l l" "v v n n" "o o o o"; }
.moment[data-mosaic="2"] .m-grid {
  grid-template-rows: auto auto auto 1fr;
  grid-template-areas: "b b l l" "v v n n" "o o o o" "h h h h"; }
.moment[data-mosaic="3"] .m-grid {
  grid-template-rows: auto auto auto 1fr;
  grid-template-areas: "h h b b" "h h l l" "h h v v" "h h n n"; }
.moment[data-mosaic="4"] .m-grid {
  grid-template-rows: auto auto auto 1fr;
  grid-template-areas: "b b h h" "l l h h" "v v h h" "n n h h"; }
/* Two of the four put the big box in a column, and there the fifth stat joins
   the stack rather than taking a band of its own. */.moment[data-mosaic="3"] .m-open, .moment[data-mosaic="4"] .m-open { display: none; }
.m-hero { grid-area: h; } .m-buyer { grid-area: b; } .m-live { grid-area: l; }
.m-visitors { grid-area: v; } .m-letters { grid-area: n; } .m-open { grid-area: o; }

/* A stat box reads as one thing, so its three parts sit together in the middle
   of the box rather than being pushed to its corners. space-between is right for
   a tile with a headline in it and wrong for a tile with a number in it — spread
   across 180 pixels the label, the figure and the note stop looking related. */.m-tile.stat { justify-content: center; gap: 3px; }
.m-tile.stat .m-k { opacity: .85; }
.m-tile.stat .m-s2 { line-height: 1.3; }
.m-fig { font-family: var(--mono); font-size: clamp(20px, 3.4dvh, 34px); font-weight: 700;
  line-height: 1; color: var(--fg); letter-spacing: -.02em; }
.m-tile.big { justify-content: space-between; }
.m-tile.big .m-h { font-size: clamp(19px, 3.2dvh, 30px); margin-top: auto; }

.m-tile.link a { display: flex; flex-direction: column; gap: 4px; height: 100%;
  justify-content: space-between; text-decoration: none; }
.m-link { font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(14px, 2.1dvh, 20px); letter-spacing: var(--lead-track); color: var(--fg); }
.m-tile.link:hover { background: var(--depth-bg); }

.m-k { font-family: var(--mono); font-size: 8.5px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--kick-fg); }
.m-h { font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(20px, 4.4dvh, 40px); line-height: 1.02;
  letter-spacing: var(--lead-track); color: var(--fg); margin: 0; }
.m-h2 { font-family: var(--display); font-weight: var(--lead-weight);
  font-size: clamp(14px, 2.2dvh, 21px); line-height: 1.08;
  letter-spacing: var(--lead-track); color: var(--fg); margin: 0; }
.m-s { font-family: var(--serif); font-style: italic; font-size: 12.5px;
  line-height: 1.35; color: var(--sub-fg); margin: 0; }
.m-s2 { font-family: var(--mono); font-size: 9px; color: var(--counts-fg); line-height: 1.4; }
.m-by { font-family: var(--mono); font-size: 9px; color: var(--counts-fg); }
.m-none { font-family: var(--serif); font-style: italic; font-size: 12px;
  line-height: 1.35; color: var(--sub-fg); }
.m-none a { color: var(--fg); }
.m-go { font-family: var(--mono); font-size: 8.5px; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--fg);
  text-decoration: underline; text-underline-offset: 3px; }
.m-money { font-family: var(--mono); font-size: clamp(19px, 3.2dvh, 30px);
  font-weight: 700; line-height: 1; color: var(--fg); }
.m-live { font-family: var(--mono); font-size: clamp(17px, 2.8dvh, 26px);
  font-weight: 700; line-height: 1; color: var(--fg); }

/* the last bid is the one thing here that is happening right now, so it is the
   one thing that gets a ground of its own */.m-tile.push { background: var(--depth-bg); }
/* A tile with nothing in it centres what little it has, rather than pinning one
   line to the floor and leaving a void above it. */.m-figs { font-family: var(--mono); font-size: 9.5px; color: var(--counts-fg); }
.m-figs b { color: var(--fg); }

.m-btn {
  display: block; text-align: center; padding: 9px 8px; margin-top: auto;
  border: var(--more-border); background: var(--more-bg); color: var(--more-fg);
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: var(--more-track); text-transform: uppercase;
}
.m-btn:hover { background: var(--more-bg-hover); color: var(--more-fg-hover); }
.m-btn.ghost { border-style: dashed; }
.m-letter { justify-content: stretch; padding: 0; }
.m-letter .subscribe { flex: 1; min-width: 0; height: auto; padding: 10px 12px;
  border: 0; gap: 6px; }
.m-letter .subscribe input { min-width: 0; }
.m-letter .subscribe button { flex: none; white-space: nowrap; }
.m-letter .subscribe .lbl { max-width: 96px; font-size: 8.5px; }

/* Narrow: four columns is too many for a phone, so the same six tiles run in
   two — and the mosaics still differ, they just differ vertically. */@media (max-width: 560px) {
  .moment .m-grid { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(6, 1fr); }
  /* Same rule at two columns: the wide tile keeps a whole row to itself. */
  .moment .m-grid { grid-template-columns: repeat(2, 1fr); }
  .moment[data-mosaic="1"] .m-grid { grid-template-rows: 1fr auto auto auto;
    grid-template-areas: "h h" "b l" "v n" "o o"; }
  .moment[data-mosaic="2"] .m-grid { grid-template-rows: auto auto auto 1fr;
    grid-template-areas: "b l" "v n" "o o" "h h"; }
  .moment[data-mosaic="3"] .m-grid { grid-template-rows: 1fr auto auto auto;
    grid-template-areas: "h h" "b l" "o o" "v n"; }
  .moment[data-mosaic="4"] .m-grid { grid-template-rows: auto auto 1fr auto;
    grid-template-areas: "b l" "v n" "h h" "o o"; }
  .moment[data-mosaic="3"] .m-open, .moment[data-mosaic="4"] .m-open { display: flex; }
  .m-tile { padding: 8px 10px; gap: 3px; }
  .m-letter .subscribe .lbl { display: none; }
}

/* ── the encore's four ─────────────────────────────────────────────────────── */.promos.four { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.promos.four .promo { padding: 10px 12px; gap: 3px; }
.promos.four .promo b { font-size: clamp(14px, 2dvh, 19px); }
.promos.four .promo-s { font-size: 10.5px; }
.promo.push { background: var(--depth-bg); }
@media (max-height: 620px) { .promos.four .promo-s { display: none; } }


/* An open position is quieter than a bought one, not invisible. It was pale
   enough that a board with twenty-two of them read as a grey page. */.line.empty .t { opacity: .82; }
.line.empty .k { opacity: .9; }


/* ── the grid over the artwork, turned down or off ───────────────────────────
   Every skin sets its own --led-opacity; this scales it. A multiplier rather
   than a replacement, so a skin that wants a heavier grain than another still
   gets one at every setting — the control changes the amount, never the
   relationship between the skins. */:root { --led-scale: 1; }
html[data-led="off"]    { --led-scale: 0; }
html[data-led="faint"]  { --led-scale: .45; }
html[data-led="normal"] { --led-scale: 1; }
html[data-led="strong"] { --led-scale: 1.9; }





/* The break screen carries an advertisement, in the same 320x50 box the ad row
   uses — one slot size on the site, wherever it appears. */.m-tile.slot { flex-direction: row; align-items: center; justify-content: flex-start; gap: 6px;
  padding: 4px 8px; background: var(--chrome); }
.m-tile.slot .banner { flex: 1; min-width: 0; height: var(--slot-h); }
.m-tile.slot .ad-lbl { flex: none; }


/* ── the eight, scattered ────────────────────────────────────────────────────
   Two columns is the structure; where a headline sits inside its column is not.
   Each one carries its own indent, width and side, seeded off its id in
   story.mjs so a cover holds still between builds but no two covers set the
   same way. Eight headlines in two tidy columns read as a table. */.lines .line { width: 100%; max-width: 100%; }
/* Aligned outward — the left column reads left, the right column reads right,
   which is what gives the old cover its edge-to-edge tension. */.lines .col.right .line { text-align: right; }
.lines .col:not(.right) .line { text-align: left; }
/* Only the air varies. */.lines .line.d1 { margin-top: 1dvh; }
.lines .line.d2 { margin-top: 2dvh; }
.lines .line.d3 { margin-top: 3dvh; }

/* ── the guide ───────────────────────────────────────────────────────────────
   Off by default. On, it draws the two-thirds line so the split can be checked
   without measuring it — and a second rule where the mark's column begins,
   because that is the other edge the cover is composed against. */html[data-guides="on"] .cover .lines { box-shadow: inset 0 -1px 0 0 var(--focus); }
html[data-guides="on"] .cover .lead::before {
  content: ''; position: absolute; top: 0; bottom: 0; right: 112px;
  border-left: 1px dashed var(--focus); opacity: .7; pointer-events: none;
}


/* ── the square above the mark ───────────────────────────────────────────────
   The mark opens the issue and takes the bottom right. Directly above it the
   cover leaves a square the same width — a slot for a logo, sold or ours. *//* One column in the bottom right: the square sits on the mark and is exactly
   as wide as it. `align-items: stretch` is what makes that true by construction
   — a hardcoded width was a guess, and the guess was wrong the moment the
   mark's label changed length. *//* ONE NUMBER sizes the corner, and everything in the quarter reads it — the
   square, the button under it, and the headline that has to keep clear of both.
   It is declared on .cover, above all three; see the clamp there.

   Three attempts at making one measure the other were all circular — the square
   has an aspect-ratio and no intrinsic width, so it contributes nothing to a
   measurement and whatever stretched to fill the column became the column. It
   kept landing at 314. Declaring it once ends the argument. */.cover-foot { flex-direction: column; align-items: flex-end;
  width: var(--corner); gap: 6px; }
/* A CIRCLE, of the same diameter the square had. The one shape in this
   magazine that is allowed a radius is a mark of identity, and that is what
   this is — a publisher's mark, in the corner of the cover. *//* THE MARK IS A BACKGROUND, not an image element. A background that has not
   arrived is simply not painted — there is no torn-photograph icon, no alt
   text, nothing to cover up. An <img> in the corner of the cover was showing
   both on every hard refresh. */.cover-badge {
  display: block; width: var(--corner); height: var(--corner); overflow: hidden;
  border-radius: 50%;
  /* Its own ground, not the chrome's. The theta is a white glyph on
     transparency, so on a cream strip it was simply not there. */
  background: var(--badge-bg, #101010); border: var(--more-border);

  /* AFTER the shorthand, not before it. `background:` resets background-image
     and background-size to their initial values, so declaring the mark above it
     set it and then threw it away — the corner drew its own colour and nothing
     else. */
  background-image: var(--brand-mark, none);
  background-size: 62% 62%; background-position: 50% 50%; background-repeat: no-repeat;
}
.cover-badge img { width: 100%; height: 100%; object-fit: contain; padding: 12px;
  display: block; }
/* Below this the quarter cannot hold the square and the button both, and the
   button is the one that has to stay. */@media (max-height: 620px) { .cover-badge { display: none; } }

/* ── the ribbon ──────────────────────────────────────────────────────────────
   A section says what it is here, where the cover says its issue number — one
   band under the masthead, never both. The masthead itself never changes.

   Each section has its own: the same slot, four different treatments, because
   four bands that differ only in their wording are four pages that look the
   same. */.ribbon {
  position: relative; z-index: 11; flex: none;
  margin: 4px 0 0; padding: 2px 8px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  white-space: nowrap; overflow: hidden; line-height: 1.3;
}
.ribbon b {
  font-family: var(--mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .32em; text-transform: uppercase; white-space: nowrap;
}
.ribbon span {
  font-family: var(--mono); font-size: 8.5px; letter-spacing: .12em;
  text-transform: uppercase; opacity: .78; text-align: right;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* TOMORROW — the one that is happening. Solid, in the colour that takes money,
   with a rule under it like a countdown line. */.ribbon--tomorrow {
  background: var(--raise-bg, var(--kick-fg)); color: var(--raise-fg, var(--paper-bg));
  border-bottom: 2px solid var(--fg);
}

/* THE WEEKLY — editorial. Ruled above and below, ink on the page, nothing
   filled: it is a summary, not an event. */.ribbon--theweekly {
  background: transparent; color: var(--fg);
  border-top: 1px solid var(--fg); border-bottom: 1px solid var(--fg);
  box-shadow: 0 3px 0 -1px var(--fg);
}
.ribbon--theweekly b { font-family: var(--display); letter-spacing: .2em; font-size: 11px; }

/* ALL TIME — the archive. Reversed, the heaviest of the four, because it is the
   one that covers everything. */.ribbon--alltime {
  background: var(--fg); color: var(--paper-bg);
}
.ribbon--alltime b { letter-spacing: .42em; }
.ribbon--alltime span { opacity: .62; }

/* EXAMPLE — not a real issue, and it says so: dashed, unfilled, quiet. */.ribbon--example {
  background: transparent; color: var(--counts-fg);
  border: 1px dashed var(--counts-fg);
}
.ribbon--example b { font-weight: 400; letter-spacing: .28em; }


/* ── AN IMAGE THAT HAS NOT ARRIVED YET ────────────────────────────────────
   It used to be an empty box with a broken-image mark and the word 0.sh in it,
   which on a hard refresh is what the corner of the cover looked like for as
   long as the fetch took. Every image now sits on a moving band of its own
   paper until it has something to draw, and an image that is deferred also
   hides its alt text — the text is for a reader who cannot see the picture, not
   for one who is waiting half a second for it.

   CSS only. Nothing here needs a load event, a class or a line of script. */@keyframes skeleton { to { background-position: -200% 0; } }

img {
  position: relative;                       /* so the cover below has something to sit in */
  background-color: var(--skeleton, rgba(128,128,128,.14));
}

/* THE TRICK, AND IT IS THE WHOLE SOLUTION.
   
   A pseudo-element on an <img> is not rendered while the image is — a replaced
   element has no room for one. The moment the picture is missing, broken, or
   simply has not arrived, the box stops being replaced and the pseudo-element
   appears. So this is a skeleton that shows itself exactly when there is
   nothing to show, and takes itself away the instant there is, with no load
   event, no class and no line of script.
   
   It covers the browser's own broken-picture mark and the alt text with it,
   which is what the corner of the cover looked like on a hard refresh: a torn
   photograph icon and the word 0.sh. */img::after {
  content: "";
  position: absolute; inset: 0; z-index: 1;   /* over the browser's own broken mark */
  background-color: var(--skeleton, rgba(128,128,128,.14));
  background-image: linear-gradient(100deg,
    transparent 18%, var(--skeleton-sheen, rgba(255,255,255,.12)) 38%, transparent 58%);
  background-size: 220% 100%;
  background-repeat: no-repeat;
  animation: skeleton 1.3s linear infinite;
}
/* A deferred image has no src at all yet; hide the alt text it would print. */img[data-src] { color: transparent; font-size: 0; }
@media (prefers-reduced-motion: reduce) { img::after { animation: none; } }

/* ── the two that are not a link ──────────────────────────────────────────
   Handing the address to the device's own sheet, and making a picture of the
   story to post. Half each — and when the device has no share sheet of its own
   the second takes the row, rather than sitting beside a gap. */.shbig { flex: none; display: grid; grid-template-columns: 1fr; gap: 8px;
  padding: 0 12px 12px; }
html[data-can-share] .shbig { grid-template-columns: 1fr 1fr; }
.shbig .native { display: none; }
html[data-can-share] .shbig .native { display: flex; }

.shbig-b {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; min-height: 62px; padding: 10px 8px;
  background: none; border: var(--more-border); color: var(--fg); cursor: pointer;
  font-family: var(--sans);
}
.shbig-i { width: 20px; height: 20px; color: var(--kick-fg); }
.shbig-i svg { width: 100%; height: 100%; display: block; }
.shbig-t { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }
.shbig-s { font-size: 8.5px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--sub-fg); }
.shbig-b:hover { background: var(--rowhover, rgba(128,128,128,.08)); }
.shbig-b:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* THE INSTAGRAM ONE WEARS INSTAGRAM'S COLOURS — the gradient, on the mark and
   on the rule around it, so nobody has to read the label to know where the
   picture is going. Square, like everything else here: the gradient is the
   signal, not the corners. */.shbig-b.insta {
  border: 0;
  background:
    linear-gradient(var(--paper-bg), var(--paper-bg)) padding-box,
    linear-gradient(115deg, #feda75, #fa7e1e 26%, #d62976 54%, #962fbf 78%, #4f5bd5) border-box;
  border: 1px solid transparent;
}
.shbig-b.insta .shbig-i {
  color: #d62976;
  background: linear-gradient(115deg, #feda75, #fa7e1e 26%, #d62976 54%, #962fbf 78%, #4f5bd5);
  -webkit-background-clip: text; background-clip: text;
}
.shbig-b.insta .shbig-i svg { stroke: #d62976; }
@supports (background-clip: text) {
  .shbig-b.insta .shbig-t {
    background: linear-gradient(115deg, #fa7e1e, #d62976 55%, #962fbf);
    -webkit-background-clip: text; background-clip: text; color: transparent;
  }
}
.shbig-b.insta[aria-disabled="true"] { cursor: default; }
.shbig-b.insta[aria-disabled="true"]:hover { background:
    linear-gradient(var(--paper-bg), var(--paper-bg)) padding-box,
    linear-gradient(115deg, #feda75, #fa7e1e 26%, #d62976 54%, #962fbf 78%, #4f5bd5) border-box; }

/* ── the lead screen: three quarters the post, one quarter its figures ─────
   What /26/08/2026/kodi opens on. Somebody followed a headline, so the
   headline is what is there — no cover in front of it — and the numbers that
   sit in a corner chip on every other screen get the bottom quarter to
   themselves, because on the one screen that is ABOUT a post they are the
   point rather than a footnote. */.screen.lead-story { flex-direction: column; }
.screen.on.lead-story { display: flex; }
/* THE PICTURE TAKES MORE OF THE SCREEN. It was three parts to the foot's one,
   which left the headline a fifth of the way up with a band of figures under
   it; the film or the photograph is the thing somebody came for, and the
   figures are a footnote to it. Four to one, and the headline sits down where
   it does on every other screen. */.ls-art { position: relative; flex: 4; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
.ls-art > .bg { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%;
  object-fit: cover; filter: var(--art-filter); }
.ls-art > .veil { position: absolute; inset: 0; z-index: 2; background: var(--overlay); }
.ls-art .body { position: relative; z-index: 10; margin-top: auto; padding: 0 12px 10px; }
/* No chip in the corner: the figures are downstairs. */.lead-story .counts { display: none; }

/* WAITING FOR THE RIGHT ISSUE. The furniture stays — the marquee, the
   masthead, the chrome — and the middle is a quiet field until it holds what
   the address asked for. Nothing false is ever on screen, and nothing jumps
   when the true thing arrives. */html[data-await] .stage > * { visibility: hidden; }

/* AND IT IS THE WHOLE SCREEN THAT WAITS.
   The curtain was over the stage alone. Hiding the advertisement inside the
   bar was not enough and could not be: the bar itself is still a band of
   chrome across the foot, so there was always a strip at the bottom that was
   not waiting — the same complaint, three times, and each time I covered one
   more thing inside it instead of the thing itself.

   Waiting is a state of the page. The marquee goes, the bar goes, and the
   stage takes the whole frame — exactly what full screen does, for exactly the
   same reason: there is one thing worth looking at and this is not the moment
   for anything around it. They come back with the issue. */
/* NO — THE FRAME IS THE FRAME AND IT DOES NOT WAIT.
   That comment above is wrong and this is what replaced it. The marquee, the
   advertisement row and the bar along the foot are STATIC components: they are
   the same on every address, they are already correct before anything is
   fetched, and taking them away while the middle loads makes the whole page
   flash rather than the one part of it that is actually changing.
   Only the middle waits, because only the middle is unknown.

   The one thing that does go: the newsletter. It belongs to the menu, and the
   moment there is nothing to read is not the moment to ask somebody for their
   address. `visibility` on the slide, so the row it rides in does not resize. */
html[data-await] .adslide[data-newsletter] { visibility: hidden; }
html[data-await] .nlpop { display: none; }

/* THE RING. A skeleton sheen says "something is coming"; it does not say the
   page is still working, and on a slow fetch the difference matters. Round,
   because a spinner is a spinner — the one shape in this design system that is
   allowed a radius, along with an avatar and a badge. */html[data-await] .stage::before {
  content: ""; position: absolute; z-index: 6; left: 50%; top: 50%;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border: 2px solid var(--spin-track, rgba(128,128,128,.32));
  border-top-color: var(--spin-lead, var(--kick-fg, #fff));
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  html[data-await] .stage::before { animation-duration: 2.4s; }
}

html[data-await] .stage::after {
  content: ""; position: absolute; inset: 0; z-index: 5;
  background-color: var(--skeleton, rgba(128,128,128,.14));
  background-image: linear-gradient(100deg,
    transparent 18%, var(--skeleton-sheen, rgba(255,255,255,.12)) 38%, transparent 58%);
  background-size: 220% 100%; background-repeat: no-repeat;
  animation: skeleton 1.3s linear infinite;
}
@media (prefers-reduced-motion: reduce) { html[data-await] .stage::after { animation: none; } }

/* THE RAIL IS NOT SHOWN OVER THE LEAD. The player starts when this screen
   finishes — the segments belong to the issue, and the issue has not begun.
   What this screen has is its own bar, running the thirty seconds it holds for,
   whether it is a film or a picture and whether it is #01 or #25. */html:has(.screen.on[data-lead]) .rail { display: none; }

/* …and in its place, the lead's own clock: one bar across the top, running the
   thirty seconds this screen holds for. The loader everywhere else is an
   indeterminate slider, which says "something is happening" and not "this long
   is left" — and this screen has to say the second, because it IS the wait. */.screen[data-lead] > .loading {
  position: absolute; top: 0; left: 0; right: 0; z-index: 60;
  display: flex; align-items: center; padding: 7px 8px;
  height: var(--rail-h); background: var(--rail-ground);
}
@supports (mix-blend-mode: difference) {
  .screen[data-lead] > .loading { background: none; mix-blend-mode: difference; }
  .screen[data-lead] > .loading .bar { background: rgba(255,255,255,.34); }
  .screen[data-lead] > .loading .bar i { background: #fff; }
}
.screen[data-lead] > .loading .bar { flex: 1; width: auto; height: var(--bar-h); }
.screen.on[data-lead] > .loading .bar i {
  width: 0; animation: fill var(--dwell-video, 30s) linear forwards;
}
@media (max-width: 560px) {
  .screen[data-lead] > .loading { height: calc(var(--rail-h) + 4px); padding: 8px 7px; }
  .screen[data-lead] > .loading .bar { height: calc(var(--bar-h) + 1px); }
}

/* The bottom quarter, in two bands. */.ls-foot {
  position: relative; z-index: 12; flex: 0 0 auto; min-height: 0;
  display: grid; grid-template-rows: auto 1fr;
  gap: 1px; background: var(--split-rule-color, var(--rule-color));
  border-top: var(--split-rule);
}
.ls-all {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 14px; background: var(--raise-bg, var(--kick-fg));
  color: var(--raise-fg, var(--paper-bg)); text-decoration: none;
}
.ls-all-t { font-family: var(--sans); font-size: 12px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; }
.ls-all-a { font-size: 15px; line-height: 1; animation: va-nudge 2.4s ease-in-out infinite; }
.ls-all:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
@media (prefers-reduced-motion: reduce) { .ls-all-a { animation: none; } }

.ls-figures {
  position: relative; z-index: 12;
  min-height: 0; display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 1px; background: var(--split-rule-color, var(--rule-color));
}
.ls-fig {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 8px 6px; background: var(--paper-bg); min-width: 0;
}
.ls-fig b { font-family: var(--mono); font-size: clamp(18px, 3.4dvh, 34px); font-weight: 700;
  line-height: 1; color: var(--fg); letter-spacing: -.02em; }
.ls-fig span { font-family: var(--sans); font-size: 9px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--kick-fg); }

/* ── the share sheet ──────────────────────────────────────────────────────
   It shares the SCREEN, not the site: on a story it is that story's address,
   and the code beside it opens the same thing on a phone that is not this one. *//* THE CODE GETS THE WIDTH. It is the thing somebody points a second phone at,
   and at 112px in a corner it was an illustration of a QR code rather than one
   you could actually read across a table. Everything else lines up under it,
   and the sheet is as tall as it needs to be for the copy line and the strip.

   AND IT IS CENTRED IN WHAT IT IS GIVEN. The sheet is the height of the middle
   now, and the code, the address and the strip are the sizes they always were:
   stacked from the top they left a third of a screen of nothing between the
   code and the copy line. `justify-content` on the column that holds them —
   not on the body around it, which they already fill. */.sharewin { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  justify-content: center; align-items: stretch; padding: 0; }
/* THE CODE IS A SQUARE OF THE WIDTH, unless the frame is too short to hold one
   — and then it is a square of what height there is. Both limits in one
   min(), so it is a square by arithmetic rather than by a flexbox that
   stretched it to 406 by 529.
   
   250px is what sits under it, measured: the bar at 40, the body's padding at
   26, the kicker and its margins at 24, two lines of headline at 46, the copy
   line at 33 and the strip at 64, with a little to spare. *//* Edge to edge, and a square. 226px is what sits under it, measured: the bar at
   40, the copy line at 33, the strip at 64 and the two big buttons at 76, with
   their padding — so on a frame too short to hold a full-width square, the
   square is what gives and nothing below it is ever cut off.

   THE THIRD LIMIT IS NEW, and the sheet growing to the height of the middle is
   what made it necessary: with that much room there is height for a code the
   size of a page, and a QR the size of a page is not more readable than one
   the size of a hand — it is only the sole thing on the screen. 380px is about
   as large as a phone camera can use from across a table. */.qr { flex: none; width: min(100%, 380px, calc(96dvh - 226px)); aspect-ratio: 1;
  margin: 0 auto; background: #fff; padding: 0; }
.qr svg { width: 100%; height: 100%; display: block; }
.sh-k, .sh-t { flex: none; }
.qr svg { display: block; width: 100%; height: 100%; }
.sh-k { margin: 10px 0 4px; font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase; color: var(--kick-fg); }
.sh-t { margin: 0 0 10px; font-family: var(--display); font-size: 15px; line-height: 1.2;
  color: var(--fg); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; }
.sh-copy { flex: none; display: flex; align-items: center; gap: 8px; width: calc(100% - 24px);
  margin: 12px 12px 0; padding: 8px 10px;
  background: none; border: var(--more-border); color: var(--fg); cursor: pointer; text-align: left; }
.sh-copy .sh-i { flex: none; width: 15px; height: 15px; }
.sh-copy .sh-i svg { width: 100%; height: 100%; display: block; }
.sh-u { flex: 1; min-width: 0; font-family: var(--mono); font-size: 10px; color: var(--sub-fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sh-copy b { flex: none; font-family: var(--sans); font-size: 10px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--kick-fg); }
.sh-copy.done b { color: var(--fg); }

/* One row, and it scrolls. Seventeen places to put something is a strip, not a
   grid of buttons nobody reads to the end of. */.shstrip { flex: none; display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none;
  padding: 10px 12px 12px; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; }
.shstrip::-webkit-scrollbar { display: none; }
.sh { flex: none; width: 62px; display: flex; flex-direction: column; align-items: center;
  gap: 6px; padding: 9px 4px; text-decoration: none; color: var(--fg);
  border: var(--more-border); scroll-snap-align: start; }
.sh-i { width: 20px; height: 20px; color: var(--kick-fg); }
.sh .sh-i svg { width: 100%; height: 100%; display: block; }
.sh-n { font-family: var(--sans); font-size: 8.5px; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--sub-fg); white-space: nowrap; }
.sh:hover { background: var(--rowhover, rgba(128,128,128,.08)); }
.sh:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Type where a link would have been. It should not invite a click it cannot
   answer — no underline on hover, no pointer, and the same colour it always
   had, because an unheld position is still a headline on the page. */.nolink { cursor: default; text-decoration: none; }
.feedrow h3 .nolink, .half .ttl .nolink { color: inherit; }

/* ── SAVE ───────────────────────────────────────────────────────────────────
   The bookmark under the heart. Same corner, same size, same drop shadow, and
   it reads itself off the picture exactly as the heart and the mute do.

   HIDDEN UNLESS THE PAGE SAYS `data-saves`. A site that does not want
   bookmarks does not carry a dead button, and the rule is one line rather
   than a flag threaded through every template. */html:not([data-saves]) .save,
html:not([data-saves]) [data-drawer="saved"] { display: none; }

/* ── THE WHOLE SCREEN ───────────────────────────────────────────────────────
   ON THE SAME LINE AS THE FIGURES, TO THE LEFT OF THEM, and small.

   It hung underneath them, at the size of the heart and the bookmark, which
   made it a third thing in a column of controls — but it is not a control over
   the story, it is a control over the frame. Set beside the counters and cut
   to their height it reads as part of that line instead.

   LEFT IS NOT "THE SIDE THE COUNTERS ARE ON". The figures move to the right on
   most screens; the switch does not follow them. It is on the left edge of
   whatever it is over, always, and only the counters that are ALSO on the left
   step aside for it. */:root { --wide-size: 18px; --wide-gap: 6px; }








/* AND THE FIGURES MAKE ROOM, but only where they share the edge. `.counts--left`
   is the player's — a middle that puts its figures on the left has the same
   collision as a story screen does, and it was not in this list, so on the feed
   the switch sat on top of the view count. Any counter on the left steps
   aside; a counter on the right never knew about this. */.screen.story > .counts,
.duo article:nth-of-type(1) .counts,
.quad article:nth-of-type(-n+2) .counts,
.counts--left {
  left: calc(var(--ctl-inset) + var(--wide-size) + var(--wide-gap));
}

/* ON THE COVER — first in the line under the masthead, on the LEFT, which is
   the side it is on everywhere else. It has to live inside the middle, because
   full screen takes away everything that is not the middle: in the foot bar it
   removed itself with the first press. */

/* DOUBLED CLASS, and it is not a flourish. `.sound svg { 19px }` lives in the
   half of this stylesheet that is split out into rest.css and loaded AFTER the
   critical block — so at equal specificity it wins on source order no matter
   where I write the override in this file. The mute came out 19px in a 15px
   box, four pixels wider than its own button and two pixels low, which is
   exactly what "not aligned" looked like. `.sound.sound--mast` outranks it and
   the split cannot reorder that. */.wide.wide--mast, .sound.sound--mast { display: inline-grid; place-items: center;
  position: static; width: 15px; height: 15px; z-index: auto; color: inherit; }
.wide.wide--mast svg, .sound.sound--mast svg { width: 15px; height: 15px; }

/* WHICH OF THE TWO IS ON SCREEN.
   It was "is a story open", which meant a page with no reel at all — the
   player, the chat — had neither: the stage one was waiting for a screen that
   does not exist there and the masthead one for a cover that does not either.
   The question is really "is the cover what you are looking at", and the cover
   is what you are looking at exactly while the reel is closed. Everything else
   — every story, every break, the back cover, a player with no reel — gets the
   one over the picture. *//* THE COVER IS WHAT YOU ARE LOOKING AT only when the reel is closed AND
   nothing is open over it. "Reel closed" alone was wrong: the list of all
   twenty-five and the menu pages are drawn with the reel shut, so on the last
   page of the issue the switch vanished — and somebody who had gone full
   screen and read to the end had no way back out. Anything in the middle that
   is not the cover gets the switch. */:root { --on-cover: initial; }



















/* AND THE WORDS STOP SHORT OF IT — BUT ONLY THE WORDS UNDER IT.
   The column is positioned inside the picture, so text that shares the picture
   with it has to end where the buttons begin. The foot bar is BELOW the
   picture and the buttons are nowhere near it, so it was being held off its
   own right edge by a control it does not overlap. Anything outside the frame
   the column is positioned in runs the full width. */.story .body, .duo .body, .quad .body { padding-right: 56px; }


/* THE SAME BUTTON, TWICE. Same box, same icon size, same type for the number
   under it — the only difference between them is which shape is inside. */.heart, .save {
  cursor: pointer; display: grid; justify-items: center; gap: 3px; width: 30px;
  padding: 0; border: 0; background: none; color: #fff;
  font-family: var(--mono); font-size: 10px; font-weight: 700; letter-spacing: .06em;
}
.heart svg, .save svg { width: 26px; height: 26px; display: block;
  transition: transform .12s ease; }
.heart:active svg, .save:active svg { transform: scale(.88); }
.heart:focus-visible, .save:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }



.rowfoot .save { position: static; }

/* Saved reads exactly like the sections beside it — same type, same box, same
   spacing, and NO NUMBER ON IT. It is a button and not a link because it opens
   a drawer rather than going anywhere, and that is the only difference a
   reader should not be able to see. */.menu button[data-drawer] { cursor: pointer; }

/* ── the saved list, in the drawer ──────────────────────────────────────── */.savedlist { margin: 0; padding: 0; list-style: none; display: grid; gap: 1px; }
.savedlist li {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
  gap: 10px; padding: 10px 2px; border-bottom: 1px solid var(--rule-color);
}
.savedlist a { display: grid; gap: 2px; min-width: 0; text-decoration: none; color: var(--fg); }
.savedlist .sv-t {
  font-size: 14px; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.savedlist .sv-w {
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--kick-fg);
}
.savedlist .sv-x {
  border: 1px solid var(--rule-color); background: none; color: var(--kick-fg);
  width: 30px; height: 30px; cursor: pointer; font-size: 12px;
}
.savedlist .sv-x:hover { background: var(--fg); color: var(--paper-bg); }
