/* ==========================================================================
   Factbox — the front page.

   Loaded AFTER css/app.css by both index.html and explore.html, which are now
   one page served at two URLs. It adds only what that page needs and app.css
   has no answer for: the greeting, the Continue hero, Today's Factbox, the
   subject tiles, and the wide-window layout.

   Covers, plates, locks, read bars, section heads and the primary button all
   come from app.css unchanged. Nothing in here edits app.css, and every rule
   that reaches outside this page's own markup is scoped under `.lib.today`,
   which only these two pages carry — so a rule here cannot change /stories,
   /library or the reader.

   Same commitments as the rest of the site: the ground and the type colour
   are painted explicitly rather than inherited from the host webview, every
   colour is an app.css token so this page follows the palette wherever it
   goes, and nothing tappable sits in the bottom of the viewport where the
   Instagram / TikTok toolbar does.
   ========================================================================== */

/* Painted rather than inherited. app.css already does this; repeating it means
   this page cannot be left transparent by a load-order change. */
body{background:var(--ground); color:var(--parchment)}

/* The mast's own h1 and standfirst ARE this page's pitch, so a first-time
   reader meets them with no script involved. A reader who has read something
   gets a different greeting, and two headlines stacked read as two pages
   joined together — so js/today.js puts `is-back` on the main element and the
   pitch steps aside for it. Hidden by a class on a page element this file
   owns: the header block itself and app.css are never touched, and if the
   header is later rebuilt without an h1 these rules simply match nothing. */
/* The mast's headline and subtitle used to be hidden for a returning reader,
   so the greeting could take their place. They are not hidden any more: the
   page says the same thing to everybody, and the streak sits underneath it
   rather than instead of it. The rule is kept as a comment rather than
   deleted so the next person does not reinvent the swap. */

/* The standfirst is the one line on this page that is not the same for
   everybody, and it is replaced once — when the billing answer arrives, which
   is between a quarter of a second and a second and a half after first paint.
   js/today.js fades it across rather than swapping it under the reader's eye;
   this is the 160ms that makes that a fade rather than a cut. It only ever
   runs pitch -> owned, because the pitch is what the markup ships. */
.lib.today #tdy-blurb{transition:opacity .16s ease}

/* --------------------------------------------------------------------------
   The greeting. Two versions of the same slot: the pitch for someone who has
   never read anything, and their own numbers for someone who has.

   The new-reader version is in the HTML, not built by script, so the page has
   real words on it before any JavaScript runs — and still has them if none
   ever does.
   -------------------------------------------------------------------------- */

.tdy-say{margin:0}
/* Nothing to say and nothing to space: a first-time reader's greeting is the
   headline in the mast, and this slot must not leave a gap under it. */
.tdy-say:empty{display:none}

/* The headline, in the display serif.

   --display is Newsreader at --w-display (500), and it appears in exactly two
   places on this page: here and on Today's hook. That pair is what makes the
   page read as an edition rather than as an app screen; every other word is
   DM Sans. The face is fetched by the Google Fonts <link> in explore.html and
   index.html, because a token cannot fetch a font — if that link is ever
   dropped this rule falls to Georgia, which is the point of the stack.

   34px is the size the design is drawn at, on a 390px phone. clamp rather
   than a fixed length because the same headline has to hold a 375px phone and
   a 1920px window: 8.72vw is 34.0px at 390, 32.7 at 375, 37.5 at 430, and it
   stops at 44 so a desktop gets a headline rather than a poster. */
.lib.today .mast h1{
  margin:16px 0 10px;
  font-family:var(--display); font-weight:var(--w-display);
  font-size:clamp(1.95rem,8.72vw,2.75rem);
  line-height:1.06; letter-spacing:-.012em;
}

/* The streak line. One sentence carrying two numbers, where there used to be
   two stat pills — the same information in the space of one line, and a line
   that does not compete with the headline it sits under.

   It exists only for a reader with reading memory, and js/progress.js gates
   that on the account which owns it: signed out, this slot is empty and
   :empty above collapses it. That is a privacy gate, not a layout one. */
