/* The home page's own styles, on top of skin.css and site.css. Split from
   the chosen mockup's style block on 2026-09-25; what follows is that page's
   own description, still true here. In the site its scripts are
   assets/js/playground.js (_06-playground.js), checklist.js
   (_04-checklist.js) and motion.js (_06-motion.js), and _skin.css is
   skin.css.

   Playground checklist, option 04 · Corner card

   The checklist is a small card in the board's bottom left corner, the
   way an app keeps its getting-started list in a corner: "Your turn", a
   count, the three tasks, and a line saying how to do the next one. The
   board's top bar is gone. "Now it's your turn" still makes its entrance
   when the demo's last tile lands, then shrinks toward the corner as the
   card rises in there, so the eye follows it to the list. The card lies
   under the tiles and takes no pointer, sticks to the bottom of the
   window on a tall board, and moves aside or folds smaller when a tile
   comes near (_04-checklist.js). All three done, it folds into a lime
   pill. On a phone it is the board's last row, under the cells. Shared
   files are untouched: the ticking is still _06-playground.js.

   What follows is 06's own description, still true here.

06 · Mix

   THESIS: the page that launches this week, put together from the owner's
   picks. 01 is the base: its header, signup area, how it works, follows
   you, Pro, plans, closing card and footer. 03 gives the headline and the
   list of 38. 05 gives the playable board and the vote.
   SECOND PASS (the owner's notes of 2026-09-24): the playground is the
   first row and carries the page's h1 and lede. The one form sits in the
   strip under the board. A cursor builds the board once when it first
   shows (_06-playground.js), and the rows below arrive once as they
   scroll in (_06-motion.js).
   WHOLE ROWS: every section is a full-bleed row with its own ground, and
   the ground says what kind of row it is.
     deep with the app's star-dot grid   the first row: the board you can
                                          use, on the ground the app draws
                                          under its own board
     navy with the star-dot grid         the vote with the closing card,
                                          so the page ends on the texture it
                                          opened on
     deep, plain                          the list of 38, the stock the
                                          board is drawn from
     panel                                real captures from the app: the
                                          board that follows you, Pro groups
     navy, plain                          plain reading: how it works
     deep, again                          plans (open state), so it never
                                          shares a ground with the vote row
   No two neighbouring rows share a ground in either state, and a hairline
   sits between every pair of rows. The page's one colored edge is the
   lime line along the top of the first row, under the header. The
   footer sits on deep, the floor of the page.
   ONE TYPE SCALE: h1 56, row h2 40, h3 19 to 22, lede 17, body 16 and 15,
   small 14 and 13 (sizes at 1440). One container: 1312px with 16, 32 or 48
   at the sides. One split for the first row's head, the strip under the
   board and the follows you lines, so their second columns line up: the
   lede, the form and the second line sit on one edge. It is 5fr and 6fr,
   and 6fr and 5fr from 1200, where the headline takes three lines instead
   of four and the strip under the board comes up into a 900px screen.
   ONE FORM: the waiting list form sits in the strip under the board. The
   header and the closing card are links to #join.
   MOTION: ease-out, short, never a bounce, and nothing loops but the
   readings. Under reduced motion there is no demo and nothing fades or
   moves; without script everything is simply there.
   WIDGET PAGES (next week): every widget name in the list of 38, the tray
   and the captions carries data-slug, the label in kebab case, ready
   for /widget/<slug>/. .wname and .wref style a span and an a the same
   way, with the hover already written, so a name can become a link with
   no layout change. Tray names sit inside buttons and stay text; their
   links belong to the list of 38. */

/* ── 1 · The board you can use ──────────────────────────────────────────── */
/* The first row: the headline and the lede over 05's bench. Its head is the
   old hero's words on the page's one split, so the lede starts on the same
   edge as the form in the strip under the board. */
.play-head {
  display: grid; grid-template-columns: minmax(0, 1fr);
  row-gap: 18px; margin-bottom: 34px;
}
.play h1 {
  font-size: clamp(34px, 3.35vw + 7px, 56px);
  line-height: 1.04;
  letter-spacing: -0.035em;
}
.lede { font-size: 17px; line-height: 1.6; color: var(--ink-2); max-width: 34em; }
/* The head's two columns end together, on the headline's last line. */
@media (min-width: 900px) {
  .play-head { grid-template-columns: var(--split); column-gap: var(--split-gap); align-items: end; }
}
/* From 600 the clamp floor would keep a phone-size headline on a tablet. */
@media (min-width: 600px) and (max-width: 899px) { .play h1 { font-size: 44px; } }
@media (max-width: 359px) { .play h1 { font-size: 29px; } }
@media (max-width: 599px) { .lede { font-size: 16px; } }

/* The board above fills the optional answer with the widgets a visitor
   places. The answer waits until they open the form, so the strip never
   grows while they are busy with the board. */
html[data-play] #join:not(.has-own-answer) form:not(:focus-within) .split,
html[data-play] #join:not(.has-own-answer) form:not(:focus-within) .quiet { display: none; }

/* ── The bench ──────────────────────────────────────────────────────────── */
/* Ground: deep with the star-dot grid, the ground the app draws under its
   own board. The lime line along the top is the page's one colored edge,
   in the brand's green rather than the app's pink-to-blue aurora.
   The bench steps up to the page navy so it reads as a surface you work
   on. Everything inside it is 05's, plus Clock, Date and Weather in the
   tray and the demo's layer. _06-playground.js drives it (see its header). */
.play {
  /* The call-to-action row follows directly, so the bottom is tighter than a
     normal row's. */
  padding-top: clamp(56px, 3vw + 36px, 96px); padding-bottom: clamp(40px, 2.5vw + 24px, 64px); border-top: 0;
  background-color: var(--page-deep);
  background-image: var(--stars);
  background-size: 40px 40px;
  background-position: 50% 0;
}
.play::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--lime);
}
.for-coarse { display: none; }
@media (pointer: coarse), (max-width: 719px) {
  .for-fine { display: none; }
  .for-coarse { display: inline; }
}
@media (max-width: 899px) { .play { padding-top: 36px; } }
@media (max-width: 599px) { .play { padding-top: 28px; } .play-head { row-gap: 14px; margin-bottom: 24px; } }
/* A laptop at 1024x768: a little less air above the headline than on a
   desktop, so the tiles' readings still land inside the first screen. */
@media (min-width: 900px) and (max-width: 1199px) {
  .play { padding-top: 48px; }
  .play-head { margin-bottom: 28px; }
}
/* Desktop: the headline gets real room under the header (the base clamp,
   about 80px at 1440), and the head stands clear of the board. The form is
   its own row now, so nothing needs pulling up into the first screen. */
@media (min-width: 1200px) {
  .play-head { margin-bottom: 40px; }
}

