/* ============================================================================
   sameshuffle kit — shared layout + finish screen for every game.

   CANONICAL SOURCE: SameShuffle/kit/ss-kit.css
   Do not edit the copy inside a game repo — edit here and run tools/sync-kit.sh.

   The kit owns LAYOUT and SCREEN BEHAVIOUR, never colour. Each game keeps its
   own palette by setting the tokens below in its own stylesheet; the kit only
   ever reads them, so Portals stays neon-purple and Chroma stays paper-white
   while both lay out identically.

   Tokens a game should set on :root
   ---------------------------------
     --ss-bg           page background
     --ss-surface      panel / card background
     --ss-surface-2    inset background (inputs, odd rows)
     --ss-border       hairline border
     --ss-border-hi    border on hover / focus
     --ss-text         primary text
     --ss-muted        secondary text
     --ss-accent       the game's highlight colour
     --ss-accent-ink   text drawn ON --ss-accent
     --ss-good         success (submitted)
     --ss-bad          error
     --ss-radius       corner radius for panels
     --ss-font         body/UI font stack
     --ss-font-display display font stack (titles)
   ========================================================================= */

:root{
  /* Neutral fallbacks so the kit is never unstyled if a game forgets a token. */
  --ss-bg:#0b0e14; --ss-surface:#10141e; --ss-surface-2:#161c29;
  --ss-border:#1f2836; --ss-border-hi:#3c4a68;
  --ss-text:#e7ecf5; --ss-muted:#7d8a98;
  --ss-accent:#4cc9f0; --ss-accent-ink:#06121a;
  --ss-good:#3fbf87; --ss-bad:#ff6b6b;
  --ss-radius:12px;
  /* One height for every button in the actions column, so a shorter label or a
     smaller font can't make a button visibly stubbier than the one above it. */
  --ss-btn-h:36px;
  --ss-font:ui-monospace,SFMono-Regular,Menlo,monospace;
  --ss-font-display:var(--ss-font);
  --ss-gap:10px;
}

/* ---------------------------------------------------------------- shell ---
   Portals' skeleton, which is the one that works: a full-height column with the
   board taking whatever is left over. The board is then never bigger than the
   space available (so it can't push the controls off the screen) and never
   smaller either (no guessed vh cap to get wrong). Everything else on the page
   is chrome, and chrome is kept to two compact rows. */
.ss-app{
  display:flex; flex-direction:column;
  /* The column IS the viewport, not merely at least as tall as it. That is
     what forces the board to absorb the remainder rather than the page
     growing past the bottom edge. */
  height:100vh; height:100dvh;
  max-width:560px; margin:0 auto;
  padding-top:    max(10px, env(safe-area-inset-top));
  padding-right:  max(12px, env(safe-area-inset-right));
  padding-bottom: max(14px, env(safe-area-inset-bottom));
  padding-left:   max(12px, env(safe-area-inset-left));
  gap:8px;
}
/* The board sits in the leftover space, centred. min-height:0 is what lets a
   flex child actually shrink below its content size. */
.ss-board{
  position:relative; flex:1 1 auto; min-height:0;
  display:flex; align-items:center; justify-content:center;
}
/* Never let the board collapse to nothing if the chrome is unusually tall —
   better to overflow slightly and scroll than to have no board. */
.ss-board{min-height:140px}
/* A square board, exactly as large as the leftover space allows. CSS alone
   can't express this: a percentage height against a flex-sized parent is an
   indefinite height and collapses, and container query units read 0 inside a
   size-contained flex child. So SS.fitSquare() measures the box and sets it —
   which is what Portals has always done in its renderer. Until it runs, fill
   the width, so the board is never invisible. */
.ss-board > .ss-square{width:100%; aspect-ratio:1}

/* --------------------------------------------------------------- top bar ---
   Two rows, Portals' arrangement: wordmark + date above, the Daily / Random
   segment and any icon buttons below. Folding the mode buttons into the header
   rather than giving them their own full-width row is worth ~50px, which is
   the difference between the board fitting and not. */