.tdy-streak{
  margin:16px 2px 2px;
  font-size:var(--fs-note); font-weight:var(--w-label); line-height:1.5;
  color:var(--dim); font-variant-numeric:tabular-nums;
}
.tdy-streak b{color:var(--parchment); font-weight:var(--w-head)}

/* --------------------------------------------------------------------------
   Continue. One wide row for the single story the reader is in the middle of,
   with the bar that says how far. Not a grid cell: it is one answer to one
   question, and it is the first thing a returning reader wants.
   -------------------------------------------------------------------------- */

.tdy-cont{
  display:flex; gap:14px; align-items:center;
  margin:20px 0 0; padding:12px;
  border-radius:20px; background:var(--raise);
  box-shadow:inset 0 0 0 1px var(--hair);
  text-decoration:none; color:inherit;
  transition:transform .18s ease;
}
.tdy-cont:active{transform:scale(.99)}
.tdy-cont .plate{width:74px; flex:0 0 74px; border-radius:14px; aspect-ratio:3/4}
.tdy-cont .t{min-width:0; flex:1 1 auto}
/* .tdy-eyebrow was a small-caps "Continue" above the title. The row now says
   "Continue: <title>" in one line of 17px, which is the design's own shape and
   one less thing stacked in a 74px-tall box. The rule is deleted rather than
   left unused; .tdy-kicker below is the eyebrow this page still has. */
.tdy-cont b{
  display:block; font-size:var(--fs-lead); font-weight:var(--w-head);
  line-height:var(--lh-tight);
}
.tdy-cont .tdy-where{
  display:block; margin-top:5px;
  font-size:var(--fs-meta); font-weight:var(--w-label); color:var(--dimmer);
  font-variant-numeric:tabular-nums;
}

/* The bar is a bar, not a claim: width is cards read over cards in the
   story, which is exactly what progress.js recorded. */
.tdy-bar{
  margin-top:9px; height:6px; border-radius:var(--pill);
  background:var(--tint-3); overflow:hidden;
}
.tdy-bar i{display:block; height:100%; background:var(--coral); border-radius:var(--pill)}

/* --------------------------------------------------------------------------
   Today's Factbox. One story, chosen by the date, the same one for everybody.
   The hook is the biggest text on the page after the greeting, because the
   hook is the reason to tap.
   -------------------------------------------------------------------------- */

.tdy-today{
  margin:26px 0 0; overflow:hidden;
  border-radius:24px; background:var(--raise);
  box-shadow:inset 0 0 0 1px var(--hair);
}
.tdy-art{position:relative; aspect-ratio:16/10; background:var(--night)}
.tdy-art img{width:100%; height:100%; object-fit:cover; object-position:center 32%}
/* A wash at the foot of the plate, so the panel below it does not start as a
   hard edge across the painting. */
.tdy-art::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:38%;
  background:linear-gradient(to top,var(--raise),var(--raise-0)); pointer-events:none;
}
.tdy-body{padding:16px 17px 19px}
.tdy-kicker{
  margin:0 0 11px; font-size:var(--fs-micro); font-weight:var(--w-label);
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--accent-ink);
}
/* .tdy-subject — a second small-caps line naming the topic, under the eyebrow
   — is gone. Two eyebrows stacked over a headline is a masthead, and the
   subject is already the first thing the hook says. The panel is now eyebrow,
   title, facts, button, which is four things rather than five.

   THE CLASS NAME IS A FOSSIL. js/today.js now puts the story's TITLE in this
   box, not its hook — one story must not carry two different headlines on two
   screens, and every other surface on the site shows the title. The selector
   is left named .tdy-hook because it is the only styling hook the hero
   headline has and renaming a class in this project has centred a whole page
   before. It styles the hero headline; the headline is the title.

   The title is the second and last place the display serif appears. Same face
   and weight as the h1 above it. */