/* The checklist: a card pinned in the board's bottom left corner, the way
   an app keeps its getting-started list in a corner. It ticks off what the
   visitor actually did, never what the demo did (_06-playground.js does
   the ticking). The card lies on the board's ground, under every tile, and
   never takes a pointer, so a tile can be dropped, moved or sized over it.
   It spans the whole board as a grid item and sticks to the bottom of the
   window, so on a board grown taller than the screen it stays in sight at
   the bottom of the part that shows. _04-checklist.js slides it along
   that edge when a tile comes near, and folds it to a small pill when the
   edge has no room for it. It takes over from "Now it's your turn" once
   the demo's last tile is down, and with all three done it folds for good
   into a lime pill. --quest-w and --quest-h sit on the board so the
   callout, a sibling, can aim at the card when it hands over. */
.board { z-index: 0; --quest-w: max(236px, calc(2 * var(--cell) + var(--gap))); --quest-h: 146px; }
.quest {
  position: sticky; bottom: 12px; z-index: -1;
  grid-column: 1 / -1; grid-row: 1 / -1; align-self: end; justify-self: start;
  width: var(--quest-w);
  translate: var(--quest-x, 0px) 0;
  transition: translate 0.28s var(--ease-out);
  pointer-events: none;
}
.quest-card {
  box-sizing: border-box;
  padding: 13px 14px 14px;
  border: 1px solid rgba(184, 255, 87, 0.32); border-radius: 14px;
  background: var(--page-deep);
  box-shadow: 0 18px 40px -22px rgba(0, 0, 0, 0.9);
  transform-origin: 0 100%;
  transition: opacity 0.2s var(--ease-out), scale 0.28s var(--ease-out);
}
.quest-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.tasks-label { margin: 0; white-space: nowrap; font-size: 16px; font-weight: 900; line-height: 20px; letter-spacing: -0.01em; color: var(--lime); }
.quest-count { margin: 0; font-size: 12px; font-weight: 700; line-height: 16px; color: var(--page-help); font-variant-numeric: tabular-nums; white-space: nowrap; }
.quest-n { color: var(--page-fg); }
/* The count is drawn from the ticks, so it can never disagree with them. */
.quest-n::before { content: "0"; }
.quest:has(.steps li[data-done]) .quest-n::before { content: "1"; }
.quest:has(.steps li[data-done] ~ li[data-done]) .quest-n::before { content: "2"; }
.quest:has(.steps li[data-done] ~ li[data-done] ~ li[data-done]) .quest-n::before { content: "3"; }

