/* Archive.
 *
 * One committed decision: a dark felt table, bone tiles, and type with serifs. The tiles are
 * physical objects, so THE TILES DO NOT CHANGE COLOUR - the room does. In the light theme the felt
 * is a dusty sage and the page around it is warm paper; in the dark theme the felt goes to almost
 * black. Bone stays bone in both, because an inverted mahjong tile is not a mahjong tile.
 *
 * That is the half of the formula that gets a game picked out of a grid of thumbnails: a green
 * table with ivory tiles on it is recognisable at 60 pixels, and recognisable is the whole reason
 * this game is in the catalogue.
 *
 * What it deliberately is NOT: wood grain, green baize texture, drop-shadowed gold lettering,
 * dragons. That is set dressing, it costs bytes, and it dates.
 *
 * Motion budget, as everywhere in this family: transforms and opacity only, nothing loops,
 * everything bounded, prefers-reduced-motion honoured, Full/Fast/Instant remembered.
 */

:root {
  --paper: #efe9dc;
  --paper-2: #e3dbc9;
  --ink: #1b1a17;
  --ink-2: #514c42;
  --ink-3: #8a8275;
  --rule: #cbc2ad;

  /* The table. */
  --felt: #27564a;
  --felt-deep: #1d4239;
  --felt-edge: #163029;

  /* The tiles. Fixed in both themes - see the note above. */
  --tile: #f7f2e6;
  --tile-top: #fffdf6;
  --tile-side: #cfc5ab;
  --tile-side-deep: #a99e82;
  --tile-dim: #e8e1cf;

  --mark-ink: #23221e;
  --mark-dot: #1f4f86;
  --mark-bam: #1f6b3f;
  --mark-red: #bb2a1c;
  --mark-num: #8a6a22;
  --mark-white: #2f5c86;
  --mark-tag: #9a9281;
  --mark-bonus-2: #6a3c86;

  /* The signal. Used for the tile you have chosen and nothing else. */
  --pick: #f0b429;
  --pick-ink: #7a5405;

  --beat: 170ms;
  --ease: cubic-bezier(.2, .8, .3, 1);
  /* Springs, for the moments something lands. --spring overshoots about 9% and settles back, the
     way a bone tile set down on felt has weight; --spring-soft overshoots about 3%, for a sheet
     arriving. Both are a damped spring with a push at the start, sampled into linear() so they move
     from the first frame instead of easing in. A spring needs longer than a curve to say anything,
     so --t-spring is three beats - and still nothing when Motion is Instant. Browsers before
     linear() get the nearest cubic-bezier. Same values in every game in the family. */
  --spring: cubic-bezier(.3, 1.35, .5, 1);
  --spring-soft: cubic-bezier(.25, 1.1, .4, 1);
  --t-spring: calc(var(--beat) * 3);

  --type: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --sans: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
  --tile-type: "Iowan Old Style", Georgia, "Times New Roman", serif;

  --tw: 44px;            /* a tile's width. The board view sets this on .board; see there. */
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #14161a;
    --paper-2: #1b1e23;
    --ink: #ece7db;
    --ink-2: #b2ab9c;
    --ink-3: #78715f;
    --rule: #333841;

    --felt: #15322b;
    --felt-deep: #0e241f;
    --felt-edge: #081714;

    --pick: #ffc94d;
    --pick-ink: #4a3300;
  }
}

@supports (animation-timing-function: linear(0, 1)) {
  :root {
    --spring: linear(0, 0.229 3.3%, 0.433 6.7%, 0.606 10%, 0.749 13.3%, 0.862 16.7%, 0.948 20%, 1.01 23.3%, 1.067 28.3%, 1.092 33.3%, 1.089 41.7%, 1.015 66.7%, 0.996 86.7%, 1);
    --spring-soft: linear(0, 0.203 3.3%, 0.375 6.7%, 0.518 10%, 0.636 13.3%, 0.732 16.7%, 0.841 21.7%, 0.916 26.7%, 0.978 33.3%, 1.015 41.7%, 1.028 55%, 1);
  }
}

html[data-motion="fast"] { --beat: 85ms; }
html[data-motion="instant"] { --beat: 0ms; }

