/**
 * Cascade — tool styles
 *
 * Cards, tabs, tables, buttons and dialogs all come from frikwork untouched.
 * What is here is the board geometry, the six tile identities, the special
 * markers, and the keyframes frikwork has no equivalent for — all switched off
 * in the reduced-motion block at the foot of the file, matching what frikwork
 * already does for its own.
 *
 * SHAPE FIRST, COLOUR SECOND
 * --------------------------
 * Every tile type carries a distinct silhouette — circle, diamond, triangle,
 * square, hexagon, star — cut with clip-path, and the colour reinforces it.
 * Six colour-only tiles are unusable for a colourblind player, and a match-3 is
 * the worst possible place to find that out: the whole game is "are these two
 * the same". The specials are drawn the same way, as knockouts cut out of the
 * tile's own fill rather than as glyphs, so they scale with the tile and never
 * become text sitting on a saturated background.
 *
 * WHY THERE ARE ONLY FIVE STATUS COLOURS AND SIX TILES
 * ----------------------------------------------------
 * --fw-success and --fw-brand-text are the SAME value in both palettes
 * (#94c83e dark, #5c8a1e light), so the token set offers five distinct hues,
 * not six. The sixth is mixed from two of them — half danger, half info, which
 * lands on a plum — rather than hardcoded, so it follows the theme like every
 * other colour here. It is also the star, the most distinctive silhouette of
 * the six, so it is the one type that would still be readable if the mix ever
 * drifted toward one of its parents.
 *
 * WHY THERE IS NO THEME-CONDITIONAL BLOCK IN THIS FILE
 * ----------------------------------------------------
 * tools/wordle/style.css needs one, and its comments are worth reading before
 * you assume this file is missing something. frikwork ships dark on bare :root
 * and declares light TWICE — once from [data-fw-theme="light"] and again from
 * `@media (prefers-color-scheme: light) { :root:not([data-fw-theme]) }`, which
 * re-declares the whole palette — so a variable defined once at :root does NOT
 * follow the theme, and that is what bit Wordle: its ink was --fw-on-primary,
 * which is near-black in every state.
 *
 * Nothing here has that problem, because every colour this file invents is a
 * color-mix() against --fw-text or a plain reference to --fw-bg-card, and
 * frikwork re-declares BOTH of those in BOTH light blocks. The mix is the same
 * trick Wordle settled on: --fw-text is by definition the pole furthest from
 * the surface, so `86% hue + 14% --fw-text` deepens a dark-theme colour toward
 * white and a light-theme colour toward black, and raises contrast either way
 * with one rule. Adding an empty two-selector block here to look like Wordle
 * would be cargo cult; what matters is that all four states were MEASURED, not
 * that the shape matches.
 *
 * Measured with a probe over frikwork 0.0.26's own token values in all four
 * states (explicit light, explicit dark, attribute-absent + OS light,
 * attribute-absent + OS dark). Worst text-grade ratio in use 4.63:1, worst
 * graphic ratio 4.20:1 — the numbers are quoted in the task report. Two raw
 * tokens were measured and REJECTED for text on a card: --fw-brand-text at
 * 4.11:1 and --fw-text-muted at 3.75:1, both in the light palette, which is the
 * documented .fw-help problem this tool declines to compound. Hence --csc-accent
 * and --csc-muted below, and hence .csc-help/.csc-note rather than .fw-help.
 *
 * WHERE THAT LEAVES TEXT THIS FILE DOES NOT AUTHOR
 * ------------------------------------------------
 * Five places on this page are painted by shared code — assets/css/app.css's
 * .app-stat__label and .app-empty p, frikwork's .fw-table td/th, and the
 * .fw-help that assets/js/games.js and includes/games.php both emit — and every
 * one of them is raw --fw-text-muted at 3.75:1. That is a shared-CSS problem
 * affecting four games, and fixing it in app.css would restyle three tools this
 * task has no business touching. So each one is out-specified HERE, scoped to
 * Cascade, and each override says which shared rule it is beating and why. The
 * requirement is every text the PAGE paints, not every text this file styles.
 *
 * --fw-bg-2 is deliberately NOT used to separate the board from the panel it
 * sits on: it resolves to the same #ffffff as --fw-bg-card in the light theme.
 * The board bed is --fw-bg-hover, the same reasoning Wordle's keyboard uses.
 */