.tdy-hook{
  margin:0;
  font-family:var(--display); font-weight:var(--w-display);
  /* Retuned for titles. The hook set this at 7.7vw because it had to survive a
     159-character first sentence; a title is 9 to 46 characters (51 stories,
     mean 27), so the same box can carry bigger type.

     8vw is the ceiling, and it is a measured ceiling rather than a taste: at
     8vw the longest title in the season, "Biblical creatures look nothing like
     paintings" (46 chars), sets to exactly two lines with no truncation at 375,
     390 and 430. One notch above it — 8.1vw — and "Who decided which books got
     into the Bible?" (43 chars) goes to three lines and gets clipped at 375 and
     390. So this is the largest type that keeps every title in the season whole
     inside the two-line box below. 30px at 375, 31.2px at 390, 34.4px at 430. */
  font-size:clamp(1.8rem,8vw,2.5rem);
  line-height:1.12; letter-spacing:-.012em; text-wrap:balance;
  /* Two lines, hard.

     It was three, sized for hooks. A title never needs three and no title in
     the season reaches two-and-a-bit, so three lines was a ceiling nothing
     could touch — the clamp had stopped being a guard. Two lines is a guard
     again: it is the shape a ~45-character headline actually wants, and it is
     what catches the day someone files a 90-character title.

     The bound is still what keeps "Start story" reachable. Measured at
     390x844: the hook set this box to 3 lines / 102.7px and the button landed
     80px above the fold; the title sets it to 1-2 lines / 34-68px and the
     button clears the fold by 149px on the longest title in the season. */
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2;
  overflow:hidden;
}
.tdy-facts{
  margin:13px 0 0; font-size:var(--fs-meta); font-weight:var(--w-label); color:var(--dimmer);
  font-variant-numeric:tabular-nums;
}
/* .go is a button in app.css and a link here, because this one navigates. */
.tdy-today a.go{
  margin:17px 0 0; text-align:center; text-decoration:none;
  display:block; padding:16px 20px;
}

/* --------------------------------------------------------------------------
   Shelves. A flick, not an inventory. Fixed-width covers, so a wider window
   shows MORE of them rather than stretching two across the screen.
   -------------------------------------------------------------------------- */

.tdy-shelf{
  display:flex; gap:13px; overflow-x:auto;
  padding:2px 18px 8px; margin:0 -18px;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
  scroll-snap-type:x proximity;
  /* The snap edge is the scrollport, which starts at the padding box — so
     without this the browser scrolls the shelf 18px to sit the first cover
     flush against it, and the row starts a cover-width left of every heading
     on the page. Measured in Chrome: first cover at 131px under a heading at
     150px. */
  scroll-padding:0 18px;
  overscroll-behavior-x:contain;   /* no accidental back-swipe mid-shelf */
}
.tdy-shelf::-webkit-scrollbar{display:none}
.tdy-shelf > .card{flex:0 0 136px; width:136px; scroll-snap-align:start}
.tdy-shelf > .card h3{font-size:var(--fs-note)}

/* --------------------------------------------------------------------------
   Binge a series. A subject is a row, not a list of covers: it says how far
   in the reader is, and tapping it opens the next story they have not read.
   The plate is that next story, so the tile shows where you are going.
   -------------------------------------------------------------------------- */

.tdy-series{
  display:grid; gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(230px,1fr));
}
.tdy-serie{
  display:flex; gap:12px; align-items:center;
  padding:10px; border-radius:20px;
  background:var(--raise); box-shadow:inset 0 0 0 1px var(--hair);
  text-decoration:none; color:inherit;
  transition:transform .18s ease;
}
.tdy-serie:active{transform:scale(.985)}
.tdy-serie .plate{width:58px; flex:0 0 58px; border-radius:8px; aspect-ratio:3/4}
.tdy-serie .t{min-width:0; flex:1 1 auto}
.tdy-serie b{
  display:block; font-size:var(--fs-sub); font-weight:var(--w-head);
  line-height:var(--lh-tight); letter-spacing:var(--ls-title);
}
.tdy-serie .tdy-where{
  display:block; margin-top:4px;
  font-size:var(--fs-meta); font-weight:var(--w-label); color:var(--dimmer);
  font-variant-numeric:tabular-nums;
}
.tdy-serie.is-done b{color:var(--dim)}

/* The same treatment app.css gives a locked cover, for a tile that is not a
   .card: dimmed and badged, never hidden. The badge is smaller because the
   plate it sits on is a third the size. */