@media (prefers-reduced-motion: reduce) {
  html:not([data-motion="full"]) { --beat: 0ms; }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

/* The page is exactly one screen tall and the table takes whatever the chrome leaves, on a desk
   and on a phone alike. The board then sizes its tiles to the frame it is given, so nothing here
   ever needs scrolling. If a window is too short for even the chrome, the body scrolls. */
body {
  height: 100vh;
  height: 100dvh;
  overflow: auto;
  display: flex;
  justify-content: center;
  padding: clamp(6px, 2vw, 20px);
  padding-bottom: max(clamp(6px, 2vw, 20px), env(safe-area-inset-bottom));
}

#app {
  width: 100%;
  max-width: 1120px;
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1.4vw, 14px);
}

/* --- Head ------------------------------------------------------------------------------------ */

.head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

h1 {
  font-family: var(--type);
  font-size: clamp(26px, 5.2vw, 40px);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
  line-height: 1;
}

.tagline {
  margin: 4px 0 0;
  color: var(--ink-2);
  font-size: clamp(12px, 2.6vw, 15px);
}

.layouts { display: flex; gap: 6px; }

.layouts button {
  display: flex;
  flex-direction: column;
  gap: 2px;
  align-items: flex-start;
  padding: 7px 11px;
  min-height: 44px;
  border: 1px solid var(--rule);
  border-radius: 7px;
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: border-color var(--beat) var(--ease), color var(--beat) var(--ease),
              transform var(--beat) var(--ease), scale var(--t-spring) var(--spring);
}

.layouts button b { font-weight: 600; }
.layouts button span { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

.layouts button[aria-pressed="true"] {
  border-color: var(--ink);
  color: var(--ink);
  background: var(--paper-2);
}

/* --- The table ------------------------------------------------------------------------------- */

.panel {
  background: var(--felt);
  border-radius: clamp(8px, 1.4vw, 14px);
  border: 1px solid var(--felt-edge);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 2px 14px rgba(0, 0, 0, 0.18);
  padding: clamp(6px, 1.2vw, 12px);
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 1vw, 10px);
  min-width: 0;
  flex: 1 1 auto;
  min-height: 0;
}

.bar {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #e8f0ec;
  padding: 0 2px;
}