:root {
  /* The knockout used for special markers. --fw-bg-card rather than
     --fw-on-primary precisely because bg-card DOES flip with the theme: it is
     near-black where --fw-text is white and white where --fw-text is near-black,
     which is the same pole the fills below are deepened away from. One
     declaration, correct in all four states. */
  --csc-ink: var(--fw-bg-card);

  --csc-t0: color-mix(in srgb, var(--fw-brand-text) 86%, var(--fw-text));
  --csc-t1: color-mix(in srgb, var(--fw-brand-2) 86%, var(--fw-text));
  --csc-t2: color-mix(in srgb, var(--fw-warning) 86%, var(--fw-text));
  --csc-t3: color-mix(in srgb, var(--fw-danger) 86%, var(--fw-text));
  --csc-t4: color-mix(in srgb, var(--fw-info) 86%, var(--fw-text));
  --csc-t5: color-mix(in srgb, color-mix(in srgb, var(--fw-danger) 50%, var(--fw-info)) 86%, var(--fw-text));

  /* Accent text and muted text, both deepened past the raw tokens — see the
     rejected measurements in the header. */
  --csc-accent: color-mix(in srgb, var(--fw-brand-text) 86%, var(--fw-text));
  --csc-muted: color-mix(in srgb, var(--fw-text-muted) 66%, var(--fw-text));
}

.csc-layout {
  display: grid;
  grid-template-columns: minmax(280px, 340px) 1fr;
  gap: var(--fw-space-lg);
  align-items: start;
}

.csc-play {
  display: flex;
  flex-direction: column;
  gap: var(--fw-space-lg);
  min-width: 0;
}

/* Replacements for .fw-help, which measures 3.75:1 on a card in the light
   palette. Same role, same size, a colour that clears AA. */
.csc-help,
.csc-note {
  color: var(--csc-muted);
  font-size: 0.85rem;
  line-height: 1.6;
}
.csc-note { margin: var(--fw-space-md) 0 0; }
.csc-help i,
.csc-note i { color: var(--csc-accent); }

/* --- Settings --------------------------------------------------------------
   .fw-tabs--segmented is an inline-flex track that scrolls when it runs out of
   room. In a 340px sidebar that would become a scroll strip, so the track is
   re-laid as a full-width grid and keeps its own chrome. */

.csc-seg {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  overflow-x: visible;
  width: 100%;
}

.csc-actions {
  display: grid;
  gap: var(--fw-space-sm);
}

.csc-keys {
  margin-top: var(--fw-space-md);
}

/* --- Stats ----------------------------------------------------------------- */

.csc-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: var(--fw-space-md);
  margin-bottom: var(--fw-space-md);
}

/* Each tile is wrapped so script.js has an id to hang off; the wrapper must not
   shrink the ccm_stat() card it holds. */
.csc-stat { display: flex; }
.csc-stat > .fw-card { flex: 1 1 auto; }

/* ccm_stat()'s label is --fw-text-muted (assets/css/app.css), which measures
   2.56:1 on a light-theme card — the same 3.75:1-token problem .fw-help has,
   arriving through a different component. Deepened here rather than in app.css,
   because app.css is shared with five other tools and this task is not the
   place to restyle them. Specificity (0,2,0) beats app.css's (0,1,0). */
.csc-stats .app-stat__label { color: var(--csc-muted); }

/* The live multiplier. Reserves its own height whether or not it holds
   anything, so a chain firing does not shove the board down the page and back
   again mid-cascade. */
.csc-chain {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--fw-space-sm);
  min-height: 2rem;
  margin: 0 0 var(--fw-space-sm);
  font-weight: 700;
  color: var(--csc-accent);
}

.csc-chain__badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  padding: 0.15rem 0.6rem;
  border-radius: var(--fw-radius-pill);
  background: var(--csc-accent);
  color: var(--csc-ink);
  font-variant-numeric: tabular-nums;
}

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

.csc-board-wrap {
  display: flex;
  justify-content: center;
}

.csc-board {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 2px;
  width: 100%;
  /* Square, and never wider than it is tall on a short screen. */
  max-width: min(30rem, 78vh);
  padding: var(--fw-space-xs);
  border: 1px solid var(--fw-border);
  border-radius: var(--fw-radius-md);
  background: var(--fw-bg-hover);
  touch-action: manipulation;
}

/* A settling board must not take a second click that would land on the wrong
   tile by the time the animation finishes. */
.csc-board.is-busy { pointer-events: none; }