.ss-top{
  display:grid;
  grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:
    "title date"
    "tabs  extra";
  align-items:center;
  gap:14px 10px;
  flex:0 0 auto;
}
.ss-wordmark{
  grid-area:title; justify-self:start; min-width:0;
  font-family:var(--ss-font-display); font-weight:800;
  font-size:clamp(20px,6vw,28px); line-height:1; letter-spacing:.04em;
  margin:0; color:var(--ss-text);
}
.ss-top-r{grid-area:date; justify-self:end; text-align:right; min-width:0}
.ss-mode-label{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:var(--ss-accent);
}
.ss-date{font-size:12px; color:var(--ss-muted); margin-top:2px; white-space:nowrap}
.ss-seg{
  grid-area:tabs; justify-self:start;
  display:flex; gap:4px;
  background:var(--ss-surface); border:1px solid var(--ss-border);
  border-radius:calc(var(--ss-radius) - 1px); padding:3px;
}
.ss-seg button{
  appearance:none; background:transparent; border:0; cursor:pointer;
  font:inherit; font-family:var(--ss-font); font-weight:600; font-size:12px;
  letter-spacing:.06em; text-transform:uppercase; color:var(--ss-muted);
  padding:7px 15px; border-radius:calc(var(--ss-radius) - 5px);
  transition:background .12s,color .12s;
}
.ss-seg button.on{background:var(--ss-surface-2); color:var(--ss-accent)}
.ss-seg button:focus-visible{outline:2px solid var(--ss-accent); outline-offset:2px}
.ss-top-extra{grid-area:extra; justify-self:end; display:flex; align-items:center; gap:6px}

/* Icon-only controls for the header. A whole row of the actions column is worth
   ~40px, which on a short screen is the difference between the board reaching
   the full width and falling short of it — so the two controls that need no
   explanation move up here. They keep an accessible name via aria-label. */
.ss-icon-btn{
  appearance:none; cursor:pointer; flex:0 0 auto; padding:0;
  width:var(--ss-icon-size,36px); height:var(--ss-icon-size,36px);
  display:inline-flex; align-items:center; justify-content:center;
  color:var(--ss-muted); background:var(--ss-surface);
  border:1px solid var(--ss-border); border-radius:calc(var(--ss-radius) - 1px);
  -webkit-tap-highlight-color:transparent;
  transition:color .12s,border-color .12s,background .12s,transform .06s;
}
.ss-icon-btn:hover{color:var(--ss-text); border-color:var(--ss-border-hi)}
.ss-icon-btn:active{transform:translateY(1px)}
.ss-icon-btn:focus-visible{outline:2px solid var(--ss-accent); outline-offset:2px}
.ss-icon-btn svg{width:19px; height:19px; display:block}

/* -------------------------------------------------------------- controls ---
   Portals' below-board orientation, which reads cleanest: d-pad anchored
   bottom-LEFT, stat + actions stacked on the RIGHT. Restart is now a full
   column away from Up, so it can't be fat-fingered mid-move. */
.ss-controls{
  display:flex; align-items:flex-start; gap:14px;
  margin-top:14px; -webkit-user-select:none; user-select:none;
}
.ss-dpad{
  flex:0 0 auto;
  display:grid;
  grid-template-columns:repeat(3,var(--ss-dpad-size,52px));
  grid-template-rows:repeat(2,var(--ss-dpad-size,52px));
  gap:6px;
}
.ss-dpad button{
  appearance:none; cursor:pointer; font:inherit; font-size:16px;
  display:flex; align-items:center; justify-content:center;
  color:var(--ss-text); background:var(--ss-surface);
  border:1px solid var(--ss-border); border-radius:calc(var(--ss-radius) - 1px);
  -webkit-tap-highlight-color:transparent;
  transition:border-color .12s,background .12s,transform .06s;
}
.ss-dpad button:hover{border-color:var(--ss-border-hi)}
.ss-dpad button:active{transform:translateY(1px); background:var(--ss-surface-2)}
.ss-dpad button:focus-visible{outline:2px solid var(--ss-accent); outline-offset:2px}
.ss-dpad .up{grid-area:1/2}
.ss-dpad .left{grid-area:2/1}
.ss-dpad .down{grid-area:2/2}
.ss-dpad .right{grid-area:2/3}