.count { display: flex; align-items: baseline; gap: 5px; }
.count[hidden] { display: none; }
.count b {
  font-family: var(--type);
  font-size: clamp(17px, 3.4vw, 22px);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.count i {
  font-style: normal;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(232, 240, 236, 0.62);
}
.count.saves b { color: var(--pick); }

.spacer { flex: 1; }

.clock {
  font-family: var(--type);
  font-size: clamp(17px, 3.4vw, 22px);
  font-variant-numeric: tabular-nums;
  color: rgba(232, 240, 236, 0.82);
}

.frame {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
  /* Clipped, but with room for a ring and a shadow: the board fills the frame exactly, and a tile
     on the edge would otherwise lose its outline the moment it is chosen or refused. */
  overflow: hidden;
  overflow: clip;
  overflow-clip-margin: 6px;
}

/* --- The board ------------------------------------------------------------------------------- */

.board {
  /* Derived HERE and not on :root: a custom property's var() is resolved where it is declared and
     the result inherited, so on :root these would freeze at 44px and every tile would be 58px tall
     whatever its width. The board view sets --tw on this element. */
  --th: calc(var(--tw) * 4 / 3);
  --stagger: calc(var(--tw) * 0.1);
  position: relative;
  width: calc(var(--across) * var(--tw) + (var(--layers) - 1) * var(--stagger));
  height: calc(var(--down) * var(--th) + (var(--layers) - 1) * var(--stagger));
}

.tile {
  position: absolute;
  left: calc(var(--x) * var(--tw) + (var(--layers) - 1 - var(--l)) * var(--stagger));
  top: calc(var(--y) * var(--th) + (var(--layers) - 1 - var(--l)) * var(--stagger));
  width: var(--tw);
  height: var(--th);
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
  /* The lift of a chosen tile is on `translate`, apart from the hover nudge on `transform`, so it
     can spring: picked up, it rises a touch past its height and settles there. */
  transition: transform var(--beat) var(--ease), translate var(--t-spring) var(--spring);
}

/* The body of the tile: a bone face with a lit top edge and two dark edges, which is the whole
   trick to making a flat rectangle read as a thick object. No images, no gradients to speak of. */
.tile-top {
  position: absolute;
  inset: 0;
  border-radius: calc(var(--tw) * 0.13);
  background: linear-gradient(176deg, var(--tile-top), var(--tile) 42%, var(--tile-dim));
  box-shadow:
    inset 0 1px 0 #fff,
    inset calc(var(--tw) * -0.07) 0 0 var(--tile-side),
    inset 0 calc(var(--th) * -0.055) 0 var(--tile-side),
    calc(var(--tw) * 0.045) calc(var(--tw) * 0.055) 0 var(--tile-side-deep),
    calc(var(--tw) * 0.06) calc(var(--tw) * 0.08) calc(var(--tw) * 0.1) rgba(0, 0, 0, 0.3);
  display: grid;
  place-items: center;
  overflow: hidden;
}

.face { width: 86%; height: 86%; display: block; }

.tile.is-blocked .tile-top { filter: saturate(0.82) brightness(0.93); }
.tile.is-blocked { cursor: default; }

.tile.is-free:hover .tile-top { background: linear-gradient(176deg, #fff, var(--tile-top) 60%, var(--tile)); }
.tile.is-free:hover { transform: translateY(-2px); }
.tile.is-free:active { transform: translateY(0) scale(0.97); }

.tile:focus-visible { outline: none; }
.tile:focus-visible .tile-top {
  box-shadow:
    0 0 0 3px var(--pick),
    calc(var(--tw) * 0.045) calc(var(--tw) * 0.055) 0 var(--tile-side-deep),
    calc(var(--tw) * 0.06) calc(var(--tw) * 0.08) calc(var(--tw) * 0.1) rgba(0, 0, 0, 0.3);
}

/* The one place the signal colour appears. */
.tile.is-selected { translate: 0 -4px; z-index: 9000 !important; }
.tile.is-selected .tile-top {
  box-shadow:
    0 0 0 3px var(--pick),
    inset 0 0 0 2px var(--pick),
    calc(var(--tw) * 0.05) calc(var(--tw) * 0.07) calc(var(--tw) * 0.14) rgba(0, 0, 0, 0.4);
}

/* Partners get an outline and no fill, so the chosen tile stays the only loud thing on the table. */
.tile.is-partner .tile-top {
  box-shadow:
    0 0 0 2px var(--pick-ink),
    inset 0 0 0 2px rgba(240, 180, 41, 0.55),
    calc(var(--tw) * 0.045) calc(var(--tw) * 0.055) 0 var(--tile-side-deep);
}

/* --- Motion ---------------------------------------------------------------------------------- */

/* On translate and scale, the same properties as the chosen tile's lift, so the lift-off takes over
   from the spring where it stands instead of the two adding up. Its length is unchanged: the
   verdict waits on it. */
@keyframes taken {
  from { opacity: 1; translate: 0 -4px; scale: 1; }
  to { opacity: 0; translate: 0 -14px; scale: 0.82; }
}
/* A tile set free gives a small bob. Left on the plain curve: it is three pixels, and a spring on
   it is invisible, while the pair that freed it is still in the air. */
@keyframes freed {
  0% { translate: 0 0; }
  45% { translate: 0 -3px; }
  100% { translate: 0 0; }
}
/* The solver's no: a sharp pull to one side and a damped wobble home, the way a spring that is
   plucked comes to rest - each swing smaller than the last. */
@keyframes refused {
  0% { translate: 0 0; }
  12% { translate: -6px 0; }
  30% { translate: 4.5px 0; }
  48% { translate: -2.6px 0; }
  66% { translate: 1.2px 0; }
  83% { translate: -0.4px 0; }
  100% { translate: 0 0; }
}
@keyframes knocked {
  0%, 100% { transform: translateY(0); }
  40% { transform: translateY(2px) scale(0.985); }
}
@keyframes hinted {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

.tile.is-freed { animation: freed calc(var(--beat) * 1.6) var(--ease); }
.tile.is-refused { animation: refused var(--t-spring) ease-in-out; }
.tile.is-refused .tile-top { box-shadow: 0 0 0 2px var(--mark-red), calc(var(--tw) * 0.045) calc(var(--tw) * 0.055) 0 var(--tile-side-deep); }
.tile.is-knocked { animation: knocked var(--beat) var(--ease); }
.tile.is-hinted { animation: hinted calc(var(--beat) * 2.4) var(--ease) 2; }
.tile.is-hinted .tile-top { box-shadow: 0 0 0 3px var(--pick), calc(var(--tw) * 0.045) calc(var(--tw) * 0.055) 0 var(--tile-side-deep); }
/* Last, so a tile taken while it is still hinted or freed lifts off rather than finishing the bob. */
.tile.is-taken { animation: taken calc(var(--beat) * 1.4) var(--ease) forwards; pointer-events: none; }

html[data-motion="instant"] .tile { animation: none !important; }

/* The beat at the end. The last pair lifts off, the table is bare, and one word settles onto the
   felt where the tiles were, with the time under it. Bone on felt, like the tiles; one movement,
   then still. Under Instant it is simply there. */
.verdict {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  text-align: center;
  color: var(--tile);
  pointer-events: none;
}
.verdict[hidden] { display: none; }
.verdict > b {
  display: block;
  font-family: var(--type);
  font-weight: 600;
  font-size: clamp(40px, 9vw, 84px);
  line-height: 1;
  letter-spacing: -0.01em;
}
.verdict > span {
  display: block;
  margin-top: 10px;
  font-family: var(--type);
  font-size: clamp(16px, 3.4vw, 24px);
  font-variant-numeric: tabular-nums;
  color: rgba(247, 242, 230, 0.78);
}
/* The word is set down on the felt: it comes in small and lands a touch too big before it settles,
   once the last pair has lifted off. What is under it follows a beat apart, each on the soft
   spring, so the end reads top to bottom. */
@keyframes settled {
  from { opacity: 0; scale: .6; translate: 0 8px; }
  25% { opacity: 1; }
  to { opacity: 1; scale: 1; translate: 0 0; }
}
@keyframes arrive {
  from { opacity: 0; translate: 0 10px; }
  35% { opacity: 1; }
  to { opacity: 1; translate: 0 0; }
}
.verdict:not([hidden]) > b { animation: settled var(--t-spring) var(--spring) calc(var(--beat) * 1.4) both; }
.verdict:not([hidden]) > span { animation: arrive var(--t-spring) var(--spring-soft) calc(var(--beat) * 2.2) both; }
.verdict:not([hidden]) .comeback { animation: arrive var(--t-spring) var(--spring-soft) calc(var(--beat) * 2.8) both; }
.verdict:not([hidden]) .strip { animation: arrive var(--t-spring) var(--spring-soft) calc(var(--beat) * 3.1) both; }
.verdict:not([hidden]) .missed { animation: arrive var(--t-spring) var(--spring-soft) calc(var(--beat) * 3.4) both; }
.verdict:not([hidden]) .next-today { animation: arrive var(--t-spring) var(--spring-soft) calc(var(--beat) * 3.4) both; }
html[data-motion="instant"] .verdict * { animation: none !important; }

/* Under the time, what comes next. After today's board: the days running and the wait for the next
   one, set like the time above it, small and settled, below a short rule. After any other board,
   while today's is still waiting: one way to it, drawn in bone on the felt like everything else on
   the table. Both arrive with the verdict and in the same movement; neither ever moves again. */
.verdict .comeback {
  margin: clamp(12px, 2.6vh, 20px) 0 0;
  font-family: var(--type);
  font-size: clamp(14px, 2.6vw, 17px);
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  color: rgba(247, 242, 230, 0.66);
}
.verdict .comeback::before {
  content: "";
  display: block;
  width: 36px;
  height: 1px;
  margin: 0 auto clamp(10px, 2vh, 14px);
  background: rgba(247, 242, 230, 0.32);
}
.verdict .comeback b { font-weight: 600; color: var(--tile); }
.verdict .comeback span { white-space: nowrap; }

/* The week under it: seven small marks, Monday to today, set in bone on the felt like the tiles.
   A day played is a filled tile-mark; a missed weekday an empty outline; a weekend left alone is
   fainter still, because it never counted against anyone; today has a ring until it is played, and
   keeps it after. The initials are small enough to be read only when looked for. */
.verdict .strip { margin-top: clamp(10px, 2vh, 14px); }
.verdict .week {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.verdict .week li { display: grid; justify-items: center; gap: 4px; }
.verdict .week i {
  display: block;
  width: 11px;
  height: 14px;
  border-radius: 3px;
  box-sizing: border-box;
  border: 1.5px solid rgba(247, 242, 230, 0.55);
}
.verdict .week [data-state="played"] i { background: var(--tile); border-color: var(--tile); }
.verdict .week [data-state="weekend"] i { border-color: rgba(247, 242, 230, 0.22); border-style: dotted; }
.verdict .week li:last-child i { outline: 1.5px solid rgba(247, 242, 230, 0.7); outline-offset: 2px; }
.verdict .week span {
  font-family: var(--sans);
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.04em;
  color: rgba(247, 242, 230, 0.5);
}
.verdict .week li:last-child span { color: rgba(247, 242, 230, 0.8); }

/* The one missed day that can still be played: a quiet line under the week, the whole of it a
   button so a thumb finds it, nothing louder than the comeback line above. */
.missed {
  pointer-events: auto;
  justify-self: center;
  margin-top: 4px;
  min-height: 40px;
  padding: 0 10px;
  border: 0;
  background: none;
  color: rgba(247, 242, 230, 0.72);
  font-family: var(--type);
  font-size: clamp(14px, 2.6vw, 16px);
  cursor: pointer;
  transition: color var(--beat) var(--ease), scale var(--t-spring) var(--spring);
}
.missed u { text-underline-offset: 3px; color: var(--tile); }
.missed:hover { color: var(--tile); }
.missed:active { scale: .95; transition-duration: var(--beat); }
.missed:focus-visible { outline: 2px solid var(--pick); outline-offset: 2px; border-radius: 6px; }

.next-today {
  pointer-events: auto;
  justify-self: center;
  margin-top: clamp(14px, 3vh, 24px);
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid rgba(247, 242, 230, 0.5);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.08);
  color: var(--tile);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background var(--beat) var(--ease), color var(--beat) var(--ease), transform var(--beat) var(--ease),
              scale var(--t-spring) var(--spring);
}
.next-today:hover { background: var(--tile); color: var(--felt-deep); border-color: var(--tile); }
.next-today:active { transform: translateY(1px); scale: .95; transition-duration: var(--beat); }
.next-today:focus-visible { outline: 2px solid var(--pick); outline-offset: 2px; }
.verdict .comeback[hidden], .verdict .next-today[hidden], .verdict .strip[hidden], .verdict .missed[hidden] { display: none; }

/* The missed day beside Today's button: the same button, quieter, a dashed edge for a day that is
   not today's. Only there while it can still be played. */
.bottom .catch-up { border-style: dashed; color: var(--ink-3); }
.bottom .catch-up[hidden] { display: none; }

/* --- Status and controls --------------------------------------------------------------------- */

/* The line under the board and, once the board is over, the result beside it. */
.result {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  padding: 0 4px;
}

.status {
  margin: 0;
  flex: 1 1 240px;
  min-height: 2.6em;
  padding: 0;
  font-size: clamp(13px, 2.8vw, 15px);
  line-height: 1.35;
  color: var(--ink-2);
}

.status[data-tone="refused"] { color: var(--ink); font-weight: 500; }
.status[data-tone="clean"] { color: var(--ink); font-weight: 600; }
.status[data-tone="bad"] { color: var(--mark-red); }

.share {
  min-height: 36px;
  padding: 0 13px;
  border: 1px solid var(--ink);
  border-radius: 7px;
  background: var(--ink);
  color: var(--paper);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: opacity var(--beat) var(--ease), scale var(--t-spring) var(--spring);
}
.share:hover { opacity: 0.88; }

.share-note { font-size: 12px; color: var(--ink-3); }
.share-note:empty { display: none; }

/* Another game from the family, once this one is over. Its own line under the result, and the
   quietest thing in the row: a nudge, not a banner. */
.also-today {
  flex-basis: 100%;
  margin: 0;
  font-size: 12px;
  color: var(--ink-3);
}
.also-today a { color: var(--ink-2); text-underline-offset: 2px; padding: 10px 2px; margin: -10px -2px; }
.also-today a:hover { color: var(--ink); }

/* Only when nothing could copy for you: the line itself, selected, to copy by hand. */
.share-text {
  flex-basis: 100%;
  max-width: 420px;
  resize: none;
  padding: 6px 8px;
  border: 1px solid var(--rule);
  border-radius: 6px;
  font: 12px/1.4 ui-monospace, Consolas, monospace;
  color: var(--ink);
  background: var(--paper-2);
}

.bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}

.actions { display: flex; gap: 6px; flex-wrap: wrap; }

button {
  font-family: var(--sans);
}

/* One family of pressed and focused looks for everything that is not a tile. */
.layouts button:focus-visible, .bottom button:focus-visible, .share:focus-visible, .wide:focus-visible,
.also-today a:focus-visible, .share-text:focus-visible {
  outline: 2px solid var(--pick);
  outline-offset: 2px;
}
.layouts button:hover { border-color: var(--ink-3); color: var(--ink); }
/* Everything a finger presses gives a little under it and springs back when it lets go: the press
   is quick, the release is the spring. `scale` on its own, so it never fights the transform. Each
   element's one transition list carries it; a second list would silently replace the first. */
.layouts button:active, .bottom button:active, .share:active, .wide:active { transform: translateY(1px); scale: .95; transition-duration: var(--beat); }

.bottom button {
  min-height: 44px;
  padding: 0 13px;
  border: 1px solid var(--rule);
  border-radius: 7px;
  background: transparent;
  color: var(--ink-2);
  font-size: 13px;
  cursor: pointer;
  transition: border-color var(--beat) var(--ease), color var(--beat) var(--ease),
              transform var(--beat) var(--ease), scale var(--t-spring) var(--spring);
}

.bottom button:hover { border-color: var(--ink-3); color: var(--ink); }
.bottom button:disabled { opacity: 0.45; cursor: default; }
.bottom button:disabled:hover { border-color: var(--rule); color: var(--ink-2); }
.bottom button[aria-pressed="true"] { border-color: var(--ink); color: var(--ink); }
.bottom .meta { font-family: var(--type); }
.bottom #help { font-weight: 600; min-width: 44px; }

/* --- Sheets ---------------------------------------------------------------------------------- */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 100000;
  background: rgba(10, 12, 11, 0.52);
  display: grid;
  place-items: center;
  padding: 12px;
  overflow: auto;
}

/* The display rule above would otherwise beat the hidden attribute, and both sheets would sit over
   the board from the first paint. Hidden means hidden. */
.overlay[hidden] { display: none; }

.sheet {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: clamp(16px, 4vw, 28px);
  max-width: 620px;
  width: 100%;
  max-height: 92vh;
  overflow: auto;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
  animation: rise var(--t-spring) var(--spring-soft);
}

/* A sheet is laid on the table: it rises into place and settles, with no more bounce than paper. */
@keyframes rise {
  from { opacity: 0; translate: 0 18px; scale: .97; }
  30% { opacity: 1; }
  to { opacity: 1; translate: 0 0; scale: 1; }
}
html[data-motion="instant"] .sheet { animation: none; }

.sheet h2 {
  font-family: var(--type);
  font-size: clamp(21px, 4.4vw, 27px);
  margin: 0 0 8px;
  font-weight: 600;
}

.sheet h3 {
  font-family: var(--type);
  font-size: clamp(15px, 3.2vw, 18px);
  margin: 22px 0 6px;
  font-weight: 600;
}

.sheet p { margin: 0 0 10px; line-height: 1.5; font-size: clamp(13px, 2.8vw, 15px); color: var(--ink-2); }
.sheet p b { color: var(--ink); }
.sheet .lede { color: var(--ink); font-size: clamp(14px, 3vw, 16px); }

.rules { margin: 14px 0 0; display: grid; grid-template-columns: auto 1fr; gap: 8px 14px; }
.rules dt { font-family: var(--type); font-weight: 600; color: var(--ink); font-size: 15px; }
.rules dd { margin: 0; color: var(--ink-2); font-size: clamp(13px, 2.8vw, 14px); line-height: 1.45; }
.rules dd b { color: var(--ink); }

.wide {
  position: sticky;
  bottom: 0;
  box-shadow: 0 0 0 8px var(--paper), 0 -12px 10px 4px var(--paper);
  width: 100%;
  margin-top: 22px;
  min-height: 46px;
  border: 1px solid var(--ink);
  border-radius: 8px;
  background: var(--ink);
  color: var(--paper);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  transition: transform var(--beat) var(--ease), scale var(--t-spring) var(--spring);
}

.records { width: 100%; border-collapse: collapse; font-size: 13px; }
.records th, .records td { text-align: right; padding: 7px 4px; border-bottom: 1px solid var(--rule); }
.records thead th { color: var(--ink-3); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; }
.records tbody th { text-align: left; font-family: var(--type); font-weight: 600; }
.records tbody th em { display: block; font-style: normal; font-size: 11px; color: var(--ink-3); font-family: var(--sans); font-weight: 400; }
.records td { font-variant-numeric: tabular-nums; color: var(--ink-2); }
.note { margin-top: 14px; font-size: 12px; color: var(--ink-3); line-height: 1.45; }

/* --- Phones ---------------------------------------------------------------------------------- */

/* Held upright, everything is on one screen and the page never scrolls: a compact head, the table
 * filling whatever is left, the line underneath, and one wrapping row of buttons. The board sizes
 * its tiles to the frame it is given, so the frame is the one thing allowed to flex. */
@media (max-width: 560px) and (orientation: portrait) {
  body { padding: 6px 6px max(6px, env(safe-area-inset-bottom)); }
  #app { gap: 6px; }

  .head { align-items: center; gap: 6px; }
  .title { flex: 1; }
  .tagline { display: none; }
  h1 { font-size: 24px; }
  .layouts { width: 100%; gap: 5px; }
  .layouts button { flex: 1; align-items: center; min-height: 38px; padding: 4px 6px; font-size: 12px; }
  .layouts button span { font-size: 11px; }

  .panel { padding: 6px; gap: 4px; }

  .result { gap: 4px 8px; padding: 0 2px; }
  .status { min-height: 2.6em; font-size: 13px; line-height: 1.3; }
  .share { min-height: 34px; font-size: 12px; }
  /* One line at most, so the end of a board takes no more of the table than it must. */
  .also-today { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  /* Every button in one wrapping row, Records included. */
  .bottom { justify-content: center; gap: 5px; }
  .actions { display: contents; }
  .bottom button { flex: 1 1 auto; min-height: 38px; padding: 0 9px; font-size: 12px; }
  /* Reduced motion follows the system setting on a phone; the toggle is not worth a row. */
  #motion { display: none; }
}

/* A phone on its side: width to spare and almost no height, so everything but the table moves
 * into a column beside it, and the table takes the full height. */
@media (orientation: landscape) and (max-height: 520px) {
  body { padding: 6px; }
  #app {
    display: grid;
    grid-template-columns: 190px minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    gap: 6px 10px;
  }

  .head { grid-column: 1; grid-row: 1; flex-direction: column; align-items: stretch; gap: 6px; }
  .tagline { display: none; }
  h1 { font-size: 22px; }
  .layouts { width: 100%; gap: 4px; }
  .layouts button { flex: 1; align-items: center; min-height: 34px; padding: 3px 4px; font-size: 11px; }
  .layouts button span { display: none; }

  .panel { grid-column: 2; grid-row: 1 / 4; padding: 6px; gap: 4px; }

  /* A column here, and not a wrapping one: in a column, a 100% flex-basis would be a height. */
  .result { grid-column: 1; grid-row: 2; flex-direction: column; flex-wrap: nowrap; align-items: stretch; min-height: 0; overflow: auto; gap: 6px; padding: 0; }
  .status { flex: none; min-height: 0; font-size: 12px; line-height: 1.3; }
  .share { flex: none; min-height: 34px; font-size: 12px; }
  .also-today, .share-text { flex: none; }
  .share-text { max-width: none; }

  .bottom { grid-column: 1; grid-row: 3; flex-direction: column; align-items: stretch; gap: 4px; }
  .bottom .meta { width: 100%; }
  .actions { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
  .bottom button { min-height: 34px; padding: 0 6px; font-size: 11px; }
  #motion { display: none; }
}