.csc-tile {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: var(--fw-radius-sm);
  background: transparent;
  cursor: pointer;
}

.csc-tile:focus-visible {
  outline: 3px solid var(--fw-text);
  outline-offset: -2px;
}

/* Picked up. Announced with aria-pressed as well, so the state is not carried
   by the ring alone. */
.csc-tile.is-selected {
  background: var(--fw-bg-card);
  box-shadow: inset 0 0 0 2px var(--csc-accent);
}

/* The silhouette. clip-path clips the whole subtree, which is what keeps the
   special markers below inside the shape instead of squaring it off. */
.csc-tile__face {
  position: absolute;
  inset: 9%;
  background: var(--csc-fill, var(--fw-text-muted));
  transition: transform var(--fw-dur-fast) var(--fw-ease-pop);
}

.csc-tile.is-selected .csc-tile__face { transform: scale(0.86); }
.csc-tile:hover .csc-tile__face { transform: scale(1.06); }

.csc-tile--t0 { --csc-fill: var(--csc-t0); }
.csc-tile--t1 { --csc-fill: var(--csc-t1); }
.csc-tile--t2 { --csc-fill: var(--csc-t2); }
.csc-tile--t3 { --csc-fill: var(--csc-t3); }
.csc-tile--t4 { --csc-fill: var(--csc-t4); }
.csc-tile--t5 { --csc-fill: var(--csc-t5); }

.csc-tile--t0 .csc-tile__face,
.csc-chip--t0 .csc-chip__face { clip-path: circle(50% at 50% 50%); }

.csc-tile--t1 .csc-tile__face,
.csc-chip--t1 .csc-chip__face { clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); }

.csc-tile--t2 .csc-tile__face,
.csc-chip--t2 .csc-chip__face { clip-path: polygon(50% 4%, 100% 96%, 0% 96%); }

.csc-tile--t3 .csc-tile__face,
.csc-chip--t3 .csc-chip__face { clip-path: inset(4% round 14%); }

.csc-tile--t4 .csc-tile__face,
.csc-chip--t4 .csc-chip__face { clip-path: polygon(25% 4%, 75% 4%, 100% 50%, 75% 96%, 25% 96%, 0% 50%); }

.csc-tile--t5 .csc-tile__face,
.csc-chip--t5 .csc-chip__face {
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%,
                     21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

/* The special markers, cut out of the fill as gradients rather than drawn as
   glyphs: every stop is a percentage, so they scale exactly with the tile and
   there is no font, no icon kit and no text sitting on a saturated colour. */
.csc-tile__mark {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.csc-tile.is-lineh .csc-tile__mark {
  background: linear-gradient(to bottom,
    transparent 0 40%, var(--csc-ink) 40% 60%, transparent 60%);
}

.csc-tile.is-linev .csc-tile__mark {
  background: linear-gradient(to right,
    transparent 0 40%, var(--csc-ink) 40% 60%, transparent 60%);
}

.csc-tile.is-prism .csc-tile__mark {
  background: radial-gradient(circle at 50% 50%,
    transparent 0 24%, var(--csc-ink) 24% 42%, transparent 42%);
}

.csc-status {
  margin: var(--fw-space-lg) 0 0;
  padding: var(--fw-space-sm) var(--fw-space-md);
  border: 1px solid var(--fw-border);
  border-radius: var(--fw-radius-md);
  /* --fw-bg-2 is the same white as the card in the light palette; the border is
     what separates this strip, not the fill. */
  background: var(--fw-bg-2);
  color: var(--fw-text-2);
  text-align: center;
  min-height: 2.75rem;
}

/* --- Leaderboard ----------------------------------------------------------- */

.csc-board__scope {
  margin: 0 0 var(--fw-space-sm);
  font-weight: 600;
  color: var(--csc-accent);
}

/* ccmGames.renderBoard() puts classPrefix on the rank and value columns and on
   the <td> holding the initials — never on the "Initials" <th>, which carries no
   class in any game. These rules are written to be correct either way.

   The rank colour is qualified with .csc-board-table on purpose. frikwork's own
   `.fw-table td` and `.fw-table th` rules are (0,1,1) and would otherwise beat a
   bare `.csc-board__rank` at (0,1,0) — which is how the rank column ends up
   rendering at raw --fw-text-muted (3.75:1 on a light card) while a stylesheet
   that plainly says --csc-muted sits three lines above it. Measured, not
   guessed; see the header. */
.csc-board__rank { width: 3rem; }
.csc-board-table .csc-board__rank { color: var(--csc-muted); }
.csc-board__initials { font-weight: 700; letter-spacing: 0.06em; }
.csc-board__num { text-align: right; font-variant-numeric: tabular-nums; }
.csc-board__note { margin-bottom: 0; }

/* THE TWO STATES ccmGames.renderBoard() PAINTS, WHICH THIS FILE DOES NOT OWN.
   The empty state is `.app-empty p` (assets/css/app.css) and the error state is
   `<p class="fw-help">` (assets/js/games.js) — both raw --fw-text-muted, both
   3.75:1 on a light card, and between them they are what a first-time visitor
   sees: the empty state is the default rendering of this panel on day one and
   every morning a fresh daily scope opens. Scoped by id so the fix stays inside
   Cascade and the shared problem is left where it is; an id beats app.css's
   (0,2,0) and frikwork's (0,1,0) alike. Measured, like everything else here. */
#cscLeaderboard .app-empty p,
#cscLeaderboard .fw-help { color: var(--csc-muted); }

/* --- Dialog content -------------------------------------------------------- */

.csc-verdict {
  margin: 0 0 var(--fw-space-md);
  padding: var(--fw-space-md);
  border: 1px solid var(--fw-border);
  border-radius: var(--fw-radius-md);
  background: var(--fw-bg-2);
  line-height: 1.5;
}

.csc-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
  gap: var(--fw-space-sm);
  margin: 0 0 var(--fw-space-md);
  padding: var(--fw-space-md);
  border: 1px solid var(--fw-border);
  border-radius: var(--fw-radius-md);
  background: var(--fw-bg-2);
}

.csc-facts__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  text-align: center;
}