.ss-actions{flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:8px}
.ss-actions .ss-btn,
.ss-actions-row .ss-btn{min-height:var(--ss-btn-h); display:flex; align-items:center; justify-content:center}
/* A readout, not a control: unfilled where the buttons are filled, with the
   figure in the accent colour — the same distinction Portals draws. */
.ss-actions-stat{
  font-size:13px; color:var(--ss-muted); text-align:center;
  background:transparent; border:1px solid var(--ss-border);
  border-radius:calc(var(--ss-radius) - 1px); padding:8px 10px;
  letter-spacing:.02em;
}
.ss-actions-stat b{color:var(--ss-accent); font-size:16px; font-weight:700; font-variant-numeric:tabular-nums}
.ss-actions-row{display:flex; gap:8px}
.ss-actions-row .ss-btn{flex:1 1 0; min-width:0; font-size:clamp(10px,2.9vw,12px)}
/* Buttons whose labels differ a lot in length share the row by what they need
   rather than splitting it evenly, so "Leaderboard" isn't squeezed to the
   width of "Undo". */
.ss-actions-row.ss-fit{gap:6px}
.ss-actions-row.ss-fit .ss-btn{
  flex:1 1 auto; padding-left:6px; padding-right:6px;
  font-size:clamp(9.5px,2.6vw,11.5px); letter-spacing:.02em;
}
/* Leaderboard sits with Undo/Restart rather than in the header — same as
   Portals, and it keeps the header down to two rows. */
.ss-lb-btn{width:100%}

/* ---------------------------------------------------------------- button ---
   One button, three weights: default, .ss-primary (the obvious next step) and
   .ss-quiet (destructive / secondary). */
.ss-btn{
  appearance:none; cursor:pointer; font:inherit; font-family:var(--ss-font);
  font-weight:600; font-size:12px; letter-spacing:.05em;
  color:var(--ss-text); background:var(--ss-surface);
  border:1px solid var(--ss-border); border-radius:calc(var(--ss-radius) - 1px);
  padding:12px 14px; text-align:center;
  /* Never clip a label, and never split a word to avoid it — "Leaderboa/rd"
     is worse than either. Wrap at spaces only; the row below gives a long
     label more of the row instead. */
  white-space:normal; overflow-wrap:normal; word-break:keep-all;
  line-height:1.2; min-width:0;
  -webkit-tap-highlight-color:transparent;
  transition:border-color .12s,background .12s,transform .06s,opacity .12s;
}
.ss-btn:hover{border-color:var(--ss-border-hi)}
.ss-btn:active{transform:translateY(1px)}
.ss-btn:focus-visible{outline:2px solid var(--ss-accent); outline-offset:2px}
.ss-btn:disabled{opacity:.45; cursor:default; transform:none}
.ss-btn.ss-primary{background:var(--ss-accent); border-color:var(--ss-accent); color:var(--ss-accent-ink)}
.ss-btn.ss-quiet{color:var(--ss-muted)}

/* ----------------------------------------------------------------- modal ---
   Every full-screen game surface (finish screen, leaderboard, how-to-play)
   uses this shell, so they open, scroll and close identically. */
.ss-modal{
  position:fixed; inset:0; z-index:200; display:none;
  align-items:center; justify-content:center; padding:20px;
  background:rgba(6,8,13,.72); -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
}
.ss-modal.ss-show{display:flex}
/* Several games pin the page (overflow:hidden / touch-action:none on body) so a
   board swipe can't be stolen by a page scroll. A touch-action:none ancestor
   also blocks panning INSIDE the card, which would strand its content on a
   short screen — so while a kit screen is open, unpin the page. */