.steps { display: flex; flex-direction: column; gap: 5px; margin: 0; padding: 0; list-style: none; font-size: 13.5px; font-weight: 700; line-height: 20px; color: var(--page-note); }
.steps li { display: flex; flex-wrap: wrap; align-items: center; column-gap: 9px; white-space: nowrap; transition: color 0.2s var(--ease-out); }
.tick {
  box-sizing: border-box; flex: 0 0 auto; display: inline-grid; place-items: center; width: 17px; height: 17px;
  /* page-muted, not page-orbit: an empty ring still reads at 3:1. */
  border: 1.5px solid var(--page-muted); border-radius: 50%; color: transparent;
  transition: background-color 0.2s var(--ease-out), border-color 0.2s var(--ease-out), color 0.2s var(--ease-out);
}
.tick .ico { width: 9px; height: 9px; }
.steps li[data-done] { color: var(--page-help); }
.steps li[data-done] .tick { background: var(--lime); border-color: var(--lime); color: #07090F; animation: tick-in 0.3s var(--ease-out); }
@keyframes tick-in { from { transform: scale(0.55); } }
/* The next one to do is lit, and says how it is done. */
.steps li:nth-child(1 of :not([data-done])) { color: var(--page-fg); }
.steps li:nth-child(1 of :not([data-done])) .tick { border-color: rgba(184, 255, 87, 0.75); }
.step-how { display: none; flex-basis: 100%; padding-left: 26px; font-size: 12px; font-weight: 600; line-height: 16px; color: var(--page-help); white-space: nowrap; }
.steps li:nth-child(1 of :not([data-done])) .step-how { display: block; }
.step-how .ico { width: 11px; height: 11px; margin: 0 1px; vertical-align: -1px; color: var(--page-note); }

/* The two pills. "Your turn 1 of 3" sits in the card's bottom left corner,
   and the card folds toward it while the edge has no room for the whole
   card. The lime "All three done" is not on the board: it waits in the row
   under it (.pg-foot), and when it shows, the card goes for good. */
.quest-dock { position: absolute; left: 0; bottom: 0; }
.quest-dock, .steps-done {
  display: inline-flex; align-items: center; gap: 8px; box-sizing: border-box;
  height: 26px; margin: 0; padding: 0 11px; border-radius: 999px;
  font-size: 12px; font-weight: 800; line-height: 1; white-space: nowrap;
  box-shadow: 0 10px 24px -14px rgba(0, 0, 0, 0.9);
}
.quest-dock {
  border: 1px solid rgba(184, 255, 87, 0.32); background: var(--page-deep); color: var(--page-help);
  opacity: 0; visibility: hidden;
  transition: opacity 0.2s var(--ease-out), visibility 0s linear 0.2s;
}
.quest-dock-t { color: var(--lime); }
.quest.is-docked .quest-dock { opacity: 1; visibility: visible; transition-delay: 0.08s, 0s; }
/* Folded, the card goes out of sight but stays in the page, so a screen
   reader still has the list. */
.quest.is-docked .quest-card { opacity: 0; scale: 0.9; }
/* All three done: the card folds away and the lime pill shows under the
   board, at its left edge. The row is there from the start, so nothing
   below it moves when the pill arrives. */
.pg-foot { display: flex; align-items: center; min-height: 26px; margin-top: 5px; }
.steps-done { gap: 6px; padding: 0 12px 0 10px; background: var(--lime); color: #07090F; }
.steps-done .ico { width: 10px; height: 10px; }
.steps-done[hidden] { display: none; }
.steps-done:not([hidden]) { animation: done-in 0.36s var(--ease-out) 0.12s backwards; }
@keyframes done-in { from { opacity: 0; transform: translateY(6px) scale(0.9); } }
.board-wrap:has(#steps-done:not([hidden])) .quest {
  opacity: 0; visibility: hidden; scale: 0.9; transform-origin: 0 100%;
  transition: opacity 0.2s var(--ease-out), scale 0.28s var(--ease-out), visibility 0s linear 0.28s;
}
/* No room on the board for even the pill: it waits under the board's
   bottom edge, in the row the done pill uses. */
.quest.is-below .quest-dock { bottom: -31px; }
/* Not even there, with the edge off screen: the pill floats over the
   tiles, still taking no pointer, until a gap opens. A dragged tile still
   passes over it. */
.quest.is-over { z-index: 20; }

/* The strip: the same card one row of the grid tall, the name and count
   beside the list. It is the card on a laptop or a tablet, where the board
   has three rows and the tray sits under it; on a desktop the card takes
   this shape (.is-strip) when the bottom edge has one free row, not two. */
@media (min-width: 720px) and (max-width: 1199px) {
  .board { --quest-w: 440px; --quest-h: 62px; }
  .quest { width: max-content; }
  .quest-card { display: flex; align-items: stretch; padding: 10px 16px 10px 14px; }
  .quest-top { flex-direction: column; align-items: flex-start; justify-content: center; gap: 1px; margin: 0 16px 0 0; padding-right: 16px; border-right: 1px solid var(--page-border); }
  .tasks-label { font-size: 15px; line-height: 19px; }
  .steps { position: relative; flex-direction: row; column-gap: 16px; padding-bottom: 20px; }
  .steps li { flex-wrap: nowrap; column-gap: 7px; }
  .step-how { position: absolute; left: 0; bottom: 0; padding-left: 0; }
}
@media (min-width: 1200px) {
  .quest.is-strip { width: max-content; }
  .is-strip .quest-card { display: flex; align-items: stretch; padding: 10px 16px 10px 14px; }
  .is-strip .quest-top { flex-direction: column; align-items: flex-start; justify-content: center; gap: 1px; margin: 0 16px 0 0; padding-right: 16px; border-right: 1px solid var(--page-border); }
  .is-strip .tasks-label { font-size: 15px; line-height: 19px; }
  .is-strip .steps { position: relative; flex-direction: row; column-gap: 16px; padding-bottom: 20px; }
  .is-strip .steps li { flex-wrap: nowrap; column-gap: 7px; }
  .is-strip .step-how { position: absolute; left: 0; bottom: 0; padding-left: 0; }
}
/* A phone: the board's own last row, under the cells, full width, so it
   never covers a tile and needs no moving aside. */
@media (max-width: 719px) {
  /* Its own ground, so the board's empty squares never show through the
     row before the card arrives. */
  .quest { position: relative; bottom: auto; z-index: auto; grid-column: 1 / -1; grid-row: -2 / -1; align-self: stretch; justify-self: stretch; width: auto; translate: none; background: var(--page-bg); box-shadow: 0 -3px 0 var(--page-bg); }
  .quest-card { padding: 12px 14px 13px; }
  .quest-dock { display: none; }
  .board-wrap:has(#steps-done:not([hidden])) .quest { display: none; }
}

.bench {
  position: relative;
  border: 1px solid var(--page-border);
  border-radius: 18px;
  background: var(--page-bg);
  box-shadow: 0 40px 90px -50px rgba(0, 0, 0, 0.9);
}
.bench-main { display: grid; grid-template-columns: minmax(0, 1fr) 272px; }
.board-wrap { position: relative; z-index: 2; min-width: 0; padding: 16px; container-type: inline-size; }

/* The app's grid: square cells, 14px gutters. 8 columns, 4 on a phone.
   --cell comes from the wrap's width, so the rows stay square. */
.board {
  --cols: 8;
  --gap: 14px;
  --cell: calc((100cqw - (var(--cols) - 1) * var(--gap)) / var(--cols));
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  grid-template-rows: repeat(var(--rows, 4), var(--cell));
  gap: var(--gap);
  /* Each empty square, drawn once per cell pitch. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Crect x='0.4' y='0.4' width='87.4' height='87.4' rx='6.5' fill='%23ffffff' fill-opacity='0.022' stroke='%238FA0C4' stroke-opacity='0.2' stroke-width='1' stroke-dasharray='3 4' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");
  background-size: calc(var(--cell) + var(--gap)) calc(var(--cell) + var(--gap));
}
/* Four rows beside the side tray, which is that tall anyway; three once the
   tray sits under the board, so the tray comes up into the first screen.
   _06-playground.js sets --rows to the same floor, or more as tiles need. */
@media (max-width: 1199px) { .board { grid-template-rows: repeat(var(--rows, 3), var(--cell)); } }
.board > .tile {
  position: relative;
  grid-column: var(--c, 1) / span var(--w, 2);
  grid-row: var(--r, 1) / span var(--h, 2);
  scroll-margin: 96px 0 24px;
}
.board > .tile:focus-visible { outline: 2px solid var(--lime); outline-offset: 3px; }
@media (max-width: 719px) {
  /* One more row, sized to its content, for the checklist card under the cells. */
  .board { --cols: 4; --gap: 10px; grid-template-rows: repeat(var(--mrows, 5), var(--cell)) auto; }
  .board > .tile { grid-column: var(--mc, 1) / span var(--mw, 2); grid-row: var(--mr, 1) / span var(--mh, 2); }
}

/* Dragging: the tile sits in its target slot and is pushed back under the
   pointer by --dx/--dy, so it follows the pointer exactly. */
.board > .tile.is-lifted {
  z-index: 30;
  transform: translate(var(--dx, 0px), var(--dy, 0px));
  box-shadow: 0 28px 50px -14px rgba(0, 0, 0, 0.75), 0 0 0 1px rgba(184, 255, 87, 0.45);
}
.board > .tile.is-sizing {
  z-index: 30; align-self: start; justify-self: start;
  width: var(--rw); height: var(--rh);
  box-shadow: 0 18px 40px -16px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(184, 255, 87, 0.45);
}
html.pg-busy, html.pg-busy * { cursor: grabbing !important; user-select: none; }
html.pg-busy.pg-sizing, html.pg-busy.pg-sizing * { cursor: nwse-resize !important; }
html.pg-busy .tile-body { pointer-events: none; }

.pg-drop {
  grid-column: var(--c, 1) / span var(--w, 2);
  grid-row: var(--r, 1) / span var(--h, 2);
  border: 1.5px dashed rgba(184, 255, 87, 0.6);
  border-radius: var(--radius);
  background: rgba(184, 255, 87, 0.05);
  pointer-events: none;
}
@media (max-width: 719px) {
  .pg-drop { grid-column: var(--mc, 1) / span var(--mw, 2); grid-row: var(--mr, 1) / span var(--mh, 2); }
}

.pg-hint {
  grid-column: 1 / -1; grid-row: 3 / span 2; align-self: center; justify-self: center;
  display: flex; flex-direction: column; align-items: center; gap: 4px; margin: 0; padding: 14px 24px 15px;
  border: 1px solid rgba(184, 255, 87, 0.32); border-radius: 16px; background: var(--page-deep);
  box-shadow: 0 18px 40px -22px rgba(0, 0, 0, 0.9);
  text-align: center; pointer-events: none;
}
/* "Now it's your turn" waits until the demo has put its last tile down:
   while any tile is still held back, the hint stays out of sight. */
.board:has(.tile[data-demo], .tile.is-held) .pg-hint { opacity: 0; visibility: hidden; }
/* Then it makes an entrance, once: the card rises in, two lime rings go out
   from it, the words glow and cool, the arrow leans toward the tray three
   times, and the tray's edge lights once so the eye follows. Nothing loops,
   and under reduced motion _skin.css cuts it all to the final frame. */
.board:not(:has(.tile[data-demo], .tile.is-held)) .pg-hint {
  animation: turn-in 0.5s var(--ease-out) 0.15s both, turn-ring 1.3s var(--ease-out) 0.55s 2, turn-hand 0.45s var(--ease-out) 3.4s forwards;
}
.board:not(:has(.tile[data-demo], .tile.is-held)) .hint-turn { animation: turn-glow 1.4s var(--ease-out) 0.35s both; }
.board:not(:has(.tile[data-demo], .tile.is-held)) .hint-how .ico { animation: turn-lean 0.75s var(--ease-out) 1.1s 3; }
/* Flat on purpose: :has() cannot hold another :has(), and a rule that
   nests one is dropped whole. */
.bench-main:not(:has(.board.has-changed, .board .tile[data-demo], .board .tile.is-held)) .tray {
  animation: tray-call 1.4s var(--ease-out) 0.9s 1;
}
@keyframes turn-in { from { opacity: 0; transform: translateY(12px) scale(0.94); } to { opacity: 1; transform: none; } }
@keyframes turn-ring {
  from { box-shadow: 0 0 0 0 rgba(184, 255, 87, 0.45), 0 18px 40px -22px rgba(0, 0, 0, 0.9); }
  to { box-shadow: 0 0 0 22px rgba(184, 255, 87, 0), 0 18px 40px -22px rgba(0, 0, 0, 0.9); }
}
@keyframes turn-glow {
  from { text-shadow: 0 0 22px rgba(184, 255, 87, 0.9); }
  to { text-shadow: 0 0 0 rgba(184, 255, 87, 0); }
}
@keyframes turn-lean { 0%, 100% { transform: none; } 45% { transform: translateX(6px); } }
/* After its moment the callout hands over to the checklist card: it
   shrinks away toward the board's bottom left corner as the card rises in
   there, so the eye follows it to where the three tasks wait. Change the
   board first and the callout goes at once, and the card is simply there.
   Under reduced motion only the card shows. */
.board {
  --hand-x: calc(var(--quest-w) / 2 - 50cqw);
  --hand-y: calc(var(--cell) + var(--gap) / 2 - var(--quest-h) / 2);
}
@keyframes turn-hand { to { opacity: 0; transform: translate(var(--hand-x), var(--hand-y)) scale(0.6); visibility: hidden; } }
.board:has(.tile[data-demo], .tile.is-held) :is(.quest-card, .quest-dock) { opacity: 0; }
.board:not(:has(.tile[data-demo], .tile.is-held)) .quest-card {
  animation: quest-in 0.5s var(--ease-out) 3.5s backwards, turn-ring 1.3s var(--ease-out) 3.8s 1;
}
.board.has-changed:not(:has(.tile[data-demo], .tile.is-held)) .quest-card { animation: none; }
@keyframes quest-in { from { opacity: 0; transform: translate(calc(var(--hand-x) * -0.3), calc(var(--hand-y) * -0.3)) scale(0.92); } }
.board:has(#steps-done:not([hidden])) .pg-hint { display: none; }
@keyframes tray-call {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(184, 255, 87, 0); }
  30% { box-shadow: inset 0 0 0 1px rgba(184, 255, 87, 0.65), inset 0 0 40px rgba(184, 255, 87, 0.08); }
}
.hint-turn { font-size: 20px; font-weight: 900; line-height: 1.2; letter-spacing: -0.01em; color: var(--lime); }
.hint-how { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--page-help); }
.pg-hint .ico { width: 13px; height: 13px; color: var(--lime); }
.pg-hint .hint-tap { display: none; }
/* The arrow points at the tray: right while it sits beside the board, down
   once it moves under it. */
.pg-hint .hint-below { display: none; }
@media (max-width: 1199px) {
  .pg-hint { grid-row: 3; }
  .board { --hand-y: calc(var(--cell) / 2 - var(--quest-h) / 2); }
  .pg-hint .hint-side { display: none; }
  .pg-hint .hint-below { display: inline; }
}
.board.has-changed .pg-hint { display: none; }
/* Where people tap rather than drag, the hint says so. On a phone it sits
   under the three tiles, in the board's empty fifth row. */
@media (max-width: 719px), (pointer: coarse) {
  .pg-hint .hint-side, .pg-hint .hint-below { display: none; }
  .pg-hint .hint-tap { display: inline; }
}
@media (max-width: 719px) {
  .pg-hint { grid-row: 5; padding: 10px 16px 11px; }
  .board { --hand-x: 0px; --hand-y: calc(var(--cell) / 2 + var(--gap) + 72px); }
  .hint-turn { font-size: 17px; }
}

/* Tile controls, added by _06-playground.js. The label takes the room, so
   the buttons sit at the right whether or not the label shows. */
.board .tile-bar { justify-content: flex-end; }
.board .tile-bar .tile-label { flex: 1 1 auto; }
.pg-tb {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 24px; height: 24px; margin: -3px 0; padding: 0;
  border: 0; border-radius: 6px; background: transparent;
  color: var(--widget-control-fg); cursor: pointer;
  transition: background-color 0.15s var(--ease-out), color 0.15s var(--ease-out);
}
.pg-tb .ico { width: 11px; height: 11px; }
.pg-tb:hover { background: color-mix(in srgb, var(--foreground) 9%, transparent); color: var(--foreground); }
.pg-tb:focus-visible { outline: 2px solid var(--primary); outline-offset: 0; }
.pg-tb[aria-expanded="true"] { background: var(--lime); color: #07090F; }
.pg-move, .pg-size { display: none; }
/* With a mouse the buttons stay out of the way, but a tile reached from
   the keyboard shows them: they work in a screen reader's browse mode,
   where the arrow keys belong to the reader. Hidden the way .sr-only
   hides, never display:none, so Tab can still land on them while focus
   passes from the tile to its bar. */
@media (pointer: fine) and (min-width: 720px) {
  .pg-move, .pg-size {
    display: grid; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .board > .tile:is(:focus-visible, :has(:focus-visible)) :is(.pg-move, .pg-size) {
    position: static; width: 24px; height: 24px; margin: -3px 0; overflow: visible; clip-path: none;
  }
}
@media (max-width: 719px), (pointer: coarse) {
  .pg-move, .pg-size { display: grid; }
  .pg-tb { position: relative; z-index: 1; width: 28px; height: 28px; margin: -6px 0; }
  .pg-tb + .pg-tb { margin-left: -4px; }
  .pg-tb .ico { width: 12px; height: 12px; }
  /* More room to press than to see: 6px up and down (the tile's own edge
     stops it at the top), 6px out at either end of the row, and across the
     2px between buttons without reaching into a neighbour's. */
  .pg-tb::before { content: ""; position: absolute; inset: -6px -1px; }
  .pg-move::before { left: -6px; }
  .pg-close::before { right: -6px; }
}
.board > .tile > .tile-bar, .pg-grip, .tray-item { user-select: none; -webkit-user-select: none; }
@media (pointer: fine) {
  .board > .tile > .tile-bar { cursor: grab; }
  .board > .tile > .tile-bar button { cursor: pointer; }
}

.pg-grip {
  position: absolute; right: 0; bottom: 0; z-index: 3;
  width: 20px; height: 20px; cursor: nwse-resize;
  color: var(--widget-control-fg); opacity: 0;
  transition: opacity 0.15s var(--ease-out);
}
.pg-grip::before {
  content: ""; position: absolute; right: 5px; bottom: 5px; width: 9px; height: 9px;
  background: linear-gradient(135deg, transparent 0 46%, currentColor 46% 56%, transparent 56% 70%, currentColor 70% 80%, transparent 80%);
}
.board > .tile:hover .pg-grip, .board > .tile:focus-within .pg-grip, .board > .tile:focus .pg-grip, .board > .tile.is-sizing .pg-grip { opacity: 1; }
@media (max-width: 719px), (pointer: coarse) { .pg-grip { display: none; } }

/* The move and size pads on a phone: arrows over the tile, Done in the middle. */
.pg-pad {
  position: absolute; inset: 0; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  border-radius: inherit;
  background: var(--widget-bg);
}
.pg-pad[hidden] { display: none; }

/* The data credit behind a weather tile's info button: a note under the bar,
   over the reading, until it is closed. */
.pg-note {
  position: absolute; top: 40px; right: 8px; left: 8px; z-index: 4;
  margin: 0; padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--foreground) 18%, transparent); border-radius: 8px;
  /* Not --muted-foreground: at 12px two of the four themes fall under 4.5:1. */
  background: var(--widget-bg); color: color-mix(in srgb, var(--foreground) 78%, var(--widget-bg));
  box-shadow: 0 12px 28px -14px rgba(0, 0, 0, 0.8);
  font-size: 12px; font-weight: 600; line-height: 17px; text-wrap: balance;
}
.pg-note[hidden] { display: none; }
.pg-note a { color: var(--foreground); text-decoration: underline; text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); text-underline-offset: 3px; }
.pg-note a:hover { text-decoration-color: currentColor; }
.pg-note a:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; border-radius: 2px; }
.pg-pad-h { margin: 0; font-size: 11px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--foreground); }
.pg-pad-grid {
  display: grid; gap: 4px;
  grid-template-columns: repeat(3, 38px); grid-template-rows: repeat(3, 34px);
  grid-template-areas: ". up ." "left done right" ". down .";
}
.pg-pad-grid button {
  display: grid; place-items: center; padding: 0; border: 0; border-radius: 9px;
  background: color-mix(in srgb, var(--foreground) 10%, transparent); color: var(--foreground);
  font: inherit; cursor: pointer;
}
.pg-pad-grid button:hover { background: color-mix(in srgb, var(--foreground) 16%, transparent); }
.pg-pad-grid button:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.pg-pad-grid button[data-d="done"] { background: var(--lime); color: #07090F; }
.pg-pad-grid .ico { width: 13px; height: 13px; }
.pg-pad-grid [data-d="up"] { grid-area: up; }
.pg-pad-grid [data-d="left"] { grid-area: left; }
.pg-pad-grid [data-d="done"] { grid-area: done; }
.pg-pad-grid [data-d="right"] { grid-area: right; }
.pg-pad-grid [data-d="down"] { grid-area: down; }

/* The tray. Clock, Date and Weather lead it: they are on the board already
   and can go on again, and the demo takes them from here. Fifteen rows at
   26px keep the side tray as tall as the four-row board beside it. */
.tray { position: relative; z-index: 1; min-width: 0; padding: 16px 12px 14px; border-left: 1px solid var(--page-border); }
/* On the side, the tray follows the page down a board that has grown. */
.tray-in { position: sticky; top: 80px; display: flex; flex-direction: column; min-height: 458px; }
.tray-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 0 8px 10px; }
.tray-h { margin: 0; font-size: 15px; font-weight: 900; color: var(--page-fg); }
.tray-sub { font-size: 12px; font-weight: 700; color: var(--page-help); white-space: nowrap; }
.tray-list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
.tray-item {
  box-sizing: border-box; display: flex; align-items: center; gap: 10px; width: 100%; height: 28px; padding: 0 8px;
  border: 0; border-radius: 8px; background: transparent; color: var(--page-text);
  font: inherit; font-size: 13.5px; font-weight: 600; text-align: left;
  cursor: pointer; touch-action: manipulation;
  transition: background-color 0.15s var(--ease-out);
}
@media (pointer: fine) { .tray-item { cursor: grab; } }
@media (min-width: 1200px) {
  .tray-list { gap: 1px; }
  .tray-item { height: 26px; }
}
.tray-item:hover, .tray-item.is-demo-hover { background: rgba(255, 255, 255, 0.05); }
.tray-item.is-demo-press { background: rgba(255, 255, 255, 0.09); }
.tray-item .ico { flex: 0 0 auto; width: 14px; height: 14px; color: var(--accent, var(--page-help)); }
.tray-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A footprint: the widget's size drawn in cells. */
.fp {
  --w: 2; --h: 2;
  flex: 0 0 auto;
  width: calc(var(--w) * 5px + (var(--w) - 1) * 2px);
  height: calc(var(--h) * 5px + (var(--h) - 1) * 2px);
  background: var(--page-orbit);
  mask: repeating-linear-gradient(90deg, #000 0 5px, transparent 5px 7px), repeating-linear-gradient(180deg, #000 0 5px, transparent 5px 7px);
  mask-composite: intersect;
}
.tray-item:hover .fp, .tray-item:focus-visible .fp, .tray-item.is-demo-hover .fp, .tray-item.is-demo-press .fp { background: var(--page-help); }
.fp[data-w="3"] { --w: 3; } .fp[data-w="4"] { --w: 4; } .fp[data-w="5"] { --w: 5; }
.fp[data-h="3"] { --h: 3; }
/* The status line, then the two buttons: Show me again beside Start over.
   On the side tray the line takes its own row above them. */
.tray-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 8px;
  margin-top: auto; padding: 12px 8px 0; border-top: 1px solid var(--page-border);
}
.pg-status { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 12.5px; line-height: 17px; color: var(--page-help); }
@media (min-width: 1200px) {
  .tray-foot { gap: 8px 6px; padding-left: 4px; padding-right: 4px; }
  .pg-status { flex-basis: 100%; padding-left: 4px; }
  .tray-foot .pg-reset { padding: 0 7px; }
}
.pg-reset {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 8px;
  border: 1px solid var(--page-orbit); border-radius: 8px; background: transparent;
  color: var(--page-note); font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
}
.pg-reset:hover { border-color: var(--page-help); color: var(--page-fg); }
.pg-reset .ico { width: 11px; height: 11px; }
/* The demo does not play under reduced motion, so nothing offers it again. */
@media (prefers-reduced-motion: reduce) { .pg-replay { display: none; } }