.tdy-serie.locked .plate img{opacity:.52; filter:saturate(.65)}
.tdy-serie.locked b{color:var(--dim)}
.tdy-serie .lock{right:4px; top:4px; width:20px; height:20px; font-size:.58rem}

/* --------------------------------------------------------------------------
   All stories — the mosaic.

   Everything above this on the page is curated: one story chosen by the date,
   twelve picked for the week, one row per subject. They are spaced, they have
   room, and the title sits under the picture where it is easy to read. This
   section is the opposite object — the whole catalogue, all fifty-one — and it
   is drawn the opposite way, so that the two cannot be mistaken for each
   other: tiles packed with no gutter at all, in three sizes on an eight-step
   rhythm, the title burned into the bottom left of the painting and — only for
   a story the reader is in the middle of — their place in it on a pill.

   The tile is app.css's .card, unchanged. Same element, same classes, same
   .plate with the read bar and the padlock inside it. js/today.js adds one
   thing to it — a span — and everything else here is CSS over the cover the
   rest of the site already draws, which is why applyLocked() and applyOpen()
   go on finding these without being told the section was rebuilt.

   The two hard parts are solved in js/today.js and are worth knowing about
   from here, because a change on this side can undo them:

   - The row height is written onto the element as an explicit length. It is
     NOT derived from the tiles. A grid row sizes itself from what STARTS in
     it, and this layout has rows that hold nothing but the lower halves of
     tiles that began in the row above — those rows collapse to nothing and
     take the page with them. So do not put grid-auto-rows back in here.
   - The spans and that row height are written in the same pass, so one can
     never exist without the other. If the script does not run, neither is
     written, and the rules below draw fifty-one plain squares: aspect-ratio
     is on the plate purely as that floor.
   -------------------------------------------------------------------------- */

/* The zero-alpha end of the glass fade. app.css carries --ground-0 and
   --raise-0 for exactly this reason — `transparent` is rgba(0,0,0,0), and a
   gradient running towards it goes grey through the middle rather than simply
   thinning out. app.css has no --glass-0 and app.css is not this file's to
   edit, so it is declared here, from the same rgb as --glass. */
/* --glass-0 now lives in app.css, beside --ground-0 and --raise-0. */
/* The rgb above is --glass's own, 9 12 22. It used to be 14 11 18, which was
   --glass's rgb before the repaint and was left behind by it: on a browser
   that interpolates un-premultiplied, a gradient running from rgba(9,12,22,.62)
   to rgba(14,11,18,0) crosses a hue it should not. Zero alpha hides it in
   Chrome and does not everywhere. app.css should own --glass-0 beside --glass
   and --ground-0 — it is requested in the report; until it does, this is the
   one colour literal on this page and it is derived, not chosen. */

.tdy-mosaic{
  display:grid;
  /* Overwritten by js/today.js with the real column count. This is the value
     the page holds before the script runs and if it never does. */
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:0;
  /* Full bleed. .lib pads the page 18px and the catalogue runs out past it to
     the edges of the phone, which is what makes it read as a wall rather than
     as one more shelf. Negative margin only, and exactly the padding: the
     element ends up the width of the viewport, so nothing scrolls sideways. */
  margin:0 -18px;
  /* Behind the tiles, so a sub-pixel seam between two of them is a dark
     hairline rather than a bright one. */
  background:var(--night);
}

.tdy-mosaic > .card{
  position:relative;
  display:block;                 /* app.css stacks a cover; here it is one tile */
  height:100%; min-width:0; min-height:0;
  border-radius:0;
  transition:transform .16s ease;
}
/* Pressed, a tile lifts over its neighbours instead of opening a hole between
   them — with no gutter there is nothing behind it to see. */
.tdy-mosaic > .card:active{transform:scale(.965); z-index:3}