html.ss-modal-open, html.ss-modal-open body{touch-action:auto !important;}
html.ss-modal-open body{overflow:hidden !important;}
.ss-card{
  position:relative; width:100%; max-width:360px;
  max-height:min(86vh,86dvh); overflow-y:auto; overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  background:var(--ss-surface); border:1px solid var(--ss-border);
  border-radius:calc(var(--ss-radius) + 6px);
  padding:30px 20px 20px; text-align:center;
  font-family:var(--ss-font); color:var(--ss-text);
}
.ss-x{
  position:absolute; top:6px; right:8px; z-index:1;
  appearance:none; background:none; border:0; cursor:pointer; padding:2px 9px;
  color:var(--ss-muted); font-size:22px; line-height:1; transition:color .12s;
}
.ss-x:hover{color:var(--ss-text)}
.ss-x:focus-visible{outline:2px solid var(--ss-accent); outline-offset:2px; border-radius:8px}

/* --------------------------------------------------------- finish screen ---
   The same screen in every game: what you did, submit once, today's board,
   then the same three ways out. */
.ss-fin-title{
  font-family:var(--ss-font-display); font-weight:800;
  font-size:26px; line-height:1.15; margin:0 0 6px; color:var(--ss-text);
}
.ss-fin-detail{margin:0; font-size:13px; color:var(--ss-muted); line-height:1.5}
.ss-fin-detail b{color:var(--ss-accent); font-weight:700}
.ss-fin-score{
  display:flex; align-items:baseline; justify-content:center; gap:8px;
  margin:14px 0 2px;
}
.ss-fin-score b{
  font-family:var(--ss-font-display); font-weight:800; font-size:42px; line-height:1;
  color:var(--ss-accent); font-variant-numeric:tabular-nums;
}
.ss-fin-score span{
  font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:var(--ss-muted);
}
.ss-fin-note{margin:10px 0 0; font-size:12px; color:var(--ss-muted); line-height:1.5}

/* Score breakdown — a receipt, not a sentence. What earned points is listed
   first and adds up to the number above; how the run went follows, quieter,
   under a rule. Labels left, figures right, so the eye can scan one column. */
.ss-fin-rows{margin:16px 0 0; text-align:left}
.ss-fin-row{
  display:flex; align-items:baseline; justify-content:space-between; gap:12px;
  padding:7px 2px; font-size:13px;
}
.ss-fin-row + .ss-fin-row{border-top:1px solid var(--ss-border)}
.ss-fin-row .k{color:var(--ss-muted); min-width:0}
.ss-fin-row .v{
  flex:0 0 auto; font-weight:700; font-variant-numeric:tabular-nums; color:var(--ss-text);
}
.ss-fin-row.ss-sum .v{color:var(--ss-accent)}
.ss-fin-row.ss-stat{font-size:12px; padding:5px 2px}
.ss-fin-row.ss-stat .v{font-weight:600; color:var(--ss-muted)}
/* the first stat row starts the quieter section */
.ss-fin-row.ss-stat-first{border-top:2px solid var(--ss-border); margin-top:2px; padding-top:9px}

/* submit — present exactly until the score is in, then gone for good */
.ss-submit{display:flex; gap:8px; margin:16px 0 0}
.ss-submit input{
  flex:1 1 auto; min-width:0; font:inherit; font-family:var(--ss-font); font-size:13px;
  padding:11px 12px; color:var(--ss-text); background:var(--ss-surface-2);
  border:1px solid var(--ss-border); border-radius:calc(var(--ss-radius) - 3px);
}
.ss-submit input:focus{outline:2px solid var(--ss-accent); outline-offset:1px}
.ss-submit .ss-btn{flex:0 0 auto; padding:11px 22px}
.ss-submit-msg{margin:8px 0 0; min-height:15px; font-size:11px; color:var(--ss-muted)}
.ss-submit-msg.ss-err{color:var(--ss-bad)}
.ss-submit-msg.ss-ok{color:var(--ss-good)}
.ss-submitted{
  display:flex; align-items:center; justify-content:center; gap:7px;
  margin:16px 0 0; padding:11px 12px; font-size:12px; color:var(--ss-good);
  background:var(--ss-surface-2); border:1px solid var(--ss-border);
  border-radius:calc(var(--ss-radius) - 3px);
}
.ss-submitted b{color:var(--ss-text); font-weight:600}