.csc-facts dt {
  font-size: 0.8rem;
  color: var(--csc-muted);
}

.csc-facts dd {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--csc-accent);
}

.csc-rules {
  display: grid;
  gap: var(--fw-space-sm);
  margin: 0 0 var(--fw-space-md);
  padding-left: var(--fw-space-lg);
}

.csc-legend__title {
  margin: var(--fw-space-lg) 0 var(--fw-space-sm);
  font-size: 1rem;
}

.csc-legend {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--fw-space-sm);
  margin: 0 0 var(--fw-space-md);
  padding: 0;
  list-style: none;
}

/* One per row, chips pinned to the top of a description that wraps to two
   lines, and the name kept on one line — "Line Blast" breaking after "Line"
   left the two chips floating beside a word. */
.csc-legend--specials { grid-template-columns: 1fr; }
.csc-legend--specials li { align-items: flex-start; }
.csc-legend--specials strong { white-space: nowrap; }

.csc-legend li {
  display: flex;
  align-items: center;
  gap: var(--fw-space-sm);
  color: var(--fw-text-2);
}

/* The legend chips are the board tiles at legend size — same clip-paths, same
   fills, same marker gradients, so what the dialog shows is what the board
   draws. */
.csc-chip {
  position: relative;
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
}

.csc-chip__face {
  position: absolute;
  inset: 0;
  background: var(--csc-fill, var(--fw-text-muted));
}

.csc-chip--t0 { --csc-fill: var(--csc-t0); }
.csc-chip--t1 { --csc-fill: var(--csc-t1); }
.csc-chip--t2 { --csc-fill: var(--csc-t2); }
.csc-chip--t3 { --csc-fill: var(--csc-t3); }
.csc-chip--t4 { --csc-fill: var(--csc-t4); }
.csc-chip--t5 { --csc-fill: var(--csc-t5); }

/* The legend chips carry their marker on the face itself, since they have no
   separate mark element to clip. */
.csc-chip.is-lineh .csc-chip__face {
  background:
    linear-gradient(to bottom, transparent 0 40%, var(--csc-ink) 40% 60%, transparent 60%),
    var(--csc-fill);
}
.csc-chip.is-linev .csc-chip__face {
  background:
    linear-gradient(to right, transparent 0 40%, var(--csc-ink) 40% 60%, transparent 60%),
    var(--csc-fill);
}
.csc-chip.is-prism .csc-chip__face {
  background:
    radial-gradient(circle at 50% 50%, transparent 0 24%, var(--csc-ink) 24% 42%, transparent 42%),
    var(--csc-fill);
}