.tdy-mosaic .plate{
  /* width AND height, both, and the width is not optional: a tall tile is 125
     wide and 250 high, and a plate given only the height resolves its auto
     width from aspect-ratio — 250px of painting in a 125px column, hanging a
     whole cell past the right edge of the phone. Measured: the page scrolled
     sideways to 500px at a 375px viewport. */
  width:100%; height:100%;
  /* Only ever bites when the row height is auto, which means the script did
     not run. Then every tile is a square and the section still reads. */
  aspect-ratio:1;
  border-radius:0;
  /* --night, not app.css's --raise. On the cream page a plate's own backdrop
     IS cream and that is right; inside this wall it is a lit hole in a dark
     room for as long as the painting takes to arrive — and with fifty-one
     lazy images that is most of the wall on a cold scroll. app.css's own note
     on --night says it: "a cream flash there is the one thing worse than a
     dark one". It is also what stands behind a plate that never arrives at
     all, and a white title on cream is unreadable where a white title on
     --night is not. Measured on a throttled load: eight tiles cream at once
     before this, none after. */
  background:var(--night);
  /* app.css lifts a cover off the page with a drop shadow. Edge to edge there
     is no page between them to cast onto, and fifty-one of them stack into a
     grey haze. */
  box-shadow:none;
}

/* The scrim under the title.

   ::before, not ::after: app.css puts the finished tick on .plate::after and
   that has to go on working. z-index is not decoration either — the <img> is
   in flow and a positioned pseudo-element with z-index:auto paints under it. */
.tdy-mosaic .plate::before{
  content:""; position:absolute; z-index:1; pointer-events:none;
  left:0; right:0; bottom:0;
  /* Measured, not guessed, and measured against the worst plate rather than a
     typical one. These are museum photographs and several of them are close to
     white — s48, the Beast of Revelation woodcut, is the palest in the season
     and a locked cover is faded a further 48% towards the cream of the page,
     so the title sits on something around rgb(240,240,240) before the scrim
     touches it.

     A scrim that starts fading where the type starts is therefore no good: the
     first version of this ramped straight off --glass-deep and the top line of
     a two-line title came out at 2.3:1 on s48. So the gradient holds
     --glass-deep flat all the way past the top of the title box and only then
     thins out. The plateau is the accessible bit; the ramp above it is the
     part that stops the tile looking like a card with a bar across the foot. */
  height:max(96px,46%);
  background:linear-gradient(to top,
              var(--glass-deep) 0%, var(--glass-deep) 50%,
              var(--glass) 68%, var(--glass-0) 100%);
}
/* A two-column tile sets its title bigger, so its plateau has further to
   reach. */
.tdy-mosaic > .card[data-w="2"] .plate::before{height:max(116px,40%)}

/* The title. Bottom left, over the painting, clamped to two lines — a museum
   plate is not a caption bed and a five-line title over one is neither. The
   words stay in the document, so the link's accessible name is still the name
   of the story followed by where the reader is in it. */
.tdy-mosaic > .card h3{
  position:absolute; z-index:2;
  left:10px; right:10px; bottom:9px;
  margin:0;
  color:var(--on-glass);
  font-size:var(--fs-meta); font-weight:var(--w-head);
  line-height:1.24; letter-spacing:var(--ls-title);
  text-shadow:0 1px 3px var(--glass-deep);
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2;
  overflow:hidden;
  /* A finished tile gives 38px of its width to the tick, and the longest
     titles in the season are wider than what is left. Without somewhere to
     break, the clamp slices a word mid-stem on a 125px tile; with these it
     breaks and hyphenates instead. */
  overflow-wrap:anywhere; hyphens:auto; -webkit-hyphens:auto;
}
/* A tile two columns wide is twice the painting, so it can carry the title at
   the size the rest of the site sets a card title in. */
.tdy-mosaic > .card[data-w="2"] h3{
  font-size:var(--fs-sub); left:13px; right:13px; bottom:12px;
}
/* app.css dims a locked title to --dim, which is a navy for the cream page.
   Over a painting it is the one thing that must not be dimmed: the padlock
   and the faded plate already say "not yet", and an unreadable title says
   nothing at all. */
.tdy-mosaic > .card.locked h3{color:var(--on-glass)}
/* app.css's finished tick lives in the bottom right corner. Give it the room
   rather than run the title underneath it. */
.tdy-mosaic > .card.is-done h3{right:38px}

