/* The browser table's look - the Mac app's, see TableView.swift and
   TableChrome.swift: the felt, the plates and bubbles, the bows, the
   trick, the panels, the side, the lobby, and the phone. */
  /* The table in the browser - one page, no framework, written from
     protocol/PROTOCOL.md alone. It draws what the wire tells it and nothing more:
     the rules stay on the server. The look follows the Mac app's table
     (TableView.swift): the same held bow of cards, the same seat plates,
     the same deck (cards.js is CardViews.swift in SVG). */
  /* the two themes' colours come from design/theme.json (Theme.swift's
     numbers), set by Settings/Theme.js; these are the dark ones so the page
     is never colourless. The table's chrome - panel, bubbles, buttons -
     keeps its explicit colours in both themes, as in the app. */
  :root { --ink: #fff; --ink-rgb: 255,255,255; --backdrop-top: #17382a; --backdrop-bottom: #0d1f17;
          --felt-top: #1a4d30; --felt-bottom: #0f301f; --warning: #ffd14d;
          --felt-ink: #f4efe2; --gold: #f2c94c;
          /* a button's words (design/theme.json buttons, re-set by Theme.js) */
          --button-text: 13px; --button-primary: 17px; --panel: #2b2b2e; --cw: 72px; --ch: 104px;
          /* the deck's corner and its shadow. --radius is re-set from
             design/table.json's card.cornerRadius wherever a felt is
             measured; this is the fallback for screens without one. */
          --radius: 8px; --cardshadow: 0 2px 3px rgba(0,0,0,.35); }
  * { box-sizing: border-box; }
  body { margin: 0; font: 15px/1.4 -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;
         background: #1e2022; color: var(--felt-ink); }
  /* the Sideboard's width and the felt's share on a phone come
     from design/table.json (layout), set by App.js - the Mac app's */
  main { display: grid; grid-template-columns: 1fr var(--sideboardw, 340px); gap: 0; height: 100vh;
         position: relative; }   /* the "i" card hangs in here (placeInfoCard) */

  /* the menus: the intro, the settings, the help, the archive - the app's
     backdrop and ink (Theme.swift), swapped whole by the theme */
  .menu { min-height: 100vh; position: relative; background: linear-gradient(var(--backdrop-top), var(--backdrop-bottom));
          color: var(--ink); display: flex; flex-direction: column; align-items: center; gap: 26px; padding: 40px 16px; }
  .menu .title { font: 900 44px/1.1 Georgia, "Times New Roman", serif; margin: 0; text-align: center; }
  .menu .title.small { font: 700 28px/1.2 -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif; }
  .corner { position: absolute; top: 22px; right: 22px; display: flex; gap: 18px; }
  /* the way back, top left as well - a chevron and nothing else, the word as its tooltip (BackLink, Sheet.kt); the pill at the foot stays */
  .menu .backlink { position: absolute; top: 20px; left: 20px; width: 34px; height: 34px; border: 0; padding: 8px; border-radius: 50%;
                    background: rgba(var(--ink-rgb), .12); color: rgba(var(--ink-rgb), .75); cursor: pointer; }
  .menu .backlink svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
  .menu .backlink:hover { color: rgba(var(--ink-rgb), .9); }
  body.narrow .menu .backlink { top: 14px; left: 10px; }
  .icon-btn { width: 28px; height: 28px; padding: 0; border: 0; background: none; color: rgba(var(--ink-rgb), .55); cursor: pointer; }
  .icon-btn svg { width: 100%; height: 100%; fill: currentColor; }
  .icon-btn:hover { color: rgba(var(--ink-rgb), .85); }
  .footer { font-size: 12px; color: rgba(var(--ink-rgb), .45); min-height: 1em; }
  .seg { display: flex; background: rgba(var(--ink-rgb), .10); border-radius: 8px; padding: 2px; width: min(560px, 100%); }
  .seg.w420 { width: min(420px, 100%); }
  .seg button { flex: 1; border: 0; background: none; padding: 6px 4px; font-size: var(--button-text); color: inherit; border-radius: 6px; cursor: pointer;
                display: flex; align-items: center; justify-content: center; gap: 6px; }
  /* the game symbols (design/symbols) beside the games' names, in the ink around them */
  .seg button svg, .title svg { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }
  .seg button svg { width: 16px; height: 16px; }
  .seg button span { white-space: nowrap; }
  .title svg { width: 28px; height: 28px; vertical-align: -5px; margin-right: 10px; }
  .seg button.on { background: #fff; color: #1a241f; box-shadow: 0 1px 3px rgba(0,0,0,.25); }
  .seg button.later { border: 1.5px dashed rgba(var(--ink-rgb), .35); opacity: .6; cursor: default; }
  .tiles { display: grid; grid-template-columns: repeat(auto-fit, 280px); gap: 18px; justify-content: center; width: min(578px, 100%); }
  .tile { width: 280px; height: 130px; border-radius: 16px; background: rgba(var(--ink-rgb), .10); border: 1px solid rgba(var(--ink-rgb), .25);
          color: inherit; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
          cursor: pointer; box-shadow: 0 6px 14px rgba(0,0,0,.18); font: inherit; }
  .tile svg { width: 34px; height: 34px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .tile b { font-size: 19px; } .tile small { font-size: 12px; opacity: .6; }
  .tile:hover:not([disabled]) { background: rgba(var(--ink-rgb), .14); }
  .tile[disabled] { background: rgba(var(--ink-rgb), .06); border: 1.5px dashed rgba(var(--ink-rgb), .35); opacity: .65; cursor: default;
                    box-shadow: 0 2px 5px rgba(0,0,0,.12); }
  body.narrow .tiles { grid-template-columns: 1fr; width: 100%; } body.narrow .tile { width: 100%; }
  /* Weiterspielen: the game you left, above the games - two doors wide, low, in gold */
  .tile.resume { width: 578px; max-width: 100%; height: 92px; flex-direction: row; justify-content: flex-start; gap: 16px; padding: 0 20px;
                 border: 1.5px solid rgba(232, 190, 40, .75); text-align: left; }
  .tile.resume svg { width: 46px; height: 46px; padding: 12px 10px 12px 14px; box-sizing: border-box; border-radius: 50%; flex: none;
                     background: rgba(240, 200, 40, .9); color: #000; stroke: none; }
  .tile.resume > span { display: flex; flex-direction: column; gap: 4px; min-width: 0; }   /* the words; not a portrait inside them */
  .tile.resume small { white-space: normal; }
  .tiles:has(.resume) { grid-template-columns: 1fr; }
  body.narrow .tile.resume { width: 100%; height: auto; min-height: 84px; padding: 10px 16px; }
  .archive .record { background: rgba(var(--ink-rgb), .10); border-radius: 12px; padding: 10px 14px; width: min(640px, 100%); margin: 0 auto 10px; }
  .archive .record > .box.pieces { margin-top: 8px; width: 100%; }   /* the coaches on a game (🎓): their box in its card, */
  .archive .record:has(> .box.pieces) { width: min(760px, 100%); }   /* the card wider, so their tables fit */
  body.narrow .archive .record .box { padding: 12px 8px; }            /* on a phone the boxes in it give their room to the tables */
  .archive .record .when { font-size: 12px; opacity: .6; } .archive .record .how { font-size: 14px; }
  .archive .empty, .archive .sum.note { opacity: .7; text-align: center; white-space: pre-line; min-height: 200px; }
  .archive .record .head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; } .archive .record .head .when { margin-left: auto; }
  .archive .record .actions { display: flex; justify-content: flex-end; align-items: center; gap: 8px; margin-top: 6px; }
  .archive .record .actions .moves { flex: 1; font-size: 12px; opacity: .45; font-variant-numeric: tabular-nums; }
  .archive .record .actions > .what { flex: 1; min-width: 0; font-size: 13px; opacity: .75; }   /* who sat and how it stands, then the buttons */
  /* on a phone the count has a line of its own, the buttons the whole width (the Mac's narrow actions row) */
  body.narrow .archive .record .actions { flex-wrap: wrap; } body.narrow .archive .record .actions .moves, body.narrow .archive .record .actions > .what { flex: 1 0 100%; }
  .archive .record .badge { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 9px; background: rgba(245, 200, 60, .8); color: #1a1a1a; font-size: 11px; font-weight: 600; }
  .archive .record .how.dim { opacity: .6; }
  .archive .record.arena { cursor: pointer; } .archive .record .fold { font-size: 11px; opacity: .7; }
  .archive .record .standings { margin: 8px 0 4px 18px; font-size: 14px; }
  .archive .seg.shelves { width: min(640px, 100%); margin-bottom: 4px; } body.narrow .archive .seg.shelves { flex-wrap: wrap; } body.narrow .archive .seg.shelves button { flex: 1 1 30%; }
  .archive .shelf { width: 100%; min-height: 200px; }
  /* Auswählen: the bar above the shelves (RecordsView.selectionBar), and a tick on every row */
  .archive .selbar { width: min(640px, 100%); margin: 0 auto 10px; display: flex; align-items: center; gap: 8px; }
  .archive .selbar .spacer { flex: 1; } .archive .selbar .question { font-size: 14px; opacity: .85; }
  .archive .record.selectable { position: relative; padding-left: 42px; cursor: pointer; user-select: none; }
  /* the settings' Bots tab: a player on a card, folded behind its name, its knobs as entry rows (SettingsView.swift) */
  .settings .record { background: rgba(var(--ink-rgb), .10); border-radius: 12px; padding: 10px 14px; width: min(680px, 100%); margin: 0 0 10px; }
  .settings .record.arena { cursor: pointer; } .settings .record .fold { font-size: 11px; opacity: .7; margin-right: 6px; }
  .settings .record .badge { display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 9px; background: rgba(245, 200, 60, .8); color: #1a1a1a; font-size: 11px; font-weight: 600; }
  .settings .record .how { font-size: 13px; opacity: .6; margin-top: 4px; }
  .settings .record .entry { cursor: default; } .settings .record .entry .choice input[type=range] { width: 150px; }
  .settings .record .entry .seg { width: min(260px, 100%); margin-left: auto; } .settings .record .actions { display: flex; justify-content: flex-end; margin-top: 6px; }
  .archive .record .tick { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-size: 20px; line-height: 1; opacity: .45; }
  .archive .record.ticked { box-shadow: inset 0 0 0 2px rgba(245, 200, 60, .85); } .archive .record.ticked .tick { opacity: 1; color: #f5c83c; }
  /* SoftButton: a soft capsule in the ink */
  .pill { background: rgba(var(--ink-rgb), .10); color: rgba(var(--ink-rgb), .75); border: 1px solid rgba(var(--ink-rgb), .22);
          padding: 8px 24px; border-radius: 999px; font: inherit; font-size: var(--button-text); cursor: pointer; box-shadow: 0 2px 4px rgba(0,0,0,.2); }
  .pill:hover { background: rgba(var(--ink-rgb), .16); }
  /* the settings: one box per section (SettingsView.swift) */
  .box { background: rgba(var(--ink-rgb), .08); border-radius: 14px; padding: 16px; width: min(720px, 100%); }
  .box h3 { margin: 0 0 10px; font-size: 11px; font-weight: 600; letter-spacing: .6px; text-transform: uppercase; color: rgba(var(--ink-rgb), .5); }
  .box h3 svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; vertical-align: -3px; margin-right: 6px; }
  .box .note { margin: 8px 0 0; font-size: 12px; color: rgba(var(--ink-rgb), .55); }
  .swatches { display: flex; flex-wrap: wrap; gap: 14px; }   /* the appearances wrap into rows, however many there are */
  /* the first start: the app's wizard, centred, one question at a time */
  .wizard .setup-body { display: flex; flex-direction: column; align-items: center; gap: 22px; }
  .wizard .welcome { font-size: 22px; font-weight: 700; margin: 0; }
  .wizard .seg { justify-content: center; }
  .swatch { background: none; border: 0; padding: 0; color: inherit; font: inherit; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 8px; }
  .swatch > div { width: 96px; height: 96px; border-radius: 10px; border: 1px solid rgba(var(--ink-rgb), .2); overflow: hidden; }
  .swatch.on > div { border: 2.5px solid rgba(255,220,0,.9); } .swatch.on span { font-weight: 700; }
  /* the appearance's line under the tiles, and the editor it opens
     (SettingsView.swift: AppearanceList / AppearanceEditor) */
  .appearance-row { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
  .appearance-row .sub { opacity: .65; font-size: 13px; margin-right: auto; }
  .appearance-editor { margin-top: 12px; padding: 14px; border-radius: 12px;
                       background: rgba(var(--ink-rgb), .07); display: flex;
                       flex-direction: column; gap: 10px; }
  .appearance-editor .choice { display: flex; align-items: center; gap: 12px; }
  .appearance-editor .choice .lbl { width: 150px; flex: none; opacity: .7; font-size: 13px; }
  .appearance-editor .name { flex: 1; max-width: 260px; }
  .books .row { display: flex; gap: 12px; align-items: center; padding: 6px 0; }
  .books .name { width: 190px; font-weight: 600; } .books .sum { flex: 1; font-size: 12px; color: rgba(var(--ink-rgb), .55); line-height: 1.4; }
  .books .row button { background: #fff; color: #1a241f; border: 0; border-radius: 6px; padding: 4px 10px; font: inherit; font-size: var(--button-text); cursor: pointer; }
  .books .row button[disabled] { opacity: .45; }
  .books .seg { margin-bottom: 8px; }
  .box .entry { display: flex; gap: 12px; align-items: center; padding: 6px 0; }
  .box .entry .name { width: 190px; font-weight: 600; } .box .entry .sum { flex: 1; font-size: 12px; color: rgba(var(--ink-rgb), .55); line-height: 1.4; }
  body.narrow .box .entry { flex-wrap: wrap; } body.narrow .box .entry .name { width: auto; flex: 1; } body.narrow .box .entry .sum { order: 3; flex-basis: 100%; }
  .stack { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }   /* two choices under each other in one box */
  /* the help book: the contents left, the page right (HelpView.swift) */
  .book { display: flex; gap: 18px; height: 470px; max-width: 100%; }
  .topics { width: 260px; background: rgba(var(--ink-rgb), .06); border-radius: 14px; padding: 8px; overflow: auto; }
  .topics h4 { margin: 6px 12px 2px; font-size: 10px; font-weight: 600; letter-spacing: .6px; text-transform: uppercase; color: rgba(var(--ink-rgb), .45); }
  .topics h4 + h4, .topics .topic + h4 { margin-top: 16px; }
  .topic { display: flex; gap: 10px; align-items: center; padding: 8px 12px; border-radius: 9px; cursor: pointer; font-size: 15px; }
  .topic.on { background: rgba(var(--ink-rgb), .12); font-weight: 600; }
  .topic svg { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: .5; }
  .page { width: 560px; background: rgba(var(--ink-rgb), .06); border-radius: 14px; padding: 20px; overflow: auto; }
  .page h2 { display: flex; gap: 10px; align-items: center; margin: 0 0 14px; font-size: 19px; }
  .page h2 svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: .6; }
  .page p, .page ul { margin: 0 0 14px; font-size: 15px; line-height: 1.45; color: rgba(var(--ink-rgb), .8); }
  .page ul { list-style: none; padding: 0; } .page li { margin: 0 0 6px; }
  /* a page's words without its own box: in a box (Learning's rules and tips), in an ⓘ */
  .prose p, .prose ul { margin: 0 0 12px; font-size: 15px; line-height: 1.45; color: rgba(var(--ink-rgb), .8); }
  .prose ul { list-style: none; padding: 0; } .prose li { margin: 0 0 6px; } .prose > :last-child { margin-bottom: 0; }
  body.narrow .book { flex-direction: column; height: auto; } body.narrow .topics, body.narrow .page { width: 100%; }
  /* on a phone the contents are a strip of topics above the page, as the app's */
  body.narrow .topics { display: flex; gap: 8px; overflow-x: auto; padding: 0 0 4px; background: none; }
  body.narrow .topics h4, body.narrow .topic svg { display: none; }
  body.narrow .topic { flex: none; padding: 6px 12px; border-radius: 999px; background: rgba(var(--ink-rgb), .07); font-size: var(--button-text); white-space: nowrap; }
  body.narrow .topic.on { background: rgba(var(--ink-rgb), .16); }
  /* the rule-book editor, in the settings' place (RulesEditor.swift) */
  .editor { width: min(600px, 100%); display: flex; flex-direction: column; gap: 16px; }
  .editor .bookname { font-size: 19px; font-weight: 700; }
  .editor input.bookname { width: 280px; padding: 6px 10px; border-radius: 8px; border: 1px solid rgba(var(--ink-rgb), .3); background: rgba(255,255,255,.7); color: #1a241f; }
  .pane { background: rgba(var(--ink-rgb), .08); border: 1px solid rgba(var(--ink-rgb), .15); border-radius: 14px; padding: 16px;
          display: flex; flex-direction: column; gap: 14px; }
  .pane h2 { margin: 0; font-size: 19px; }
  .pane .note { margin: 0; color: rgba(var(--ink-rgb), .6); }
  .psec { display: flex; flex-direction: column; gap: 7px; }
  .psec h4 { margin: 0; font-size: 11px; font-weight: 600; letter-spacing: .6px; text-transform: uppercase; color: rgba(var(--ink-rgb), .5); }
  .toggle { display: flex; align-items: center; gap: 8px; font-size: 15px; } .toggle.off { opacity: .45; }
  .stepper { display: flex; align-items: center; gap: 6px; font-size: 15px; } .stepper.off .lbl { opacity: .4; }
  .stepper .lbl { width: 90px; } .stepper .val { width: 40px; text-align: right; font-variant-numeric: tabular-nums; }
  .stepper button, .brow button { border: 1px solid rgba(var(--ink-rgb), .25); background: rgba(255,255,255,.6); color: #1a241f; border-radius: 6px;
                                  width: 26px; height: 24px; cursor: pointer; font: inherit; line-height: 1; }
  .stepper button[disabled] { opacity: .3; cursor: default; }
  .brow { display: flex; gap: 10px; } .brow button { width: auto; padding: 0 10px; font-size: var(--button-text); }
  .radio label { display: flex; gap: 8px; align-items: center; font-size: 15px; padding: 2px 0; }
  .choice { display: flex; align-items: center; gap: 10px; font-size: 15px; }
  .editor .footer-row, .appearance-editor .footer-row { display: flex; align-items: center; gap: 10px; } .editor .spacer, .appearance-editor .spacer { flex: 1; }
  .editor .footer-row button, .appearance-editor .footer-row button { font: inherit; font-size: var(--button-text); border: 0; border-radius: 999px; padding: 8px 20px; cursor: pointer; }
  .editor button.quiet, .appearance-editor button.quiet { background: rgba(var(--ink-rgb), .10); color: rgba(var(--ink-rgb), .8); }
  .editor button.danger { background: none; color: rgba(220, 40, 40, .85); }
  .editor button.gold, .appearance-editor button.gold { background: var(--gold); color: #000; font-weight: 700; padding: 9px 34px; box-shadow: 0 2px 5px rgba(0,0,0,.25); }
  .editor .warn { display: inline-flex; align-items: center; gap: 8px; padding: 7px 14px; border-radius: 999px; font-weight: 500; font-size: 15px;
                  color: var(--warning); background: rgba(242, 184, 64, .22); border: 1px solid rgba(242, 184, 64, .35); }
  .editor .warn::before { content: '⚠︎'; }
  .seg.w220 { width: min(220px, 100%); } .seg.w260 { width: min(260px, 100%); }
  /* the seat setup: the rules first, you, the characters, the options (SeatSetupView.swift) */
  .setup-body { width: min(1000px, 100%); display: flex; flex-direction: column; align-items: center; gap: 20px; }
  .setup-body .title { margin-bottom: 6px; }
  /* the Tschau Sepp centre: the draw pile (a beacon when drawing is the only move), the discard, the wish chip (TschauSeppView.swift) */
  .ts-pile, .ts-discard { position: absolute; width: 0; height: 0; z-index: var(--z-trick, 2050); }   /* on the cloth, where a Jass trick lies */
  .ts-pile .card, .ts-discard .card { position: absolute; left: 0; top: 0; }
  .ts-pile { cursor: pointer; } .ts-pile.empty { opacity: .35; }
  .ts-pile.beacon .card:last-of-type { outline: 3px solid rgba(242,201,76,.9); outline-offset: -3px; animation: beacon .7s ease-in-out infinite alternate; }
  @keyframes beacon { from { transform: translate(-50%, -50%) translate(4px, -4px) scale(1); } to { transform: translate(-50%, -50%) translate(4px, -4px) scale(1.06); opacity: .7; } }
  .ts-badge { position: absolute; left: 0; transform: translate(-50%, -50%); background: rgba(242,201,76,.92); color: #000; font-weight: 700; padding: 3px 10px; border-radius: 12px; white-space: nowrap; font-size: 14px; box-shadow: 0 2px 6px rgba(0,0,0,.4); }
  .ts-chip { position: absolute; transform: translate(-50%, -50%); width: 34px; height: 34px; border-radius: 50%; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; }
  .ts-chip .disc { width: 22px; height: 22px; }
  .team.ts { display: flex; justify-content: space-between; align-items: baseline; } .team.ts .pts.small { font-size: 13px; }
  /* the tournament: the crosstable and the progress bar (TournamentViews.swift) */
  .progress { width: min(460px, 100%); text-align: center; }
  .progress .bar { height: 6px; border-radius: 3px; background: rgba(var(--ink-rgb), .15); overflow: hidden; }
  .progress .fill { height: 100%; background: rgba(var(--ink-rgb), .6); transition: width .3s; }
  .standings { border-collapse: collapse; margin: 0 auto; font-variant-numeric: tabular-nums; }
  .standings th { font-size: 10px; font-weight: 600; letter-spacing: .5px; text-transform: uppercase; color: rgba(var(--ink-rgb), .5); padding: 4px 12px; }
  .standings td { padding: 5px 12px; text-align: right; } .standings td.name, .standings th.name { text-align: left; min-width: 150px; }
  #crosstable td, #crosstable th { padding: 4px 8px; }
  /* a table wider than its box scrolls sideways inside it - the box keeps to the screen (ScreenView.js grid) */
  .grid-scroll { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
  body.narrow .standings th, body.narrow .standings td { padding-left: 8px; padding-right: 8px; }
  body.narrow .standings td { font-size: 13px; } body.narrow .standings th { font-size: 11px; }   /* geier's sizes */
  body.narrow .standings td.name, body.narrow .standings th.name { min-width: 110px; }
  .standings td.up { color: #5fc878; } .standings td.down { color: #e86b5c; }
  .box .busy { position: absolute; top: 8px; right: 12px; opacity: .5; } .box { position: relative; }
  .warn { color: #e0b000; font-size: 13px; }
  #tournaments h3 { margin: 0 0 8px; font-size: 13px; text-transform: uppercase; letter-spacing: .5px; opacity: .6; }
  .box.wide { width: min(1000px, 100%); }
  .row-box { display: flex; align-items: center; gap: 12px; }
  .row-box .lbl { font-size: 15px; } .row-box .sum { flex: 1; font-size: 12px; color: rgba(var(--ink-rgb), .6); }
  .row-box .lbl + select { width: 72px; }
  .row-box select, .seat-row select, .options select { min-width: 200px; }
  /* EVERY small button looks alike: the archive's rounded, bordered pill,
     not the browser's own grey box. :where() keeps the rule weightless,
     so a place with a size of its own (the rules editor's steppers) still
     sets it. */
  :where(button.small) { border: 1px solid rgba(var(--ink-rgb), .25); background: rgba(255,255,255,.7); color: #1a241f; border-radius: 6px; padding: 4px 10px; font: inherit; font-size: var(--button-text); cursor: pointer; }
  :where(button.small:not(.gold):not([disabled]):hover) { background: rgba(255,255,255,.85); }
  /* one sign for playing a game (ScreenView.js): Weiterspielen's gold disc with its ▶ (.tile.resume svg),
     Nachspielen's blue one with its ↻; the disc alone, its words the tooltip - and the coaches' orange one with the 🎓 */
  .sign-disc { flex: none; width: 22px; height: 22px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; color: #10151a; }
  .sign-disc.resume { background: rgba(240, 200, 40, .9); }
  .sign-disc.replay { background: rgb(92, 168, 240); }
  .sign-disc.coach { background: rgb(255, 159, 10);   /* geier's coach orange */ font-size: 16px; line-height: 1; }
  .sign-disc.resume svg { width: 13px; height: 13px; margin-left: 2px; stroke: none; }
  .sign-disc.replay svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
  button.sign-alone { flex: none; padding: 0; border: 0; background: none; cursor: pointer; display: inline-flex; border-radius: 50%; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
  button.sign-alone .sign-disc { width: 28px; height: 28px; }
  button.sign-alone .sign-disc svg { width: 16px; height: 16px; }
  button.sign-alone:not([disabled]):hover .sign-disc { filter: brightness(1.08); }
  button.sign-alone[disabled] { opacity: .4; cursor: default; }
  /* the archive's ★ and trash can (ScreenView.js): plain signs a disc's size, the words the tooltip */
  button.sign-plain { flex: none; width: 28px; height: 28px; padding: 0; border: 0; background: none; cursor: pointer;
                      display: inline-flex; align-items: center; justify-content: center; color: rgba(var(--ink-rgb), .45); }
  button.sign-plain svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  button.sign-plain.star.on svg { fill: rgb(245, 200, 40); stroke: rgb(205, 150, 10); }
  button.sign-plain.trash { color: rgba(220, 40, 40, .85); }
  button.sign-plain:not([disabled]):hover { filter: brightness(1.15); }
  button.sign-plain[disabled] { opacity: .4; cursor: default; }
  :where(button.small.danger) { color: rgba(220, 40, 40, .9); }
  /* the one that goes somewhere - Nachspielen, Wiederholen - is gold, as on the Mac and the phone */
  :where(button.small.gold) { background: var(--gold); border-color: transparent; font-weight: 600; }
  :where(button.small[disabled]) { opacity: .45; cursor: default; }
  /* and every dropdown is one of them, with a chevron of its own drawn in
     the ink of the words - the browser's grey box and arrow nowhere */
  :where(select) { appearance: none; -webkit-appearance: none; font: inherit; font-size: 14px; color: #1a241f; cursor: pointer;
                   border: 1px solid rgba(var(--ink-rgb), .25); border-radius: 6px; padding: 4px 28px 4px 10px;
                   background: rgba(255,255,255,.7) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%231a241f' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 10px center / 10px 6px; }
  :where(select:not([disabled]):hover) { background-color: rgba(255,255,255,.85); }
  :where(select[disabled]) { opacity: .45; cursor: default; }
  .setup-body input.name { width: 200px; font: inherit; font-size: 15px; padding: 6px 10px; border-radius: 8px; border: 1px solid rgba(var(--ink-rgb), .3); background: rgba(255,255,255,.75); color: #1a241f; }
  /* the profile row: who plays, its face, and a name for a new one or a rename (SeatSetupView.swift) */
  .profile-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .picker-grid { display: grid; gap: 8px; max-width: 100%; }   /* rows of pickers sharing their columns */
  .picker-grid > select { width: 100%; min-width: 0; }
  .box.pieces > .how { font-size: 13px; opacity: .6; }   /* a box's grey line: the Mac's caption, the phone's 13 */
  .profile-row .face { width: 70px; }
  .profile-row input.name { width: 170px; font-size: 14px; padding: 3px 8px; border-radius: 6px; }   /* as tall as the buttons beside it */
  .seats { display: flex; flex-direction: column; gap: 10px; width: auto; }
  .box.pieces { display: flex; flex-direction: column; gap: 10px; }
  .box.pieces > h3 { margin-bottom: 0; }
  .box-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }   /* a title and its ⓘ */
  .box-head > h3 { margin: 0; }
  /* a form line (row style form): the labels in one column, the controls beside them */
  .form-row { display: grid; grid-template-columns: 130px 1fr; align-items: center; gap: 12px; font-size: 15px; }
  .form-row .form-label { color: rgba(var(--ink-rgb), .6); }
  .form-row .form-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .form-row .form-controls select { min-width: 220px; }
  .form-row .form-controls input { width: 220px; box-sizing: border-box; }
  .form-row .form-controls .seg { width: auto; flex: 0 1 auto; }
  .form-row .form-controls .seg button { padding: 6px 14px; }
  body.narrow .form-row { grid-template-columns: 1fr; gap: 4px; }
  /* a chair: who - the character -, what it plays as, its strength where it has one, its build; its temper on the line below */
  .seat-row { display: grid; grid-template-columns: 64px 160px 130px 100px 1fr; align-items: center; column-gap: 10px; row-gap: 2px; font-size: 15px; }
  .seat-row select { min-width: 0; width: 100%; }
  .seat-row .skill { grid-column: 5; }
  .seat-row .temper { grid-column: 2 / span 4; }
  .seat-row .skill, .options .skill { font-size: 12px; color: rgba(var(--ink-rgb), .55); font-variant-numeric: tabular-nums; }
  .seat-row .temper { font-size: 12px; font-style: italic; color: rgba(var(--ink-rgb), .45); }
  /* the clock's word on a chair or an entrant (Clock.js): a line of its own, under the pickers */
  .seat-row > .how, .seat-row > .warn { grid-column: 2 / -1; font-size: 12px; }
  .options { display: flex; gap: 26px; align-items: center; flex-wrap: wrap; justify-content: center; }
  .box > .options { justify-content: flex-start; }   /* in its section it starts at the left, as every box's pieces do (the Mac's) */
  .options .choice { display: flex; align-items: center; gap: 10px; font-size: 15px; }
  .options input[type=range] { width: 150px; }
  .setup-body .buttons { display: flex; gap: 16px; align-items: center; }
  .start { background: var(--gold); color: #000; border: 0; border-radius: 999px; padding: 12px 44px; font: inherit; font-size: var(--button-primary); font-weight: 700; cursor: pointer; box-shadow: 0 3px 8px rgba(0,0,0,.3); }
  .start[disabled] { opacity: .5; cursor: default; box-shadow: none; }          /* shut, and it looks it (the Mac's primary at half) */
  body.narrow .seat-row { grid-template-columns: 1fr; gap: 4px; } body.narrow .seat-row .skill, body.narrow .seat-row .temper, body.narrow .seat-row > .how, body.narrow .seat-row > .warn { grid-column: auto; } body.narrow .row-box { flex-wrap: wrap; }

  /* The table screen is a FLOOR: design/floor/wood.svg, one tile
     repeated at the design's own size (App.js publishes it, because
     the SVG's natural size and the phone's rendered PNG are two other
     numbers). The Jassteppich lies on it. */
  .table { position: relative; overflow: hidden;
           background: var(--floor, url('design/floor/wood.svg')) repeat;
           background-size: var(--floor-size, var(--floortile, 300px) var(--floortile, 300px)); }
  /* THE TABLE the cloth lies on and the hands rest on - between the
     floor and the felt. One drawing stretched over it (no tile, no
     seam), round or square as the appearance says; measure() gives it
     its place and its size, applyLook() its stone and its shape. */
  #tabletop { position: absolute; transform: translate(-50%, -50%); z-index: var(--z-tabletop, 1500);
              background-size: 100% 100%; background-repeat: no-repeat;
              border: var(--tableedge, 3px) solid var(--tableedgeink, rgba(0,0,0,.35));
              box-shadow: var(--tableshadow); border-radius: var(--tablecorner, 28px); }
  #tabletop.round { border-radius: 50%; }
  /* on water it floats (the Badi): no shadow, rings round the rim instead - Theme.js lays them */
  #tabletop.floats { box-shadow: none; }
  /* The sun's light on the pool, drifting over the floor (Theme.js applyLook): a tile
     wider than the table on every side, moved by whole tiles - no seam when it loops -
     and moved by the compositor, not repainted. Still for whoever asked for less motion. */
  .table > .shimmer { position: absolute; inset: calc(-1 * var(--floortile, 300px)); pointer-events: none;
                      z-index: var(--z-background, 0); background-repeat: repeat;
                      background-size: var(--floortile, 300px) var(--floortile, 300px);
                      animation: drift linear infinite; will-change: transform; }
  @keyframes drift { from { transform: translate(0, 0); }
                     to { transform: translate(calc(var(--dx) * var(--floortile, 300px)), calc(var(--dy) * var(--floortile, 300px))); } }
  /* Now and then a breeze over the lawn (Theme.js applyLook): a band slanted across
     the table, carried from wholly off the left to wholly off the right. */
  .table > .breeze { position: absolute; inset: 0; pointer-events: none; z-index: var(--z-background, 0);
                     animation: breeze linear infinite; will-change: transform; }
  .table > .breeze > div { position: absolute; top: -50%; height: 200%; width: var(--bw, 45%);
                           left: calc(50% - var(--bw, 45%) / 2); transform: rotate(var(--bd, 20deg));
                           background: linear-gradient(90deg, transparent, rgba(255,255,255,var(--bo, .08)), transparent); }
  @keyframes breeze { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
  /* The water round a floating table - its shade, its rings, their glints - drawn by
     TableView.js water() under the table. On water the table DRIFTS (--bx, --by, set by
     the same loop) and all that lies on it with it; the panels and the controls stay. */
  #water { position: absolute; pointer-events: none; overflow: visible; transform: translate(-50%, -50%);
           z-index: calc(var(--z-tabletop, 1500) - 1); }
  #afloat { position: absolute; inset: 0; pointer-events: none; }
  #afloat > * { pointer-events: auto; }
  /* only while it floats and moves is it one piece that turns: a transform makes it a
     stacking context of its own, which anywhere else would cost the layers nothing but
     here the seat card could no longer rise over the round's panel */
  #afloat.moving { transform-origin: var(--cx, 50%) var(--cy, 50%);
                   transform: translate(var(--bx, 0px), var(--by, 0px)) rotate(var(--br, 0deg)) scale(var(--bs, 1)); }
  #glitter { position: absolute; inset: 0; pointer-events: none; z-index: var(--z-background, 0); }
  #snow { position: absolute; inset: 0; pointer-events: none; z-index: var(--z-weather, 5040); }
  .table > canvas.fireworks { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: var(--z-fireworks, 10000); }
  /* The table is still: the person asked for it, or the system did (Theme.js tableMoves). */
  body.still .table > .shimmer, body.still .table > .breeze { animation: none; }
  body.still .table > .breeze { display: none; }
  /* A big moment's flourish over the cloth (TableView.js flourish, table.json motion.flourish). */
  .flourish { position: absolute; width: 0; height: 0; pointer-events: none; z-index: var(--z-flourish, 10020); }
  .flourish > .ring { position: absolute; border-radius: 50%; border-style: solid; transform: translate(-50%, -50%); }
  .flourish > .pill { position: absolute; transform: translate(-50%, -50%); white-space: nowrap; border-radius: 999px;
                      font-weight: 900; font-family: ui-rounded, system-ui, sans-serif; }
  /* a square of a disc's width is a quarter larger in area, and looked it (table.json squareOfRound) */
  #tabletop:not(.round) { transform: translate(-50%, -50%) scale(var(--squareofround, .8)); }
  /* THE JASSTEPPICH - a SQUARE cloth in the middle, big enough for the
     Stiche and nothing more; the hands lie on the floor around it. It
     is NOT what the felt is laid on (the felt is the whole table), so
     it is an element behind the cards rather than a margin round them
     - measure() gives it its place and its side. */
  #cloth { position: absolute; transform: translate(-50%, -50%); z-index: var(--z-felt, 2000);
           background: var(--clothfibres, none) center / 100% 100% no-repeat,   /* the felt's fibres, one piece (cloth.texture) */
                       linear-gradient(var(--felt-top), var(--felt-bottom));   /* over T.feltTop -> T.feltBottom */
           border-radius: var(--clothcorner, 18px);
           border: var(--clothedge, 1.5px) solid var(--clothedgeink, rgba(255,255,255,.2));
           box-shadow: var(--clothshadow); }
  #cloth .cord { position: absolute; left: calc(-1 * var(--clothedge, 1.5px)); top: calc(-1 * var(--clothedge, 1.5px)); pointer-events: none; }   /* the Kordel round its edge (cordSVG) */
  /* the plate as every screen draws it (table.json plate, the Mac's PlateDisc) */
  .plate { position: absolute; width: var(--platepx, 56px); height: var(--platepx, 56px); border-radius: 50%; background: var(--platefill, rgba(0,0,0,.4));
           border: var(--plateringpx, 1.6px) solid var(--platering, rgba(255,255,255,.25)); display: flex; align-items: center; justify-content: center;
           font-size: var(--plateface, 30px); transform: translate(-50%, -50%); z-index: var(--z-plate, 2040); transition: box-shadow .2s;
           box-shadow: var(--plateshadow); }
  /* AN OPEN PLATE RISES, and it has to be the PLATE that rises, not
     its tip. A positioned element with a z-index of its own opens a
     stacking context, so the tip's z-index only ever sorted it against
     its own plate: the pair stacked together at 3 and went under the
     ask panel at 6, whatever number the tip carried. The card is the
     one thing the person asked to see, so while it is open it goes
     over everything on the felt. */
  /* A computer player's card opens on a click on its "i" and closes on the
     next one, or on a click anywhere else (TableView.js plateTip) - never on
     hover, which a touch screen does not have. It opens BELOW the plate, in
     the hand's band: over your hand, under what is said (layers.playerCard). */
  .plate { cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .plate.open { z-index: var(--z-playerCard, 5010); } .plate.open .tip { display: block; }
  /* the "i": a small disc on the plate's lower edge, on the side toward the table */
  .plate .info { position: absolute; width: 36%; height: 36%; bottom: -12%; right: -14%; padding: 0; border-radius: 50%;
                 background: rgba(0,0,0,.62); border: 1.2px solid rgba(255,255,255,.5); color: #fff; cursor: pointer;
                 font: bold calc(var(--platepx, 56px) * .23) Georgia, 'Times New Roman', serif; line-height: 1;
                 display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 4px rgba(0,0,0,.4); }
  .plate.right .info { right: auto; left: -14%; }
  .plate .info::before { content: ''; position: absolute; inset: -8px; border-radius: 50%; }   /* a finger's reach, more than the disc */
  .plate.open .info { background: rgba(0,0,0,.85); border-color: rgba(255,255,255,.9); }
  /* your own line, being typed: a white bubble over your plate, its tail down */
  .spruch { position: absolute; z-index: var(--z-seatCard, 10050); transform: translate(-50%, -100%); display: flex; gap: 6px; align-items: center;
            padding: 6px 6px 6px 12px; background: #fff; border-radius: 14px; box-shadow: 0 4px 14px rgba(0,0,0,.35); }
  .spruch[hidden] { display: none; }
  .spruch::after { content: ''; position: absolute; top: 100%; left: 50%; margin-left: -8px; border: 8px solid transparent; border-top-color: #fff; }
  .spruch input { width: min(240px, 60vw); border: 0; outline: 0; font: inherit; font-size: 15px; color: #222; background: transparent; }
  .spruch button { width: 30px; height: 30px; border: 0; border-radius: 50%; background: var(--gold, #e8b923); color: #222; padding: 5px; cursor: pointer; }
  .spruch button svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
  /* its turn: the disc and the ring turn gold, as on the Mac - no halo besides */
  .plate.due { background: var(--platedue, rgba(255,204,0,.3)); border-color: var(--plateduering, rgba(255,204,0,.9)); }
  .plate .tip { position: absolute; display: none; z-index: var(--z-seatCard, 10050); background: rgba(0,0,0,.85); color: var(--felt-ink);
                border-radius: 12px; padding: 10px 14px; width: 230px; text-align: left; font-size: 13px;
                border: 1px solid rgba(255,255,255,.3); box-shadow: 0 4px 14px rgba(0,0,0,.4); }
  .plate .tip b { display: block; font-size: 15px; }
  .plate .tip .kind { color: var(--gold); font-size: 12px; font-weight: 600; margin: 2px 0 4px; }
  .plate .tip .blurb { opacity: .8; font-size: 12px; }
  .plate .tip .player { color: var(--gold); font-size: 12px; font-weight: 600; margin: 8px 0 3px; }
  .plate .tip .cheat { color: #ffad5c; font-size: 12px; font-weight: 600; margin-top: 4px; }
  /* below the plate (clear of its "i"); from a side seat it reaches toward the table */
  .plate .tip { top: calc(124% + 6px); left: 50%; transform: translateX(-50%); }
  .plate.left .tip { left: 0; transform: none; }
  .plate.right .tip { left: auto; right: 0; transform: none; }
  .bubble { position: absolute; z-index: var(--z-bubble, 5020); padding: 7px 12px; background: #fff; color: #222; border-radius: 14px;
            font-size: 14px; font-weight: 500; max-width: 240px; box-shadow: 0 2px 8px rgba(0,0,0,.35);
            animation: pop .25s ease-out; }
  .bubble::after { content: ''; position: absolute; width: 0; height: 0; border: 8px solid transparent; }
  .bubble.up::after   { bottom: 100%; left: 50%; margin-left: -8px; border-bottom-color: #fff; }
  .bubble.lft::after  { right: 100%; top: 50%; margin-top: -8px; border-right-color: #fff; }
  .bubble.rgt::after  { left: 100%; top: 50%; margin-top: -8px; border-left-color: #fff; }
  @keyframes pop { from { transform: scale(.6); opacity: 0 } to { opacity: 1 } }
  .bubble.still { animation: none; }            /* drawn again, not new (bubbleEl) */
  /* on a phone the side bubbles take at most a good third of the felt each: left and right never meet over the trick */
  body.narrow .bubble.lft, body.narrow .bubble.rgt { box-sizing: border-box; font-size: 13px; padding: 6px 10px; }   /* their width: up to the middle (TableView.js) */
  body.narrow .bubble.up { max-width: 70%; box-sizing: border-box; font-size: 13px; }

  /* the held bow: every card on one circle around a pivot below the hand */
  /* the hands: another seat's lies on the felt, your own in front of everything (TSTableView's zIndex 5) */
  .fan { position: absolute; width: 0; height: 0; z-index: var(--z-fan, 2030); }
  .fan.mine { z-index: var(--z-hand, 5010); }
  .fan .card { position: absolute; left: 0; top: 0; transition: transform .15s, opacity .15s; }
  /* A SHADOW ON ANYTHING THAT MOVES OR IS REBUILT IS A BOX SHADOW.
     filter: drop-shadow paints ink OUTSIDE the element's box, belonging
     to no box a compositor tracks; Safari leaves it behind when the
     thing it belonged to moves or goes away, which is what put strips
     on this felt. A box-shadow is painted with the box and goes where
     the box goes. A filter is still the right tool for something that
     sits still and is not a rectangle - see .felt-btn. */
  .card { width: var(--cw); height: var(--ch); user-select: none;
          border-radius: var(--radius); box-shadow: var(--cardshadow); }
  .card img { width: 100%; height: 100%; display: block; }
  /* a new card, mark or pile whose picture is not there yet: unseen, shadow and all, rather than a shadow without a card (keptEl) */
  .unready { visibility: hidden; }
  .card.small { width: calc(var(--cw) * var(--other)); height: calc(var(--ch) * var(--other)); }
  .card.legal { cursor: pointer; }
  .card.dim { opacity: .8; }
  /* the trump, on a dark disc at the exact centre of the table */
  .mark { position: absolute; width: var(--markpx); height: var(--markpx); border-radius: 50%; background: rgba(0,0,0,.32);
          border: 1px solid rgba(255,255,255,.18); transform: translate(-50%, -50%); display: flex;
          box-shadow: var(--markshadow);
          align-items: center; justify-content: center; z-index: var(--z-mark, 2010); }
  .mark img { width: calc(var(--markpx) * .56); height: calc(var(--markpx) * .56); }
  .mark span { font-size: calc(var(--markpx) * .5); font-weight: 700; color: rgba(255,255,255,.85); line-height: 1; }
  /* the tricks a seat has taken, face down in front of its hand */
  .pile { position: absolute; width: calc(var(--cw) * var(--mini)); height: calc(var(--ch) * var(--mini)); cursor: pointer;
          transform: translate(-50%, -50%); z-index: var(--z-pile, 2020);
          border-radius: calc(var(--radius) * var(--mini)); box-shadow: 0 1px 2px rgba(0,0,0,.4); /* lampShadow() sets the real one */ }
  .pile img { width: 100%; height: 100%; display: block; }
  .pile:hover { filter: drop-shadow(0 0 6px rgba(242,201,76,.9)); }
  .spread { position: absolute; z-index: var(--z-spread, 10020); background: rgba(0,0,0,.85); border-radius: 12px; padding: 8px 10px 10px;
            border: 1px solid rgba(255,255,255,.25); text-align: center; }
  .spread .title { font-size: 12px; font-weight: 600; margin-bottom: 6px; opacity: .9; white-space: nowrap; }
  .spread .cards { display: flex; gap: 5px; }
  .spread .played { display: flex; flex-direction: column; align-items: center; gap: 3px; }
  .spread .played .face { font-size: max(14px, calc(var(--cw) * .2)); line-height: 1; }
  .spread .card { width: calc(var(--cw) * .5); height: calc(var(--ch) * .5);
                  border-radius: calc(var(--radius) * .5); box-shadow: none; }
  .spread::after { content: ''; position: absolute; width: 0; height: 0; border: 8px solid transparent; }
  .spread.above { transform: translate(-50%, -100%); }
  .spread.above::after { top: 100%; left: 50%; margin-left: -8px; border-top-color: rgba(0,0,0,.85); }
  .spread.below { transform: translate(-50%, 0); }
  .spread.below::after { bottom: 100%; left: 50%; margin-left: -8px; border-bottom-color: rgba(0,0,0,.85); }
  .spread.rightof { transform: translate(0, -50%); }
  .spread.rightof::after { right: 100%; top: 50%; margin-top: -8px; border-right-color: rgba(0,0,0,.85); }
  .spread.leftof { transform: translate(-100%, -50%); }
  .spread.leftof::after { left: 100%; top: 50%; margin-top: -8px; border-left-color: rgba(0,0,0,.85); }

  /* the trick: a cross around the centre, each card lying towards its owner */
  .slot { position: absolute; width: 0; height: 0; z-index: var(--z-trick, 2050); }
  .slot .turn { position: absolute; left: 0; top: 0; transform: translate(-50%, -50%); }
  /* the beats: a slot moves (left/top), its wrapper scales, the card
     turns and flips - all transitions, the state machine sets targets */
  .slot.beat { transition: transform .45s ease-in-out; }
  .slot.beat .turn { transition: transform .45s ease-in-out; will-change: transform; }
  .slot.beat .card { transition: transform .45s ease-in-out; will-change: transform; }
  .slot.sweep { transition: transform .6s ease-in; }
  .slot.sweep .card { transition: transform .6s ease-in; will-change: transform; }
  .card .layer { position: absolute; inset: 0; transition: opacity .3s; }
  .card .layer img { width: 100%; height: 100%; display: block; }
  .card .layer.back { opacity: 0; }
  .card.flipped .layer.back { opacity: 1; }
  .card.flipped .layer.face { opacity: 0; }

  /* the question: a dark band across the middle, like the app's panels */
  .ask { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: var(--z-overlay, 10010);
         background: rgba(0,0,0,.72); padding: 16px 22px; border-radius: 16px; text-align: center;
         border: 1px solid rgba(255,255,255,.18); max-width: 92%; }
  .ask .weis { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; } .ask .weis .card { position: static; width: 44px; height: calc(44px * 1.45); }
  .ask .weis b { margin-left: 6px; }
  .ask.wide { max-width: 92%; padding: 24px; }   /* no width: an absolute box shrinks to its content everywhere (max-content does not, in Safari) */
  .ask h3 { margin: 0 0 12px; font-size: 22px; font-weight: 700; }
  /* the door's question: the whole window dimmed behind it (a click there is Weiterspielen), the
     panel just above the exit sign, right edges flush (askQuit places it) - the middle has questions enough */
  .quit-block { position: fixed; inset: 0; background: rgba(0,0,0,.4); z-index: calc(var(--z-seatCard, 10050) + 100); }
  .quit-block[hidden] { display: none; }
  .ask.quit-ask { left: auto; top: auto; transform: none; background: rgba(0,0,0,.9); max-width: min(340px, calc(100% - 16px)); box-sizing: border-box; }
  .ask.quit-ask p { margin: 0 0 14px; opacity: .8; } .ask.quit-ask button { margin: 0 4px; }
  /* an exercise (Drills.js): the question above the felt, the verdict in the ask's place */
  .drillBar { position: absolute; left: 12px; top: 12px; z-index: var(--z-overlay, 10010);
              background: rgba(0,0,0,.6); color: #fff; padding: 8px 14px; border-radius: 12px;
              border: 1px solid rgba(255,255,255,.18); max-width: min(260px, 40%); font-size: 15px; }
  .drillBar .dim { font-size: 12px; opacity: .7; margin-bottom: 2px; }
  .drillBar { display: flex; align-items: flex-start; gap: 6px; }
  .drillBar .drill-close { background: none; border: 0; color: #fff; opacity: .75; font-size: 16px; line-height: 1; padding: 2px 2px 2px 8px; cursor: pointer; min-width: 0; box-shadow: none; }
  .drillBar .drill-close:hover { opacity: 1; }
  /* a question over the hand: its bottom edge above the bow (TablePanels.js) */
  .ask.over-hand { transform: translate(-50%, -100%); }
  body.drill #feltLeft, body.drill #undo, body.drill #redo { display: none; }
  .ask.drill-verdict { width: min(460px, 92%); max-height: 86%; overflow: auto; text-align: left; box-sizing: border-box; background: rgba(12,16,20,.94); }
  .ask.drill-verdict h3.grade { text-align: center; } .ask.drill-verdict h3.right { color: #8fe08a; } .ask.drill-verdict h3.good { color: #e8d77a; } .ask.drill-verdict h3.wrong { color: #ff9a8a; }
  .ask.drill-verdict .why { font-size: 15px; line-height: 1.45; margin: 0 0 14px; }
  .ask.drill-verdict .ranking { font-size: 14px; margin-bottom: 14px; }
  .ask.drill-verdict .ranking > .dim { font-size: 11px; letter-spacing: .6px; text-transform: uppercase; opacity: .6; margin-bottom: 6px; }
  .ask.drill-verdict .ranking > .dimmer { font-size: 12px; opacity: .55; margin-top: 6px; }
  .ask.drill-verdict .drow { display: grid; grid-template-columns: 1fr auto 84px; gap: 10px; padding: 3px 6px; border-radius: 6px; }
  .ask.drill-verdict .drow.mine { background: rgba(255,255,255,.12); font-weight: 600; }
  .ask.drill-verdict .drow.right .cost { color: #8fe08a; } .ask.drill-verdict .drow.good .cost { color: #e8d77a; } .ask.drill-verdict .drow.wrong .cost { color: #ff9a8a; }
  .ask.drill-verdict .drow .yours { font-size: 12px; opacity: .7; text-align: right; }
  .ask.drill-verdict .mini { display: inline-block; width: 16px; height: 16px; vertical-align: -3px; margin-right: 4px; padding: 1px;
                             background: #f4f1e8; border-radius: 3px;      /* a black sign on the dark panel */
                             color: #1d1d1b; font-size: 13px; font-weight: 700; line-height: 1;
                             display: inline-flex; align-items: center; justify-content: center; }   /* Obenabe's and Undenufe's arrows are text */
  .ask.drill-verdict .which { text-align: center; font-size: 12px; opacity: .65; margin-bottom: 4px; }
  body.narrow .ask.drill-verdict { top: 8px; transform: translateX(-50%); max-height: calc(100% - 16px); padding: 16px; }
  .ask.drill-verdict .mini img, .ask.drill-verdict .mini svg { width: 16px; height: 16px; }
  .ask.drill-verdict .buttons { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
  .ask .signs { display: flex; justify-content: center; gap: 14px; flex-wrap: nowrap; margin-bottom: 14px; }   /* one row: the box shrinks to it (a box at left 50% is offered only half the felt) */
  body.narrow .ask .signs { flex-wrap: wrap; }
  .ask .sign { cursor: pointer; text-align: center; font-size: 12px; opacity: .95; width: 68px; }
  .ask .sign .disc { width: var(--pickpx); height: var(--pickpx); border-radius: 50%; background: #fff; margin: 0 auto 6px;
                     display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 6px rgba(0,0,0,.4);
                     transition: transform .12s; }
  .ask .sign:hover .disc { transform: translateY(-4px); }
  .ask .sign .disc img { width: calc(var(--pickpx) * .56); height: calc(var(--pickpx) * .56); }
  .ask .sign .disc span { font-size: calc(var(--pickpx) * .52); color: #222; line-height: 1; }
  .ask button { background: var(--gold); color: #222; border: 0; padding: 10px 28px; border-radius: 999px;
                font-weight: 700; cursor: pointer; margin: 3px; font-size: var(--button-text); }
  .ask button.quiet { background: rgba(255,255,255,.22); color: var(--felt-ink); }
  .ask input { width: 80px; font-size: 18px; padding: 6px; border-radius: 8px; border: 0; text-align: center; }
  /* the Differenzler's call: −5 −1 [the figure] +1 +5, the slider under it (the Mac's PredictionPanel) */
  .ask .call { display: flex; align-items: center; justify-content: center; gap: 6px; }
  .ask .call input { width: 96px; font-size: 34px; font-weight: 900; padding: 4px 6px; background: rgba(255,255,255,.12); color: var(--felt-ink);
                     -moz-appearance: textfield; }
  .ask .call input::-webkit-outer-spin-button, .ask .call input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  .ask button.step { background: rgba(255,255,255,.16); color: var(--felt-ink); padding: 6px 0; min-width: 40px; margin: 0;
                     font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; }
  .ask .trump { display: flex; align-items: center; justify-content: center; gap: 12px; width: max-content; max-width: 100%; margin: 0 auto 10px;
                padding: 6px 22px; border-radius: 999px; background: rgba(255,255,255,.12); font-size: 27px; font-weight: 700; white-space: nowrap; }
  .ask .trump .disc { width: 54px; height: 54px; border-radius: 50%; background: #fff; display: flex; align-items: center; justify-content: center; flex: none; }
  .ask .trump .disc img { width: 34px; height: 34px; }
  .ask .trump .disc span { font-size: 31px; color: #222; line-height: 1; }
  body.narrow .ask .trump { font-size: 22px; gap: 10px; padding: 5px 16px; }
  body.narrow .ask .trump .disc { width: 44px; height: 44px; } body.narrow .ask .trump .disc img { width: 28px; height: 28px; }
  /* the call's slider stands in a pill with a border - a dark track on the dark panel vanished */
  .ask input[type=range] { display: block; width: 300px; max-width: 100%; margin: 10px auto 4px; padding: 6px 12px; box-sizing: border-box;
                           background: rgba(255,255,255,.06); border: 1.5px solid rgba(255,255,255,.55); border-radius: 999px;
                           -webkit-appearance: none; appearance: none; height: 32px; cursor: pointer; }
  .ask input[type=range]::-webkit-slider-runnable-track { height: 6px; border-radius: 3px; background: rgba(255,255,255,.35); }
  .ask input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 20px; height: 20px; margin-top: -7px; border-radius: 50%;
                                                 background: rgb(245, 200, 40); box-shadow: 0 1px 3px rgba(0,0,0,.5); }
  .ask input[type=range]::-moz-range-track { height: 6px; border-radius: 3px; background: rgba(255,255,255,.35); }
  .ask input[type=range]::-moz-range-progress { height: 6px; border-radius: 3px; background: rgb(245, 200, 40); }
  .ask input[type=range]::-moz-range-thumb { width: 20px; height: 20px; border: 0; border-radius: 50%; background: rgb(245, 200, 40); box-shadow: 0 1px 3px rgba(0,0,0,.5); }
  .ask p.question { margin: 0 0 10px; font-size: 15px; opacity: .7; }
  /* "Deine Ansage" explains itself on a click: the question in a quiet box under it */
  .ask h3.explains { cursor: pointer; text-align: center; font-size: 15px; font-weight: 600; opacity: .7; margin-bottom: 10px; }   /* a newcomer's word */
  .ask h3.explains .i { margin-left: 5px; font-size: 12px; vertical-align: 1px; }
  .ask h3.explains + p.question { width: max-content; max-width: 100%; margin: -4px auto 12px; padding: 6px 12px; border-radius: 10px;
                                  background: rgba(255,255,255,.12); opacity: 1; color: rgba(255,255,255,.85); }
  .ask input[type=range] { display: block; width: min(320px, 70vw); margin: 14px auto 12px; padding: 0; accent-color: var(--gold); background: none; }
  .ask .sheet { text-align: left; min-width: 260px; margin-bottom: 10px; font-size: 15px; }
  .ask .sheet .big { font-size: 20px; font-weight: 800; margin-bottom: 6px; }
  /* the round's points as the chalkboard splits them: cards, what came on top, the total (TablePanels.js roundTable) */
  .ask .sheet table.round { border-collapse: collapse; margin: 2px 0 6px; font-size: 17px; font-variant-numeric: tabular-nums; }
  .ask .sheet table.round th { text-align: left; font-weight: 400; opacity: .7; padding: 2px 14px 2px 0; }
  .ask .sheet table.round td { text-align: right; padding: 2px 0 2px 18px; white-space: nowrap; }
  body.narrow .ask .sheet table.round { font-size: 15px; }
  body.narrow .ask .sheet table.round td { padding-left: 12px; }
  body.narrow .ask .sheet table.round th { padding-right: 6px; }
  .ask .sheet table.round tr.total th, .ask .sheet table.round tr.total td { opacity: 1; font-weight: 800; border-top: 1px solid rgba(255,255,255,.25); padding-top: 4px; }
  /* a Matsch, the Bergpreis, the game's end: what the round made of the game, after it */
  .ask .sheet .matsch { margin-top: 8px; text-align: center; font: 900 34px/1.1 Georgia, 'Times New Roman', serif; color: rgb(245, 200, 40); }
  body.narrow .ask .sheet .matsch { font-size: 27px; }       /* KONTERMATSCH! across a phone's panel */
  /* MATSCH! and BERGPREIS! pulse with the fireworks - while the table moves (body.still) */
  .ask .sheet .matsch { animation: cheer .55s ease-in-out infinite alternate; }
  body.still .ask .sheet .matsch { animation: none; }
  @keyframes cheer { from { transform: scale(.94); } to { transform: scale(1.1); } }
  .ask .sheet .matsch + b { display: block; text-align: center; }
  .ask .sheet .matsch.berg { font-size: 27px; }
  body.narrow .ask .sheet .matsch.berg { font-size: 22px; }
  .ask .sheet .won { margin-top: 8px; font-size: 20px; font-weight: 800; color: rgb(245, 200, 40); }
  .ask .own-news { margin-top: 8px; font-size: 14px; color: #8fe08a; }   /* Deine Übungen: what the game left (OwnDrillsNews) */
  /* the table's controls: quiet icons in the bottom corners, white at
     .75, a disabled one at .35 - TableView.swift's overlays */
  .felt-btns { position: absolute; bottom: 12px; display: flex; gap: 14px; z-index: var(--z-controls, 5030); }
  .tvBar { position: absolute; transform: translate(-50%, -50%); z-index: var(--z-bar, 10030); display: flex; align-items: center; gap: 14px; padding: 6px 18px;
           background: rgba(0,0,0,.62); border: 1px solid rgba(255,255,255,.2); border-radius: 999px; color: rgba(255,255,255,.75); font-size: 14px; white-space: nowrap; }
  .tvBar .felt-btn { width: 28px; height: 28px; } .tvBar .felt-btn[disabled] { opacity: .4; }
  .felt-btns.left { left: 16px; } .felt-btns.right { right: 16px; }
  .felt-btn { width: 34px; height: 34px; padding: 4px; border: 0; background: none; color: rgba(255,255,255,.75);
              cursor: pointer; filter: drop-shadow(0 1px 2px rgba(0,0,0,.5)); }
  .felt-btn svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .felt-btn:hover { color: #fff; }
  .felt-btn[disabled] { opacity: .35; cursor: default; }
  .felt-btn.on { color: var(--gold); }
  /* the appearance at the table (LookCard.swift): the settings' own box on the "i" card's chrome, hung off the palette */
  .lookCard { color: #fff; --ink: #fff; --ink-rgb: 255, 255, 255; }
  .icon-btn.look svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
  .icon-btn.look.on { color: var(--gold); }
  .lookCard .head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10px; font-weight: 600; }
  .lookCard .head button { background: none; border: 0; color: rgba(255,255,255,.75); font: inherit; font-size: var(--button-text); cursor: pointer; }
  .lookCard .head button:hover { color: #fff; }
  /* the advisers' bubble: white, above the ? button, its tail pointing down at it (HintBubble) */
  .hintBubble { position: absolute; left: 60px; bottom: 58px; z-index: var(--z-hint, 10020); background: rgba(255,255,255,.95); color: #000;
                border-radius: 12px; padding: 8px 14px; font-size: 15px; font-weight: 500; line-height: 1.5;
                box-shadow: 0 4px 14px rgba(0,0,0,.35); }
  .hintBubble::after { content: ''; position: absolute; left: 14px; bottom: -8px; border: 8px solid transparent;
                       border-top-color: rgba(255,255,255,.95); border-bottom: 0; }
  /* the glow, and the card's own shadow under it - a box-shadow
     replaces rather than adds, so the base has to be named again */
  .card.hinted { box-shadow: 0 0 0 3px var(--gold), 0 0 14px rgba(242,201,76,.8), var(--cardshadow); }
  .join .title { font-size: 18px; font-weight: 600; margin-bottom: 6px; }

  /* the side: the app's panel */
  /* the Sideboard (Sideboard.swift): the Chalkboard and the Tischbuech, two slates in a column */
  .sideboard { background: var(--panel); padding: 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }
  .sideboard > section { background: #3a3a3e; border-radius: 12px; padding: 14px 16px; }
  /* THE CHALKBOARD is slate in a light wooden frame, the way a
     Jasstafel hangs in a Stube (TableChrome.swift: inWoodenFrame,
     ChalkboardView.kt). The wood and the slate are design/theme.json's
     and the widths design/table.json's; a frame is thicker than its
     rebate is round, so the slate's corner is the smaller of the two. */
  .sideboard > section.chalkboard { background: var(--board-frame); padding: var(--boardframe, 12px);
                                    border-radius: var(--boardcorner, 16px);
                                    border: 1px solid var(--board-frame-edge); }
  .chalkboard .slate { background: var(--board-slate); border-radius: var(--boardslatecorner, 6px);
                       padding: 14px 16px; border: 1px solid var(--board-frame-edge); }
  /* THE TISCHBUECH beside it, in the same frame of charcoal (TableChrome.swift:
     inCharcoalFrame, ChalkboardView.kt): the frame's width and corners are the
     board's, the charcoal and the page design/theme.json's (bookFrame*, bookPage). */
  .sideboard > section.tischbuech,
  .sideboard > section.coaches { background: var(--book-page); border: var(--boardframe, 12px) solid var(--book-frame);
                                 border-radius: var(--boardcorner, 16px);
                                 box-shadow: 0 0 0 1px var(--book-frame-edge), inset 0 0 0 1px var(--book-frame-edge); }
  /* THE COACHES card (CoachesCard): folding by its title, their picks and verdicts */
  .sideboard > section.coaches { padding: 10px 14px; font-size: 14px; }
  .coaches .fold { display: flex; align-items: baseline; gap: 6px; width: 100%; background: none; border: 0; color: inherit;
                   font: inherit; padding: 0; cursor: pointer; text-align: left; }
  .coaches .fold b { font-size: 12px; letter-spacing: .12em; }
  .coaches .fold .chev { display: inline-block; transition: transform .15s; font-weight: 700; }
  .coaches .fold[aria-expanded="true"] .chev { transform: rotate(90deg); }
  .coaches .fold .figure { margin-left: auto; font-size: 12px; opacity: .6; }
  .coaches .crow { display: flex; justify-content: space-between; gap: 8px; margin-top: 8px; }
  .coaches .ctip .crow { margin-top: 10px; }
  .coaches .said { padding-bottom: 8px; border-bottom: 1px solid rgba(255,255,255,.15); }
  .coaches .said + .ctip, .coaches .said + p { margin-top: 4px; }
  .coaches .said:last-child { border-bottom: 0; padding-bottom: 0; }
  .coaches .who { opacity: .6; }
  .coaches .pick { color: var(--gold); font-weight: 600; }
  .coaches .same { color: #7fd38a; }
  .coaches .dim, .coaches .dimmer { opacity: .55; font-size: 12px; }
  .coaches p.dim { margin: 8px 0 0; }
  .coaches .agree { margin: 10px 0 0; font-size: 12px; color: var(--gold); opacity: .85; }
  /* a replay: the move the record played, and their word on it, under their picks */
  .coaches .played { margin: 12px 0 0; padding-top: 8px; border-top: 1px solid rgba(255,255,255,.15); font-weight: 600; }
  .coaches .played + .said { border-bottom: 0; padding-bottom: 0; }
  /* the round's look back: the dearest decisions */
  .coaches .look { margin-top: 10px; }
  .coaches .look .ltitle { display: block; font-size: 12px; letter-spacing: .08em; opacity: .75; }
  .coaches .look p.dim { margin: 10px 0 0; }
  .coaches .look .lrow { margin-top: 10px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,.15); }
  .coaches .look .lrow .crow { margin-top: 0; }
  .coaches .look .lrow b { font-weight: 600; }
  .coaches .look .lrow .says { margin-top: 3px; font-size: 13px; }
  .coaches .look .lplay { background: none; border: 0; padding: 0 0 0 4px; margin: 0; color: rgba(255,214,90,.85); cursor: pointer; vertical-align: -3px; line-height: 0; }
  .coaches .look .lplay svg { width: 17px; height: 17px; }
  .coaches .look .lplay svg circle { fill: none; stroke: currentColor; stroke-width: 1.6; }
  .coaches .look .lplay svg path { fill: currentColor; }
  .coaches .look .lplay:hover { color: #ffd65a; }
  .coaches .mini, .hintBubble .mini, .log .mini { display: inline-block; width: 14px; height: 14px; vertical-align: -2px; font-weight: 700; }
  .coaches .mini img, .coaches .mini svg, .log .mini img, .log .mini svg { width: 14px; height: 14px; }
  .hintBubble .title { font-size: 13px; font-weight: 700; }
  .hintBubble .small { font-size: 12px; }
  .hintBubble .faint { opacity: .55; }
  .head h1 { margin: 0 0 4px; font-size: 24px; font-weight: 700; }
  .head h1 svg { width: 22px; height: 22px; vertical-align: -3px; margin-right: 8px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
  .hintBubble .mini { display: inline-block; width: 14px; height: 14px; vertical-align: -2px; font-weight: 700; } .hintBubble .mini img, .hintBubble .mini svg { width: 14px; height: 14px; }
  .head .sub { opacity: .75; font-size: 14px; padding-bottom: 8px; border-bottom: 1px solid rgba(255,255,255,.2); margin-bottom: 10px; }
  .team { margin: 10px 0 18px; }
  .team .who { font-size: 20px; letter-spacing: 2px; display: flex; align-items: center; gap: 10px; }
  .team .who .owed { margin-left: auto; display: flex; align-items: center; gap: 10px; letter-spacing: 0; }   /* right-aligned on the pairing's line */
  .team .pts { font-size: 40px; font-weight: 800; line-height: 1; }
  .team .pts small { font-size: 16px; font-weight: 500; opacity: .7; margin-left: 8px; }
  .team .contract { font-size: 13px; opacity: .8; }
  #teams .diff-row { margin: 4px 0 10px; }
  #teams .diff-row .who { font-size: 15px; font-weight: 600; }
  #teams .diff-row .nums { display: flex; justify-content: space-between; gap: 10px; font-size: 13px; opacity: .75; font-variant-numeric: tabular-nums; }
  #teams .diff-row .nums b { opacity: 1; }
  #teams > .contract { font-size: 13px; opacity: .8; margin-top: 4px; }
  .head .row { display: flex; align-items: center; gap: 10px; }
  .head .row h1 { margin-right: auto; }        /* the title takes the room; the palette and the "i" sit together at the right */
  .head .icon-btn { color: rgba(255,255,255,.7); }   /* white on the board, whatever the room's light - the Mac's .white.opacity */
  .head .icon-btn:hover { color: #fff; }
  .head .icon-btn.info { width: 22px; height: 22px; color: rgba(255,255,255,.55); } .head .icon-btn.info svg { fill: none; stroke: currentColor; stroke-width: 1.6; }
  /* the round line is the fold: the account, round by round, under it (SideboardHead) */
  .head summary { cursor: pointer; list-style: none; padding-bottom: 0; border-bottom: 0; margin-bottom: 0; } .head summary::-webkit-details-marker { display: none; }
  .head summary::after { content: ' ▸'; opacity: .6; } .head details[open] summary::after { content: ' ▾'; }
  .head details.closed summary { pointer-events: none; } .head details.closed summary::after { content: ''; }
  .head #tafel { margin-top: 8px; }
  .chalkboard .status { font-size: 12px; opacity: .5; margin-top: 10px; }
  .chalkboard .status:empty { display: none; }   /* news only: connecting, a lost line, watching - never just a name */
  .team .who .cost { font-size: 12px; opacity: .6; font-variant-numeric: tabular-nums; display: flex; align-items: center; gap: 3px; }
  .team .who .cost svg { width: 11px; height: 11px; fill: currentColor; }
  /* the info card: this table's rules, then the game's page from the
     help book (TableInfoCard). ANCHORED AT THE "i": the head is the
     positioned ancestor, so the card's right edge lands under the
     button and the 380 it wants go left, over the felt. Without
     that `position: relative` nothing between here and <body> was
     positioned, so `top: 60px` meant 60px down the WINDOW - which
     is how the card came to sit in the far corner from the button
     that opens it. */
  /* THE "i" CARD, hung on the "i" itself. It opens into the room there
     is - down from a button at the top of a tall board, up from one
     under the felt on a narrow window - and its spike points back at
     the button either way, as the table's own bubbles do.
     placeInfoCard() measures; this only dresses. */
  .info-card { position: absolute; z-index: var(--z-card, 10040); padding: 18px;
               width: min(var(--sideboardw, 340px), calc(100vw - 32px));
               background: #212628; border: 1px solid rgba(255,255,255,.15); border-radius: 12px;
               box-shadow: 0 8px 24px rgba(0,0,0,.5); font-size: 14px; line-height: 1.4; }
  /* the scroller is INSIDE, so the spike can stick out of the card */
  .info-card .scroll { max-height: min(70vh, var(--infomax, 70vh)); overflow: auto; }
  /* THE SPIKE IS THE CARD'S EDGE CARRIED ON: the same face and the same
     hairline round it. A CSS triangle has no border of its own, so it is
     two of them - the ::before one pixel bigger in the edge's colour,
     the ::after on top in the face's, sitting a pixel INSIDE the card so
     its own border does not cut across the join. */
  .info-card::before, .info-card::after {
      content: ''; position: absolute; width: 0; height: 0; border: solid transparent; }
  .info-card::before { right: calc(var(--spike, 14px) - 1px); bottom: 100%;
                       border-width: 10px; border-bottom-color: rgba(255,255,255,.15); }
  .info-card::after  { right: var(--spike, 14px); bottom: calc(100% - 1px);
                       border-width: 9px; border-bottom-color: #212628; }
  .info-card.up::before { bottom: auto; top: 100%;
                          border-bottom-color: transparent; border-top-color: rgba(255,255,255,.15); }
  .info-card.up::after  { bottom: auto; top: calc(100% - 1px);
                          border-bottom-color: transparent; border-top-color: #212628; }
  .info-card .cap { font-size: 11px; font-weight: 600; letter-spacing: .5px; color: rgba(255,255,255,.55); margin: 10px 0 4px; } .info-card .cap:first-child { margin-top: 0; }
  .info-card .game { font-size: 19px; font-weight: 700; margin-bottom: 6px; } .info-card .game svg { width: 20px; height: 20px; vertical-align: -3px; margin-right: 8px; fill: none; stroke: currentColor; stroke-width: 1.8; }
  .info-card table { border-collapse: collapse; } .info-card td { padding: 2px 14px 2px 0; vertical-align: top; } .info-card td:first-child { color: rgba(255,255,255,.6); }
  .info-card .devs { color: rgba(255,220,0,.85); font-size: 12px; margin-top: 6px; } .info-card hr { border: 0; border-top: 1px solid rgba(255,255,255,.25); margin: 12px 0; }
  .info-card p { margin: 0 0 10px; color: rgba(255,255,255,.72); }
  /* the chalk: 20er strokes in five-bar gates, and the Strichli tally (Chalkboard.swift) */
  .chalk { display: block; margin-top: 6px; } .chalk path { fill: none; stroke: rgba(255,255,255,.9); stroke-width: 2.2; stroke-linecap: round; }
  .tally { vertical-align: middle; } .tally path { fill: none; stroke: rgba(255,255,255,.85); stroke-width: 2; stroke-linecap: round; }
  table.tafel { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 6px; }
  table.tafel td, table.tafel th { padding: 2px 4px; text-align: right; }
  table.tafel th { opacity: .6; font-weight: 500; }
  table.tafel td:first-child, table.tafel th:first-child { text-align: left; opacity: .7; }
  table.tafel tr.sum td { border-top: 1px solid rgba(255,255,255,.3); font-weight: 700; }
  .strichli { font-family: ui-monospace, Menlo, monospace; letter-spacing: 1px; }
  /* the Coiffeur ladder on the chalkboard (Chalkboard.swift: coiffeurGrid) */
  .rungs { display: grid; grid-template-columns: 1fr auto auto; gap: 5px 12px; align-items: center; font-size: 12px;
           margin-top: 10px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,.3); }
  .rungs .head { font-size: 13px; font-weight: 600; opacity: .6; }
  .rungs .head.who { opacity: 1; font-weight: 400; text-align: right; }
  .rungs .what { display: flex; align-items: center; gap: 6px; white-space: nowrap; opacity: .85; }
  .rungs .what b { width: 22px; text-align: right; opacity: .65; font-variant-numeric: tabular-nums; }
  .rungs .what .disc { width: 14px; height: 14px; min-width: 14px; font-size: 11px; background: none; box-shadow: none; }
  .rungs .what .disc img, .rungs .what .disc svg { width: 14px; height: 14px; }
  .rungs .n { text-align: right; min-width: 40px; font-weight: 600; font-variant-numeric: tabular-nums; }
  .rungs .open { text-align: right; min-width: 40px; opacity: .25; }
  .tischbuech h2 { margin: 0 0 8px; font-size: 15px; font-weight: 600; opacity: .85; }
  .tischbuech.folded h2 { margin-bottom: 0; }
  /* folding by its title, as the COACHES card; open, about fourteen lines, the newest in view */
  .tischbuech .fold { display: flex; align-items: baseline; gap: 6px; width: 100%; background: none; border: 0; color: inherit;
                      font: inherit; padding: 0; cursor: pointer; text-align: left; }
  .tischbuech .fold .chev { display: inline-block; transition: transform .15s; font-weight: 700; }
  .tischbuech .fold[aria-expanded="true"] .chev { transform: rotate(90deg); }
  .tischbuech .log { max-height: 300px; overflow-y: auto; }
  .log { font-size: 14px; }
  .log .row { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
  .log .row .av { width: 22px; text-align: center; flex: none; }
  .log .talkb { background: #fff; color: #222; border-radius: 14px; padding: 4px 10px; font-size: 13px; }
  .log .fact { opacity: .8; font-size: 13px; }
  .log .rmark { color: var(--gold); font-weight: 600; margin-top: 8px; font-size: 13px; }

  /* sitting down */
  .join { position: absolute; inset: 0; z-index: var(--z-bar, 10030); display: flex; align-items: center; justify-content: center;
          background: rgba(0,0,0,.45); }
  .join form { background: #10321f; padding: 22px 26px; border-radius: 14px; text-align: center; }
  .join input { font-size: 16px; padding: 7px 10px; border-radius: 8px; border: 0; width: 220px; }
  .join p { margin: 6px 0 12px; opacity: .8; font-size: 13px; }
  .join button { background: var(--gold); color: #222; border: 0; padding: 7px 14px; border-radius: 999px;
                 font-weight: 600; cursor: pointer; margin: 3px; font-size: var(--button-text); }
  .join button.quiet { background: rgba(255,255,255,.2); color: var(--felt-ink); }
  [hidden] { display: none !important; }

  /* a phone: the felt on top, the panel below. `compact` is set by
     App.js below the design's layout.compactBelowPx - the width the
     Mac app stacks its table by too (TableView.swift). No breakpoint
     of the page's own. */
  body.compact main { grid-template-columns: 1fr; height: auto; }
  body.compact .table { height: calc(var(--feltshare, .78) * 100vh); min-height: var(--feltmin, 520px); }
  body.compact .plate { width: 40px; height: 40px; font-size: 22px; }
  /* a phone (narrower than the app's own floor): the menus have to give
     way, because nothing here may push the page wider than the glass -
     one overflowing row takes the title and the tiles with it. */
  /* the help "i" and the gear keep the top-right corner; on a phone the
     title would run underneath them, so it starts below instead. */
  body.narrow .menu { padding: 62px 12px 28px; gap: 20px; }
  body.narrow .corner { top: 16px; right: 16px; }
  body.narrow .menu .title { font-size: 30px; }
  /* five games will not sit in one row on a phone: let them wrap rather
     than stretch the page. The names stay whole (no ellipsis on a game). */
  body.narrow .seg { flex-wrap: wrap; justify-content: center; }
  body.narrow .seg button { flex: 0 1 auto; padding: 6px 10px; }
  /* A phone has room for one thing at a time: while the table is asking
     (trump, a Weis, a Schub) the chatter steps aside rather than being
     read through the overlay. The app's window is never this narrow -
     when Android comes, it will want this rule too. */
  body.narrow .table:has(#ask:not([hidden])) .bubble { display: none; }
  body.narrow .ask h3 { font-size: 17px; }
  body.narrow .ask.wide { width: 94%; padding: 12px; }
  body.compact .side { padding: 10px; }

/* A list's own buttons - add an entrant, take one away - change the LIST,
   not a setting: geier's quiet circled signs, no box, so they never read
   as one more parameter beside the pickers (ScreenView.js 'add', 'minus'). */
.list-minus, .list-add { background: none; border: none; box-shadow: none; padding: 4px 2px; min-width: 0; width: auto;
                         cursor: pointer; color: rgba(var(--ink-rgb), .6); font: inherit; display: inline-flex; align-items: center; gap: 6px; }
.list-add { font-size: var(--button-text); justify-self: start; align-self: flex-start; margin-top: 4px; }
.list-minus:hover, .list-add:hover { color: rgba(var(--ink-rgb), .9); }
.list-minus:disabled, .list-add:disabled { opacity: .35; cursor: default; }
.list-minus .circle, .list-add .circle { display: inline-flex; align-items: center; justify-content: center; width: 17px; height: 17px;
                                         border: 1.5px solid currentColor; border-radius: 50%; font-size: 13px; line-height: 1; }
/* on a phone a row that can be taken away stays one line: its pickers,
   then the sign at the end - the number above them */
body.narrow .seat-row:has(> .list-minus) { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
body.narrow .seat-row:has(> .list-minus) > :first-child { flex: 1 0 100%; }
body.narrow .seat-row:has(> .list-minus) > select { flex: 1 1 0; min-width: 0; }

/* the ⓘ beside a player, and its popover (ScreenView.js infoButton) - a quiet
   circled i like geier's, no box: it tells, it does not set anything */
.info-button { background: none; border: 1.5px solid rgba(var(--ink-rgb), .4); color: rgba(var(--ink-rgb), .6);
               border-radius: 50%; width: 18px; height: 18px; min-width: 0; padding: 0; box-shadow: none; flex: none;
               font: italic 600 11px/15px Georgia, 'Times New Roman', serif; text-align: center; cursor: pointer; }
.info-button:hover, .info-button[aria-expanded="true"] { color: rgba(var(--ink-rgb), .95); border-color: rgba(var(--ink-rgb), .8); }
.popover-layer { position: fixed; z-index: var(--z-seatCard, 10050); }   /* the top band's top: over every menu */
.popover { width: min(340px, calc(100vw - 32px)); box-sizing: border-box; padding: 16px; border-radius: 14px;
           background: linear-gradient(rgba(var(--ink-rgb), .07), rgba(var(--ink-rgb), .07)), var(--backdrop-top);
           color: var(--ink); border: 1px solid rgba(var(--ink-rgb), .2); box-shadow: 0 8px 24px rgba(0,0,0,.35);
           display: flex; flex-direction: column; gap: 8px; text-align: left; font-size: 14px; line-height: 1.4; }
.popover .form-row { grid-template-columns: 96px 1fr; gap: 8px; font-size: 13px; }
.popover .form-label { opacity: .6; }
.popover .head { display: flex; align-items: baseline; gap: 8px; }   /* the name and its build */
.popover .note { font-size: 13px; opacity: .75; margin-top: 4px; }
.popover .prose p, .popover .prose ul { font-size: 13px; color: inherit; opacity: .85; margin-bottom: 10px; }   /* the Jass basics (learning.json) */
.popover .prose > :last-child { margin-bottom: 0; }
table.standings td.info { padding: 0 2px; width: 22px; }
/* the face piece (ScreenView.js faceButton): your portrait on a round button,
   and in the ⓘ's panel every face, four to a row, the chosen one ringed in gold */
.face-button { width: 44px; height: 44px; min-width: 0; padding: 3px; border-radius: 50%; flex: none; cursor: pointer;
               background: rgba(var(--ink-rgb), .08); border: 1px solid rgba(var(--ink-rgb), .25); box-shadow: none;
               display: inline-flex; align-items: center; justify-content: center; line-height: 1; }
.face-button:hover, .face-button[aria-expanded="true"] { border-color: rgba(var(--ink-rgb), .6); }
.face-button > .character-art, .face-pick > .character-art { width: 100%; height: 100%; }
.face-button > .character-art img, .face-pick > .character-art img { width: 100%; height: 100%; object-fit: contain; }
:has(> .face-button) { align-items: center; }
:has(> .face-button) > input.name { flex: 1 1 auto; min-width: 0; max-width: 360px; }   /* the name beside the face takes the rest of the line */
.popover.face-panel { width: auto; }
.face-panel .face-title { font-weight: 600; font-size: 15px; }
.face-grid { display: grid; grid-template-columns: repeat(4, 60px); gap: 8px; }
.face-pick { width: 60px; height: 60px; min-width: 0; padding: 5px; border-radius: 50%; cursor: pointer; box-shadow: none;
               background: none; border: 2.5px solid transparent; line-height: 1; }
.face-pick:hover { background: rgba(var(--ink-rgb), .08); }
.face-pick.on { background: rgba(242, 201, 76, .22); border-color: var(--gold); }
   /* the runner's i, beside the name */

/* the first seconds (index.html, IntroView.js, body.loading): the name
   and the busy sign are in the HTML itself, so the first paint shows them
   before a script has run; then the doors come, dimmed, not dashed (dashed
   is "leads nowhere yet"), and the sign turns on in the middle of the
   screen while the engine and the rest come in: a card over the dimmed
   doors, plain enough to be read at a glance (a third smaller since
   2026-10-16, Christoph), so that nobody takes the wait for a fault. The HTML's sign says its word in four languages;
   the page's lang (index.html, Localization.js) picks the one shown. */
body.loading { pointer-events: none; }                      /* no click and no tap lands anywhere until the page is whole */
#bootBusy [lang] { display: none; }
html[lang="de"] #bootBusy [lang="de"], html[lang="en"] #bootBusy [lang="en"],
html[lang="fr"] #bootBusy [lang="fr"], html[lang="it"] #bootBusy [lang="it"] { display: inline; }
body.loading .tile[disabled] { border-style: solid; opacity: .5; }
.busy-line { display: flex; align-items: center; justify-content: center; gap: 10px; font-size: 17px;
             position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: var(--z-seatCard, 10050);
             padding: 14px 22px; border-radius: 12px; color: var(--ink); white-space: nowrap;
             background: linear-gradient(rgba(var(--ink-rgb), .07), rgba(var(--ink-rgb), .07)), var(--backdrop-top);
             border: 1px solid rgba(var(--ink-rgb), .2); box-shadow: 0 8px 24px rgba(0,0,0,.35); }
.busy-line::before { content: ''; width: 20px; height: 20px; border-radius: 50%; border: 2.5px solid rgba(var(--ink-rgb), .3);
                     border-top-color: rgba(var(--ink-rgb), .9); animation: busy-turn 0.9s linear infinite; flex: none; }
@keyframes busy-turn { to { transform: rotate(360deg); } }