.csc-scoretable { margin-top: var(--fw-space-md); }
.csc-scoretable__caption {
  caption-side: top;
  padding-bottom: var(--fw-space-xs);
  color: var(--csc-muted);
  font-size: 0.85rem;
  text-align: left;
}
.csc-scoretable__num { text-align: right; font-variant-numeric: tabular-nums; }

/* The initials prompt's help line, which ccm_game_initials_modal() renders as
   `<small class="fw-help">` inside a dialog this page owns. Same raw
   --fw-text-muted, same 3.75:1, and it is load-bearing copy — askInitials()
   rewrites it to say WHY a submission was refused. Scoped by the modal's id so
   the other three games' prompts are untouched. */
#cscInitialsModal .fw-help { color: var(--csc-muted); }

.csc-initials {
  text-transform: uppercase;
  letter-spacing: 0.3em;
  text-align: center;
  font-size: 1.4rem;
  font-weight: 700;
}

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

/* A swap the board will not take: the two tiles lean into each other and give
   up. Deliberately not a shake — the direction says WHY it failed. */
@keyframes csc-reject {
  0%, 100% { transform: scale(1); }
  35%      { transform: scale(0.82) rotate(-6deg); }
  70%      { transform: scale(1.04) rotate(4deg); }
}
.csc-tile.is-reject .csc-tile__face { animation: csc-reject 0.3s ease-in-out; }

/* The swap itself, before the board settles. */
@keyframes csc-swap {
  0%   { transform: scale(1); }
  50%  { transform: scale(0.78); }
  100% { transform: scale(1); }
}
.csc-tile.is-swapping .csc-tile__face { animation: csc-swap 0.16s ease-out; }

/* Matched, about to go. */
@keyframes csc-pop {
  0%   { transform: scale(1); opacity: 1; }
  40%  { transform: scale(1.22); opacity: 1; }
  100% { transform: scale(0.1); opacity: 0; }
}
.csc-tile.is-clearing .csc-tile__face { animation: csc-pop 0.2s ease-in forwards; }

/* Landed after a fall. Cheap and uniform rather than a per-tile distance: the
   trace says WHICH tiles moved, and a single settle beat reads as gravity
   without pretending to be a physics engine. */
@keyframes csc-drop {
  from { transform: translateY(-28%); opacity: 0.35; }
  to   { transform: translateY(0); opacity: 1; }
}
.csc-tile.is-dropping .csc-tile__face { animation: csc-drop 0.22s var(--fw-ease-settle); }

/* A special being planted.
   EVERY DURATION IN THIS BLOCK IS BOUNDED BY ITS BEAT IN script.js, because the
   class is taken off when the beat ends and paint() rebuilds className at the
   same moment: 0.16s < SWAP_MS 170, 0.2s < CLEAR_MS 210, and both of the
   fall-beat animations < FALL_MS 230. This one shipped at 0.3s against a 230ms
   beat and clipped at ~77%, so a forged special snapped to size mid-flourish —
   which looked like a rendering glitch rather than a shorter animation.
   Change a beat and change its keyframe with it. */
@keyframes csc-forge {
  from { transform: scale(0.4) rotate(-25deg); }
  to   { transform: scale(1) rotate(0); }
}
.csc-tile.is-forged .csc-tile__face { animation: csc-forge 0.22s var(--fw-ease-pop); }

/* --- Responsive ------------------------------------------------------------ */

@media (max-width: 960px) {
  .csc-layout { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  .csc-board { max-width: 100%; gap: 1px; }
  .csc-keys { display: none; }
  .csc-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--fw-space-sm); }
}

/* Same contract frikwork honours for its own animations: no motion, but nothing
   below costs a player information. Every state that matters — the fill, the
   shape, the special marker, the selection ring — is a class or a colour, not a
   keyframe, and script.js additionally collapses the cascade timeline to zero
   under the same query so a settled board arrives at once instead of slowly. */
@media (prefers-reduced-motion: reduce) {
  .csc-tile__face { transition: none; }
  .csc-tile:hover .csc-tile__face { transform: none; }
  .csc-tile.is-reject .csc-tile__face,
  .csc-tile.is-swapping .csc-tile__face,
  .csc-tile.is-clearing .csc-tile__face,
  .csc-tile.is-dropping .csc-tile__face,
  .csc-tile.is-forged .csc-tile__face { animation: none; }
  /* is-clearing is `forwards` above, so without this the tile would be left
     invisible when the animation is switched off. */
  .csc-tile.is-clearing .csc-tile__face { opacity: 1; transform: none; }
}