/* the three ways out — same order, same words, every game */
.ss-fin-actions{display:flex; flex-direction:column; gap:8px; margin-top:18px}
.ss-fin-actions .ss-btn{width:100%; padding:13px 14px}

/* ----------------------------------------------------------- leaderboard ---
   One leaderboard rendering, used inline on the finish screen and inside the
   standalone leaderboard modal. */
.ss-lb{margin-top:18px; text-align:left}
.ss-lb-h{
  margin:0 0 8px; text-align:center; font-size:10px; font-weight:600;
  letter-spacing:.16em; text-transform:uppercase; color:var(--ss-muted);
}
.ss-lb-note{margin:8px 0; text-align:center; font-size:12px; color:var(--ss-muted)}
.ss-lb-list{list-style:none; margin:0; padding:0; counter-reset:ssr}
.ss-lb-list li{
  display:flex; align-items:center; gap:8px;
  padding:7px 10px; border-radius:calc(var(--ss-radius) - 4px); font-size:13px;
}
.ss-lb-list li::before{
  counter-increment:ssr; content:counter(ssr);
  flex:0 0 20px; font-size:11px; color:var(--ss-muted);
  font-variant-numeric:tabular-nums; text-align:right;
}
.ss-lb-list li:nth-child(odd){background:var(--ss-surface-2)}
.ss-lb-list li.ss-me{
  color:var(--ss-accent); box-shadow:inset 0 0 0 1.5px var(--ss-accent);
}
.ss-lb-name{flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.ss-lb-score{flex:0 0 auto; font-weight:700; font-variant-numeric:tabular-nums}

/* ---------------------------------------------------------- how to play ---
   The rules open in the standard modal instead of a <details> that pushes the
   board off-screen, so they always fit without scrolling the game away. */
.ss-how{text-align:left; font-size:13px; line-height:1.55; color:var(--ss-muted)}
.ss-how p{margin:0 0 10px}
.ss-how p:last-child{margin-bottom:0}
.ss-how b,.ss-how strong{color:var(--ss-text); font-weight:700}

/* ------------------------------------------------------------------ misc ---*/
.ss-hidden{display:none !important}
@media (prefers-reduced-motion:reduce){
  .ss-btn,.ss-dpad button,.ss-seg button{transition:none !important}
}
@media (max-width:400px){
  .ss-controls{gap:10px}
  .ss-dpad{--ss-dpad-size:clamp(44px,14.5vw,52px)}
  .ss-btn{font-size:11px; padding:11px 10px}
  .ss-card{padding:28px 16px 16px}
  .ss-fin-title{font-size:23px}
}

/* -------------------------------------------------------------- compact ---
   Inside the hub's player a game gets the tab height minus the player bar, and
   iOS Safari's own chrome takes more again. The board absorbs most of this by
   itself (it takes what is left over), so these only trim the chrome. */
/* Marked by each game on its own hint/status lines: worth showing when there
   is room, worth trading for board size when there isn't. */
@media (max-height:700px){
  :root{--ss-btn-h:34px; --ss-icon-size:33px}
  .ss-optional{display:none !important}
  .ss-app{gap:6px; padding-top:max(6px,env(safe-area-inset-top))}
  .ss-top{gap:10px 10px}
  .ss-seg button{padding:6px 13px; font-size:11px}
  .ss-controls{margin-top:8px}
  .ss-dpad{--ss-dpad-size:clamp(42px,13.5vw,48px)}
  .ss-actions{gap:6px}
  .ss-actions-stat{padding:6px 10px; font-size:12px}
  .ss-actions-stat b{font-size:15px}
  .ss-btn{padding:9px 12px}
}
@media (max-height:600px){
  :root{--ss-btn-h:32px; --ss-icon-size:31px}
  .ss-wordmark{font-size:20px}
  .ss-dpad{--ss-dpad-size:clamp(38px,12.5vw,44px)}
  .ss-btn{padding:8px 10px; font-size:11px}
  .ss-controls{margin-top:6px}
  .ss-actions{gap:5px}
}