@media (max-width: 1199px) {
  .bench-main { grid-template-columns: minmax(0, 1fr); }
  .tray { border-left: 0; border-top: 1px solid var(--page-border); padding: 12px 0 14px; }
  .tray-in { position: static; min-height: 0; }
  .tray-head { padding: 0 16px 6px; justify-content: flex-start; }
  .tray-list {
    flex-direction: row; gap: 8px; overflow-x: auto; padding: 5px 16px 6px;
    overscroll-behavior-x: contain; scrollbar-width: none;
    mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 28px), transparent);
  }
  .tray-list::-webkit-scrollbar { display: none; }
  .tray-list li { flex: 0 0 auto; }
  .tray-item { width: auto; height: 38px; padding: 0 14px 0 12px; border: 1px solid var(--page-orbit); border-radius: 999px; }
  .tray-item .fp { display: none; }
  .tray-foot { margin: 10px 16px 0; padding: 12px 0 0; }
}
/* On a tablet or a laptop all twelve fit in two or three rows, so nothing
   hides in the fade and a pill reached by Tab always shows whole. The
   sideways tray stays on phones, where _06-playground.js scrolls a pill
   reached by Tab clear of the fade (scroll-padding sets the margin). */
@media (min-width: 720px) and (max-width: 1199px) {
  .tray-list { flex-wrap: wrap; overflow: visible; mask-image: none; }
}
@media (max-width: 719px) {
  .tray-list { padding-right: 32px; scroll-padding-inline: 16px 32px; }
}
/* On a phone the tray sits above the board. Tap a pill and the widget
   lands right under your thumb, and the demo's taps and the tiles they add
   fit on one screen. (Under the board the pills were a screen away.)
   Show me again and Start over go under the board, beside the credit, so
   the board comes up the screen far enough for the demo to play on a
   short phone. _06-playground.js moves the tray before the board and the
   two buttons after it, so Tab follows what the eye sees; until it runs,
   order puts the tray on top and the buttons wait out of the flow. */