/* Card 6 of 9, top right, on its own glass so it is legible over a white
   painting as well as a dark one.

   ONLY for a story the reader is in the middle of. The catalogue line — "8
   cards · 3½ min" — is inventory, and inventory painted across fifty-one
   museum plates is not a reason to tap any of them; the one thing worth
   saying over a painting is where the reader got to. So the pill is drawn for
   .is-reading and for nothing else.

   The element itself always exists, and it is hidden by being taken out of
   sight rather than out of the document — display:none would strip the line
   from the link's accessible name, and it would also mean applyLocked() and
   applyOpen() were writing into nothing. They go on rewriting it; the reader
   simply does not see it until it says something worth reading. */
.tdy-mosaic > .card .meta{
  position:absolute; z-index:2;
  top:7px; right:7px; max-width:calc(100% - 14px);
  padding:3px 8px;
  border-radius:var(--pill);
  background:var(--glass-deep);
  -webkit-backdrop-filter:saturate(1.15) blur(7px);
  backdrop-filter:saturate(1.15) blur(7px);
  box-shadow:inset 0 0 0 1px var(--hair);
  color:var(--on-glass);
  font-size:var(--fs-micro); font-weight:var(--w-label);
  /* No --ls-badge. That token is for a filled badge with a word on it; this is
     a line of numbers, and .08em of tracking added 14px to it — enough to run
     "8 cards · 3½ min" past the end of a 125px tile and lose the last two
     characters to the ellipsis. Measured at 375: seventeen of the fifty-one
     were being cut. */
  font-variant-numeric:tabular-nums;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* app.css writes a part-read cover's line in --accent-ink, which is the blue
   you may write in ON CREAM. On the glass pill it is 1.1:1 — invisible.
   --accent-lift is the same accent from the light end of the scale.

   It is also the one colour on this page with a thin margin: --accent-lift on
   one --glass-deep layer over s48, the palest plate in the season, measured
   4.74:1 against a 4.5 floor. So the pill that carries it gets a second layer
   of --glass under the first — same token, no new colour — which takes the
   same measurement to 8.9:1 and, being the story you are in the middle of,
   is worth being the one pill that reads a shade heavier. */
.tdy-mosaic > .card.is-reading .meta{
  color:var(--accent-lift); font-weight:var(--w-strong);
  background:linear-gradient(var(--glass),var(--glass)), var(--glass-deep);
}

/* Not reading, or locked — a locked cover shows no progress at all, which is
   what applyLocked() enforces on the text and this enforces on the pill. Clip,
   not display:none: see the note above. */
.tdy-mosaic > .card:not(.is-reading) .meta,
.tdy-mosaic > .card.locked .meta{
  width:1px; height:1px; padding:0; overflow:hidden; white-space:nowrap;
  clip:rect(0 0 0 0); clip-path:inset(50%);
  background:none; box-shadow:none;
  -webkit-backdrop-filter:none; backdrop-filter:none;
}

/* The pill has the top right corner, so the padlock takes the other one. */
.tdy-mosaic .lock{left:8px; right:auto; top:8px}

/* Everything that sits ON the tile has to sit on the scrim as well.

   .plate::before is z-index:1, because that is the only way a pseudo-element
   gets above an <img> that is in flow. Every other mark inside the plate is
   position:absolute with z-index:auto, which paints BELOW a z-index:1 sibling
   — so without this the read bar, the padlock, the FREE ribbon and the
   finished tick would all be under 72% of near-black. */
.tdy-mosaic .lock,
.tdy-mosaic .readbar{z-index:2}
.tdy-mosaic > .card.is-done .plate::after{z-index:2}

/* The FREE ribbon and the three rules that made room for it are gone.

   They existed because the ribbon and the meta pill both wanted the top right
   of a mosaic tile: the ribbon won it and the pill was pushed down 26px on a
   one-column tile and across to the left on a two-column one, because measured
   side by side at a 375px viewport they overlapped by 45px.

   With the ribbon removed (js/today.js, and js/library.js's copy of it) there
   is nothing to make room for, so the pill goes back to the corner every other
   tile puts it in. Leaving the offsets behind would have left the two free
   covers with their meta line sitting 26px lower than the other forty-nine,
   which is the kind of drift nobody reports and everybody sees. */

/* Wider, because it is now a mark on a photograph rather than a rule under
   one, and it has the full width of the tile to be seen across. */
.tdy-mosaic .readbar{height:4px}

/* The failure state. Never a blank column: a sentence, and a way out that is
   one tap rather than a reload the reader has to think of. */
.tdy-void{
  margin:22px 0 4px; padding:20px 18px;
  border-radius:16px; background:var(--raise);
  box-shadow:inset 0 0 0 1px var(--hair);
}
.tdy-void b{
  display:block; margin-bottom:7px;
  font-size:var(--fs-lead); font-weight:var(--w-head); line-height:1.3;
}
.tdy-void p{
  margin:0 0 14px; font-size:var(--fs-note); line-height:var(--lh-body); color:var(--dim);
}
.tdy-void a{
  display:block; min-height:var(--tap); padding:12px 16px;
  border-radius:var(--pill); background:var(--tint);
  color:var(--parchment); font-size:var(--fs-sub); font-weight:var(--w-strong);
  text-align:center; text-decoration:none;
}

/* --------------------------------------------------------------------------
   Wider windows.

   Two things change and nothing else does. The hero lays the painting beside
   the words instead of above them, because at this width a 16:10 plate across
   the column is a billboard with a caption. And the page is allowed past
   app.css's 900px, so the shelves gain covers — the failure being designed
   against is two cards stretched across a 1440px window.
   -------------------------------------------------------------------------- */

@media (min-width:700px){
  .tdy-shelf > .card{flex:0 0 158px; width:158px}
}

@media (min-width:1024px){
  /* The column stays app.css's 900px at every width above this one, which is
     the container the design is drawn inside. It used to open to 1180px here.
     A 1920px window therefore shows the same page a 1024px window does, moved
     to the middle of the screen: the mosaic settles at five columns, the rails
     hold five and a half covers and go on scrolling, and nothing stretches. */

  .tdy-today{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.08fr); align-items:stretch}
  /* The plate now fills whatever height the words need, rather than setting
     the height itself. Taking the image out of flow is what makes that work:
     left in flow it is 550px of painting tall at its own aspect ratio, and the
     panel becomes a 700px billboard with a paragraph beside it. */
  .tdy-art{aspect-ratio:auto; height:auto; min-height:360px}
  .tdy-art img{position:absolute; top:0; right:0; bottom:0; left:0}
  .tdy-art::after{
    left:auto; top:0; bottom:0; right:-1px; width:24%; height:auto;
    background:linear-gradient(to left,var(--raise),var(--raise-0));
  }
  .tdy-body{padding:30px 32px; align-self:center}
  .tdy-today a.go{width:auto; min-width:250px; margin-left:0; margin-right:0}

  .tdy-cont .plate{width:88px; flex:0 0 88px}

  /* The mosaic stops being the page and goes back to being a section of one.
     Full bleed across a 1440px window is a wall with no room around it; inside
     the column it is a wall you are standing in front of. */
  .tdy-mosaic{margin:0; border-radius:18px; overflow:hidden}
}

@media (prefers-reduced-motion:reduce){
  .tdy-shelf{scroll-behavior:auto}
  .tdy-cont, .tdy-serie, .tdy-mosaic > .card{transition:none}
  /* js/today.js swaps the standfirst with no dip at all under this setting;
     the transition is removed here so the two cannot half-apply. */
  .lib.today #tdy-blurb{transition:none}
}

/* Short viewports — the iPhone SE and anything else 700px or less.

   "Start story" is the one control this page exists to be tapped, and at
   375x667 it sat 15px below the fold: the masthead is 257px and the 16/10
   plate another 212, which is 469 of a 667px screen before the headline.
   That was true before the hero moved from the hook to the title too; the
   shorter headline gained 31-36px and still did not clear it.

   So on short screens only, the furniture gives way and the content does
   not: less air above the wordmark, a tighter headline, and a smaller gap
   before the card. No text is hidden, nothing is truncated, and every value
   here is inert above 700px. */
@media (max-height: 700px){
  .lib.today .mast{ padding-top:10px; padding-bottom:0 }
  .lib.today .mast h1{ margin-bottom:6px; line-height:1.04 }
  .lib.today .mast p:not(.mark){ margin-bottom:0 }
  .tdy-card{ margin-top:14px }
}