@media (max-width: 719px) {
  .board-wrap { padding: 12px; }
  .tray { order: -1; border-top: 0; border-bottom: 1px solid var(--page-border); padding: 12px 0 6px; }
  .tray-head { padding-bottom: 6px; }
  html:not([data-play]) .tray-foot { display: none; }
  .bench-main > .tray-foot { justify-content: flex-end; margin: 0 12px; padding: 10px 0 12px; }
  /* Above the board the pills and the tile a tap adds are one glance
     apart, so the status line is read out and not shown, and the two
     buttons keep one row. */
  .pg-status { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
@media (max-width: 767px) {
  .pg-reset { height: 34px; }
}

/* The row under the board, for the done pill, takes the board's bottom
   padding. */
.board-wrap:has(.pg-foot) { padding-bottom: 10px; }
@media (max-width: 719px) { .board-wrap:has(.pg-foot) { padding-bottom: 8px; } }

/* Without JavaScript. _06-playground.js sets html[data-play] once the board
   works. Until then nothing invites a drag or a press: the tray, the hint,
   the checklist, the how-to lines and the dots in the list of 38 keep
   their room but do not show, so nothing moves when a slow script arrives.
   With script switched off they go, the board shrinks to its three tiles,
   and the <noscript> line says why. */
html:not([data-play]) .tray-in,
html:not([data-play]) .pg-hint,
html:not([data-play]) .quest,
html:not([data-play]) .steps,
html:not([data-play]) .play-only { visibility: hidden; }
.nojs { margin: 16px 0 0; max-width: 58ch; font-size: 15px; font-weight: 700; line-height: 1.5; color: var(--ink-2); }
@media (scripting: none) {
  /* Nothing can be ticked off without script, so the done pill's row goes. */
  .tray, .pg-hint, .quest, .steps, .play-only, .pg-foot { display: none; }
  .bench-main { grid-template-columns: minmax(0, 1fr); }
  .board { grid-template-rows: repeat(2, var(--cell)); }
}
@media (scripting: none) and (max-width: 719px) {
  .board { grid-template-rows: repeat(4, var(--cell)); }
}

/* The demo. _06-playground.js builds the board once the way a visitor
   would: a cursor takes Clock, Date and Weather from the tray and drops
   each in its place, or taps them in where the board is tap-to-add. The
   three tiles are in the page and running from the first paint; the demo
   only holds them out of sight (opacity, so a screen reader and the check
   still find them). The CSS holds them before the script arrives, so they
   never show and vanish again, and lets them show at 3.5 s if the script
   never takes over. No demo, and no hold, under reduced motion. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .board > .tile[data-demo] { opacity: 0; animation: pg-show 1ms linear 3.5s forwards; }
}
@keyframes pg-show { to { opacity: 1; } }
.board > .tile.is-held { opacity: 0; }
.board > .tile.is-carried { z-index: 30; will-change: transform; }
/* The cursor, its press ring and the touch point draw on one layer over
   the bench. It never takes a pointer and is aria-hidden. */
.pg-stage { position: absolute; inset: 0; z-index: 6; overflow: hidden; border-radius: inherit; pointer-events: none; }
.pg-cursor { position: absolute; left: 0; top: 0; will-change: transform, opacity; }
.pg-cursor svg {
  position: absolute; display: none; overflow: visible;
  fill: #FFFFFF; stroke: #07090F; stroke-width: 2.5px; stroke-linejoin: round; paint-order: stroke;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
}
.pg-cursor svg path { vector-effect: non-scaling-stroke; }
.pg-cursor[data-icon="arrow"] .pg-c-arrow,
.pg-cursor[data-icon="hand"] .pg-c-hand,
.pg-cursor[data-icon="fist"] .pg-c-fist { display: block; }
/* Each shape is placed so its hot spot sits on the layer's point: the
   arrow's tip, the middle of the open hand, the middle of the fist. */
.pg-c-arrow { left: -3px; top: 0; width: 19px; height: 22px; }
.pg-c-hand { left: -12px; top: -13px; width: 23px; height: 23px; }
.pg-c-fist { left: -9px; top: -11px; width: 18px; height: 24px; }
.pg-ring {
  position: absolute; left: -14px; top: -14px; width: 28px; height: 28px; box-sizing: border-box;
  border: 1.5px solid rgba(184, 255, 87, 0.85); border-radius: 50%; opacity: 0;
}
.pg-touch {
  position: absolute; left: -20px; top: -20px; width: 40px; height: 40px; box-sizing: border-box;
  border: 1.5px solid rgba(255, 255, 255, 0.75); border-radius: 50%;
  background: rgba(255, 255, 255, 0.22); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
  opacity: 0; will-change: transform, opacity;
}

/* ── 3 · How it works ───────────────────────────────────────────────────── */
/* Ground: plain navy. Four drawings and four lines; the cells in the
   drawings are the only texture it needs. */
.how { background: var(--page-bg); }
/* How it works: one tile's life in four frames, on the same lattice. */
.how-list { display: grid; gap: 28px; margin: 40px 0 0; padding: 0; list-style: none; }
/* Phone: the drawing beside its words, so four steps take one screen. */
.how-step { display: grid; grid-template-columns: 96px minmax(0, 1fr); grid-template-rows: auto 1fr; column-gap: 20px; row-gap: 6px; }
.how-step svg { grid-row: 1 / span 2; width: 96px; height: auto; overflow: visible; }
.how-step h3 { font-size: 19px; letter-spacing: -0.02em; }
.how-step p { font-size: 15px; color: var(--ink-3); max-width: 26em; }
.cellgrid rect { fill: none; stroke: var(--page-orbit); stroke-width: 1; }
.fig-new { fill: rgba(184, 255, 87, 0.07); stroke: var(--lime); stroke-width: 1.5; stroke-dasharray: 4 3; }
.fig-ghost { fill: none; stroke: #4A5E82; stroke-width: 1.25; stroke-dasharray: 3 3; }
.fig-tile { fill: var(--tile-bg); stroke: var(--primary); stroke-width: 1.5; }
.fig-done { fill: var(--tile-bg); stroke: var(--lime); stroke-width: 1.5; }
.fig-ico-lime { fill: var(--lime); }
.fig-ico-blue { fill: var(--primary); }
.fig-handle { stroke: var(--primary); stroke-width: 1.5; stroke-linecap: round; }
@media (min-width: 640px) {
  .how-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 44px 32px; }
  .how-step { grid-template-columns: none; grid-template-rows: auto auto 1fr; row-gap: 12px; }
  .how-step svg { grid-row: auto; width: 140px; margin-bottom: 14px; }
  .how-step h3 { font-size: 21px; }
}
@media (min-width: 1100px) {
  .how-list { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; }
  .how-step { padding-right: 32px; }
  .how-step + .how-step { border-left: 1px solid var(--rule); padding-left: 32px; }
}

/* ── 4 · Follows you ────────────────────────────────────────────────────── */
/* Ground: panel, the lifted navy the page keeps for real captures. */
.proof { background: var(--page-panel); }
.proof-lines { display: grid; gap: 32px; }
.proof-lines p { margin-top: 12px; font-size: 16px; color: var(--ink-3); max-width: 30em; }
/* The address in the page's own face, set apart by weight: the only mono
   text on the page would cost a 21KB font for one span. */
.proof-lines code { font-family: inherit; font-size: 1em; font-weight: 700; color: var(--ink-2); }
.shot { margin: 48px 0 0; }
.shot-frame {
  position: relative; overflow: hidden; border-radius: 14px; border: 1px solid var(--rule);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5), 0 4px 14px rgba(0, 0, 0, 0.35);
}
.shot img { width: 100%; height: auto; }
/* On a phone the whole board would shrink to a strip, so show its left
   part at a readable size: Clock, Date, World Clock and the row under them. */
@media (max-width: 699px) {
  .shot-frame { aspect-ratio: 985 / 760; }
  .shot img { position: absolute; left: -6.09%; top: -5.26%; width: 162.44%; max-width: none; }
}
.shot figcaption { margin-top: 16px; font-size: 14px; color: var(--ink-3); max-width: 60em; }
/* The second line starts on the page's one split, under the lede and the form. */
@media (min-width: 900px) {
  .proof-lines { grid-template-columns: var(--split); column-gap: var(--split-gap); }
}

/* ── 5 · All 38 widgets ─────────────────────────────────────────────────── */
/* Ground: deep, like the playground: this is the stock the board above is
   drawn from. 03's six columns, with a lime dot on the fifteen a visitor
   can place above. Every name sits in the same column whether or not it
   has a dot, so the names line up as a list. */
.cat { background: var(--page-deep); }
.cat-groups { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 40px 28px; margin: 40px 0 0; padding: 0; list-style: none; }
.cat-group { --g: var(--primary); min-width: 0; }
.cat-group[data-group="time"]     { --g: #3B9EFF; }
.cat-group[data-group="weather-sky"] { --g: #4DEEEA; }
.cat-group[data-group="reading"]  { --g: #FF6FC0; }
.cat-group[data-group="developer"] { --g: #B28CFF; }
.cat-group[data-group="personal"] { --g: #39D353; }
.cat-group[data-group="money-media"] { --g: #F0C142; }
.cat-group h3 {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding-bottom: 10px; border-bottom: 1px solid color-mix(in srgb, var(--g) 55%, var(--rule));
  font-size: 15px; letter-spacing: -0.005em;
}
.cat-group h3 span { color: var(--ink-3); font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.cat-group ul { margin: 12px 0 0; padding: 0; list-style: none; }
.cat-group li {
  display: grid; grid-template-columns: 7px minmax(0, 1fr); column-gap: 10px; align-items: center;
  padding: 3px 0; font-size: 14.5px; line-height: 1.45;
}
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--lime); }
.cat-group .dot { grid-column: 1; }
/* A widget name. Today a span; next week an <a href="/widget/<slug>/">.
   Both get the same box, and the link's hover is written already. */
.wname {
  grid-column: 2; justify-self: start;
  margin: -2px -4px; padding: 2px 4px; border-radius: 5px;
  color: var(--ink-2); text-decoration: none;
  transition: color 0.15s var(--ease-out), background-color 0.15s var(--ease-out);
}
html[data-play] .cat-group .dot + .wname { color: var(--ink); }
.wname:focus-visible { outline-offset: 1px; }
a.wname:hover { color: var(--ink); background: color-mix(in srgb, var(--g) 14%, transparent); }
.cat-note {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 28px;
  margin-top: 40px; padding-top: 18px; border-top: 1px solid var(--rule);
  font-size: 14px; color: var(--ink-3);
}
/* The beta note: the count above is today's, not a promise. */
.cat-beta { max-width: 72ch; margin: 14px 0 0; font-size: 14px; line-height: 1.55; color: var(--ink-3); }
.cat-beta a { color: var(--ink-2); text-decoration: underline; text-decoration-color: rgba(143, 160, 196, 0.4); text-underline-offset: 3px; }
.cat-beta a:hover { color: var(--ink); text-decoration-color: currentColor; }
/* The legend's dot sits in the same column as the dots above it, level
   with the first line when the words wrap. */
.cat-legend { display: inline-grid; grid-template-columns: 7px auto; column-gap: 10px; align-items: start; color: var(--ink-2); }
.cat-legend .dot { margin-top: calc(0.775em - 3.5px); }
@media (max-width: 1199px) { .cat-groups { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 767px) {
  .cat-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px 20px; margin-top: 36px; }
  .cat-group li { padding: 5px 0; font-size: 14px; }
}

/* ── 6 · What Pro adds ──────────────────────────────────────────────────── */
/* Ground: panel again, for the second real capture: a Pro group. */
.pro { background: var(--page-panel); }
.pro-grid { display: grid; gap: 44px; align-items: start; }
.pro-list { display: grid; gap: 0; margin: 40px 0 0; padding: 0; list-style: none; }
.pro-list li { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: 14px; padding: 14px 0; border-top: 1px solid var(--rule); }
.pro-list li:last-child { border-bottom: 1px solid var(--rule); }
.pro-list .ico { width: 16px; height: 16px; margin-top: 4px; color: var(--lime); }
.pro-list b { display: block; font-weight: 700; color: var(--ink); }
.pro-list span { font-size: 15px; color: var(--ink-3); }
.pro-shot { position: relative; margin: 0; }
/* The Focus group, cut from board-space-groups (x 664 to 1592, y 399 to 1198 at 1x)
   into its own files, so a phone does not download the whole board to show 38% of it. */
.crop {
  position: relative; overflow: hidden; aspect-ratio: 928 / 799;
  border-radius: 14px; border: 1px solid var(--rule); background: var(--page-bg);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.crop img { display: block; width: 100%; height: auto; }
/* A label above the frame, so it never covers part of the capture. */
.pro-label { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; font-size: 14px; font-weight: 700; color: var(--ink-2); }
.pro-tag {
  padding: 4px 10px; border-radius: 999px; background: var(--lime); color: #07090F;
  font-size: 12px; font-weight: 900; line-height: 1.2; letter-spacing: 0.08em; text-transform: uppercase;
}
.pro-shot figcaption { margin-top: 14px; font-size: 14px; color: var(--ink-3); }
@media (min-width: 1000px) {
  .pro-grid { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); column-gap: 72px; }
}

/* ── 7 · Plans (open state only) ────────────────────────────────────────── */
/* Ground: deep. Plans sits between Pro (panel) and the vote (navy), so
   on deep it never runs into either as one block. */
.plans { background: var(--page-deep); }
.plan-wrap { margin-top: 40px; overflow-x: auto; }
.plan-table { width: 100%; max-width: 880px; border-collapse: collapse; font-size: 15px; }
.plan-table th, .plan-table td { padding: 14px 14px; border-bottom: 1px solid var(--rule); text-align: left; vertical-align: top; }
.plan-table thead th { padding-bottom: 12px; font-size: 18px; font-weight: 900; color: var(--ink); }
.plan-table thead th:last-child { color: var(--lime); }
.plan-table tbody th { font-weight: 700; color: var(--ink-2); }
.plan-table td { color: var(--ink); }
.plan-table td:nth-child(2) { white-space: nowrap; }
.plans-after { display: grid; gap: 24px; margin-top: 32px; max-width: 880px; }
.plans-after > p { font-size: 15px; color: var(--ink-3); max-width: 62em; }
.plans-start .start-terms { max-width: 40em; }
@media (max-width: 560px) {
  .plan-table { font-size: 14px; table-layout: fixed; }
  .plan-table th, .plan-table td { padding: 12px 6px; }
  .plan-table thead th:first-child { width: 34%; }
  .plan-table thead th:nth-child(2) { width: 24%; }
  .plan-table thead th { font-size: 17px; }
}

/* ── 8 · The vote, and the closing card ─────────────────────────────────── */
/* Ground: navy with the star-dot grid again, so the page closes on the
   ground it opened on. 05's words, with the two links as large buttons
   (64px tall, full width on a phone), and 01's closing card beside them
   so the page still ends on its one action. */
.vote {
  background-color: var(--page-bg);
  background-image: var(--stars);
  background-size: 40px 40px;
  background-position: 50% 0;
}
.vote-grid { display: grid; gap: 44px; align-items: end; }
.vote-links { display: grid; gap: 12px; margin-top: 40px; }
.vote-link {
  box-sizing: border-box; display: flex; align-items: center; gap: 14px;
  min-height: 64px; padding: 0 22px 0 20px;
  border: 1px solid var(--page-orbit); border-radius: 14px; background: var(--page-panel);
  color: var(--ink); font-size: 17px; font-weight: 700; line-height: 1.2; text-decoration: none;
  transition: border-color 0.15s var(--ease-out), background-color 0.15s var(--ease-out);
}
.vote-link:hover { border-color: #4A5E82; background: #152037; }
.vote-link .ico { flex: 0 0 auto; width: 22px; height: 22px; }
.vote-link[data-dest="roadmap"] .ico { color: var(--lime); }
.vote-link[data-dest="discord"] .ico { width: 25px; color: #8C95F7; }
.vote-link .go { flex: 0 0 auto; width: 14px; height: 14px; margin-left: auto; color: var(--ink-3); transition: transform 0.15s var(--ease-out), color 0.15s var(--ease-out); }
.vote-link:hover .go { transform: translateX(3px); color: var(--ink); }
@media (min-width: 560px) {
  .vote-links { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 620px; }
}
@media (min-width: 1200px) {
  .vote-grid { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); column-gap: 72px; }
}

/* ── Motion ─────────────────────────────────────────────────────────────── */
/* The drawings in How it works each play their own step once (motion.js);
   their parts turn about their own middles. */
.how-step svg g, .how-step svg rect, .how-step svg path { transform-box: fill-box; transform-origin: center; }
/* The press for this page's own controls (site.css has the buttons'). */
@media (prefers-reduced-motion: no-preference) {
  .pg-reset {
    transition-property: background-color, border-color, color, transform;
    transition-duration: 0.15s, 0.15s, 0.15s, 0.1s;
    transition-timing-function: var(--ease-out);
  }
  .vote-link {
    transition-property: background-color, border-color, transform;
    transition-duration: 0.15s, 0.15s, 0.1s;
    transition-timing-function: var(--ease-out);
  }
  .tray-item {
    transition-property: background-color, transform;
    transition-duration: 0.15s, 0.1s;
    transition-timing-function: var(--ease-out);
  }
  .pg-reset:active, .vote-link:active { transform: translateY(1px); }
  a.wname, .cat-beta a, .pg-note a {
    transition: color 0.2s var(--ease-out), background-color 0.2s var(--ease-out), text-decoration-color 0.2s var(--ease-out);
  }
  .tray-item:active, .tray-item.is-demo-press { transform: scale(0.98); }
}
