/*
 * Chain Drop.
 *
 * No brand colour is written down in this file. Every surface reads a shared
 * custom property from brand.config.js, and the five block colours arrive as
 * --cd-fill-1..5 and --cd-ink-1..5, which game.js derives from --la-primary at
 * boot (the arithmetic is buildPalette in logic.js, where a test can measure
 * it). The only literal colour values below are neutral black and white
 * shadow alphas, which carry no hue and follow the block they sit on.
 *
 * Because a hue shift alone would fail a player with colour vision deficiency
 * — and would collapse entirely if a buyer picked a grey brand colour — the
 * five blocks are also set five measured steps apart in lightness, and each
 * carries its own shape. Shape is the real signal; colour is the shortcut.
 *
 * The three ideas that give the board its depth, in case you restyle it:
 *   1. The well is a shaft sunk into the page — a dark wash and an inner
 *      shadow — so the blocks read as objects dropped into it rather than
 *      squares painted on it.
 *   2. A block's material is a light top edge, a darker base and a lift
 *      shadow. Both washes stop clear of the middle 56% of the square, which
 *      is exactly the glyph box, so every glyph still sits on the flat fill
 *      that logic.js measured to 4.5:1. Never let a wash reach the glyph.
 *   3. Labels use --la-ink-soft, not --la-ink-faint: faint clears 4.5:1
 *      against none of these surfaces, soft clears it against all of them.
 */

.cd-stage {
  /*
   * The starting cell size, and on a roomy screen the final one. game.js
   * measures the space it is given and writes --cd-cell back; because the
   * controls below are cut to the well's own width, that measurement lands
   * on whatever value it started from and stays there — so this number is
   * what decides how big the well is on a desktop. It shrinks on a phone.
   */
  --cd-cell: 44px;
  --cd-gap: 4px;
  --cd-cols: 7;
  --cd-rows: 10;
  --cd-span: calc(var(--cd-cell) + var(--cd-gap));
  --cd-mini: calc(var(--cd-cell) * 0.62);

  /* The shaft's measurements, so the controls and the side column can be
     sized from the well instead of guessing at it. The 18px is the well's
     own padding and border — the same number game.js measures with, as
     WELL_CHROME. Change one and change the other. */
  --cd-shaft-w: calc(var(--cd-cols) * var(--cd-cell) + (var(--cd-cols) - 1) * var(--cd-gap));
  --cd-shaft-h: calc(var(--cd-rows) * var(--cd-cell) + (var(--cd-rows) - 1) * var(--cd-gap));
  --cd-well-w: calc(var(--cd-shaft-w) + 18px);
  --cd-well-h: calc(var(--cd-shaft-h) + 18px);

  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: clamp(10px, 2.5vw, 18px);
}

/* ---- Heads-up display ------------------------------------------------- */
/*
 * One card, not two: the preview and the round figures are a single piece of
 * furniture. Below 620px it is a strip above the well, cut to the well's own
 * width; above that it becomes the column beside it, top edge level with the
 * top of the shaft.
 */
.cd-hud {
  order: 1;
  /* Cut to the well's width, like the controls, so the three stacked pieces
     share one edge instead of nearly sharing one. */
  width: min(100%, var(--cd-well-w));
  display: flex;
  gap: 12px;
  align-items: stretch;
  padding: 10px 12px;
  background-color: var(--la-surface);
  border: 1px solid var(--la-line);
  border-radius: var(--la-radius);
  box-shadow: var(--la-shadow-sm), var(--la-shadow-inset);
}

.cd-panel {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The divider between the two halves — a hairline, not a second border. */
.cd-panel--stats {
  border-left: 1px solid var(--la-line);
  padding-left: 12px;
}

.cd-panel__title {
  margin: 0;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--la-ink-soft);
}

.cd-next {
  display: flex;
  gap: 8px;
  align-items: stretch;
}

/* Each preview is its own little shaft, so a double reads as two blocks
   stacked in one column rather than two blocks side by side. */
.cd-next__slot {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 3px;
  padding: 4px;
  min-height: calc(var(--cd-mini) * 2 + 11px);
  border-radius: var(--la-radius-sm);
  background-color: rgb(0 0 0 / 0.04);
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.07);
}

.cd-stats {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* .cd-stat also carries .la-chip from game.js — the pill is the shell's,
   sunken into the card the way the preview slots are. */
.cd-stat {
  justify-content: space-between;
  gap: 10px;
  padding: 7px 12px;
}

.cd-stat__label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--la-ink-soft);
}

/* Kills the browser's indent on a <dd>; the figure style is .la-stat__value. */
.cd-stat__value {
  margin: 0;
}

/* ---- The well --------------------------------------------------------- */
.cd-play {
  order: 2;
  /* min-width:0 keeps this column's width independent of the well it holds,
     which is what lets the script measure the space before sizing a cell. */
  flex: 1 1 260px;
  min-width: 0;
  display: grid;
  justify-items: center;
  gap: 10px;
}

/*
 * The well is a shaft cut into the page. .la-tray from the shell supplies the
 * sunken surface, the hairline and the radius; everything here deepens it —
 * a dark wash down the walls, a real inner shadow and a lit bottom lip.
 */
.cd-well {
  position: relative;
  padding: 8px;
  max-width: 100%;
  border-color: var(--la-line-strong);
  background-image: linear-gradient(
    180deg,
    rgb(0 0 0 / 0.22) 0,
    rgb(0 0 0 / 0.15) 18%,
    rgb(0 0 0 / 0.13) 100%
  );
  box-shadow:
    inset 0 4px 10px rgb(0 0 0 / 0.22),
    inset 0 -1px 0 rgb(255 255 255 / 0.18),
    0 1px 0 var(--la-surface-edge);
}

.cd-well:focus-visible {
  outline: 3px solid var(--la-primary);
  outline-offset: 3px;
}

.cd-field {
  position: relative;
  width: var(--cd-shaft-w);
  height: var(--cd-shaft-h);
  overflow: hidden;
  border-radius: 4px;
  /* Faint guides: the gap between two columns is a groove in the shaft wall,
     which gives the eye a lane to aim down. Kept lighter than the empty
     squares so the lanes read as guides and the squares as holes. */
  background-image: repeating-linear-gradient(
    to right,
    rgb(0 0 0 / 0) 0 var(--cd-cell),
    rgb(0 0 0 / 0.035) var(--cd-cell) var(--cd-span)
  );
  /* The well handles its own drags, so the browser must not steal them. */
  touch-action: none;
}

.cd-grid {
  display: grid;
  grid-template-columns: repeat(var(--cd-cols), var(--cd-cell));
  grid-auto-rows: var(--cd-cell);
  gap: var(--cd-gap);
}

.cd-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* ---- Blocks ----------------------------------------------------------- */
.cd-cell {
  position: relative;
  border-radius: 26%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* An empty square is barely there: enough to keep the grid legible, not
   enough to compete with a block. The shaft, not the squares, is the object. */
.cd-cell:not(.is-filled) {
  background-color: rgb(0 0 0 / 0.065);
}

/*
 * The material. The light wash is finished by 18% and the dark one does not
 * start until 82%, and the glyph box is the middle 56% — so the shape always
 * sits on the untouched fill and keeps the contrast logic.js measured.
 */
.cd-cell.is-filled {
  background-color: var(--la-primary);
  color: var(--la-on-primary);
  background-image: linear-gradient(
    180deg,
    rgb(255 255 255 / 0.26) 0,
    rgb(255 255 255 / 0) 18%,
    rgb(0 0 0 / 0) 82%,
    rgb(0 0 0 / 0.2) 100%
  );
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.38),
    inset 0 -2px 0 rgb(0 0 0 / 0.2),
    inset 0 0 0 1px rgb(0 0 0 / 0.07),
    0 1px 2px rgb(0 0 0 / 0.2);
}

.cd-glyph {
  width: 56%;
  height: 56%;
  display: block;
}

/* A preview block: the same material, smaller, scaled from the cell so the
   panel keeps its proportions whatever size the well ends up. */
.cd-mini {
  width: var(--cd-mini);
  height: var(--cd-mini);
  flex: none;
}

/*
 * The five block colours. Each one takes a fill and a glyph ink that game.js
 * worked out from the brand colour at boot: the fills climb a fixed ladder of
 * lightness so they stay apart in greyscale, and each ink is picked to clear
 * 4.5:1 against the block it sits on. The shape inside is the fallback that
 * never fails. Only the colour changes here — the material above is shared.
 */
.cd-cell.cd-colour-1 { background-color: var(--cd-fill-1); color: var(--cd-ink-1); }
.cd-cell.cd-colour-2 { background-color: var(--cd-fill-2); color: var(--cd-ink-2); }
.cd-cell.cd-colour-3 { background-color: var(--cd-fill-3); color: var(--cd-ink-3); }
.cd-cell.cd-colour-4 { background-color: var(--cd-fill-4); color: var(--cd-ink-4); }
.cd-cell.cd-colour-5 { background-color: var(--cd-fill-5); color: var(--cd-ink-5); }

.cd-falling,
.cd-hint {
  --x: 0;
  --y: 0;
  position: absolute;
  top: 0;
  left: 0;
  transform: translate(calc(var(--x) * var(--cd-span)), calc(var(--y) * var(--cd-span)));
}

.cd-falling {
  width: var(--cd-cell);
  height: var(--cd-cell);
  z-index: 2;
}

/* The piece in the air is lit a little harder than the stack it will join. */
.cd-falling.is-filled {
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.45),
    inset 0 -2px 0 rgb(0 0 0 / 0.2),
    inset 0 0 0 1px rgb(0 0 0 / 0.07),
    0 4px 8px rgb(0 0 0 / 0.26);
}

.cd-falling[hidden] { display: none; }

/* Where the block will land — the whole column lit, brightest at the floor,
   so the lane reads at a glance without outlining the piece twice. It runs
   the full height of the well but the settled stack paints over it (see the
   z-index note below), so it only ever lights the empty part of the column. */
.cd-hint {
  width: var(--cd-cell);
  height: 100%;
  border-radius: 5px;
  background-image: linear-gradient(
    180deg,
    var(--la-primary-tint) 0,
    var(--la-primary-glow) 100%
  );
  opacity: 0.85;
}

.cd-hint[hidden] { display: none; }

/* ---- Clearing --------------------------------------------------------- */
/*
 * One beat per link of the chain: the group flashes, swells, then shrinks
 * away — all of it inside 230ms, which is under the 240ms game.js waits
 * before it repaints the board for the next link.
 */
.cd-cell.is-clearing {
  animation: cd-pop 230ms cubic-bezier(0.3, 0.7, 0.4, 1) forwards;
  z-index: 3;
}

.cd-cell.is-clearing::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 0 var(--la-primary-glow);
  animation: cd-ring 230ms ease-out forwards;
}

@keyframes cd-pop {
  0% { transform: scale(1); filter: brightness(1); }
  22% { transform: scale(1.13); filter: brightness(1.55); }
  100% { transform: scale(0.18); opacity: 0; filter: brightness(1.2); }
}

@keyframes cd-ring {
  0% { box-shadow: 0 0 0 0 var(--la-primary-glow); }
  100% { box-shadow: 0 0 0 9px rgb(0 0 0 / 0); }
}

/*
 * Paint order inside the well, and the reason for it. A settled block sits
 * above the lit lane: the lane is a tint, and a tint over a block would move
 * the block's colour off the rung logic.js measured and pull its glyph below
 * 4.5:1. So the lane lights the empty squares only. Above the stack come the
 * falling piece, a clearing group, then the warning line — which has to stay
 * visible at exactly the moment the stack reaches it.
 */
.cd-grid .cd-cell.is-filled { z-index: 1; }
.cd-grid .cd-cell.is-clearing { z-index: 3; }

/* ---- The line you must not cross -------------------------------------- */
/*
 * Three states, and the stack is what moves between them: a quiet rule while
 * there is room, a warmer one once the stack is within a few rows, and a
 * brand-coloured rule with the top row shaded out once a block reaches the
 * row below the roof. The pulse runs three times and then holds, because a
 * warning that flashes forever stops being a warning.
 */
.cd-danger {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--cd-cell) + var(--cd-gap) / 2 - 1px);
  height: 2px;
  background-image: repeating-linear-gradient(
    to right,
    var(--la-ink-soft) 0 6px,
    transparent 6px 12px
  );
  opacity: 0.55;
  pointer-events: none;
  z-index: 4;
  transition: opacity 0.25s ease;
}

/* The forbidden row, shaded from the line upwards. */
.cd-danger::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: var(--cd-cell);
  background-image: linear-gradient(180deg, rgb(0 0 0 / 0) 0, var(--la-primary) 100%);
  opacity: 0;
  transition: opacity 0.25s ease;
}

.cd-field.is-close .cd-danger {
  opacity: 0.8;
}

.cd-field.is-close .cd-danger::before {
  opacity: 0.14;
}

.cd-field.is-danger .cd-danger {
  height: 3px;
  opacity: 1;
  background-image: repeating-linear-gradient(
    to right,
    var(--la-primary) 0 7px,
    transparent 7px 12px
  );
}

.cd-field.is-danger .cd-danger::before {
  opacity: 0.3;
  animation: cd-warn 780ms ease-in-out 3;
}

/* Three beats when the stack first reaches the line, then it holds bright.
   Written with its own 0% and 100% so the pulse is self-contained. */
@keyframes cd-warn {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 0.1; }
}

/* ---- Start, pause and game over --------------------------------------- */
.cd-curtain {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 14px;
  padding: 16px;
  border-radius: inherit;
  z-index: 6;
}

.cd-curtain[hidden] { display: none; }

/* A pseudo-element scrim, so the dimming works from the brand ink alone. */
.cd-curtain::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--la-ink);
  opacity: 0.72;
  border-radius: inherit;
  backdrop-filter: blur(2px);
}

.cd-curtain__title {
  position: relative;
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--la-bg);
  text-align: center;
}

.cd-curtain__btn { position: relative; }

/* ---- Controls --------------------------------------------------------- */
.cd-controls {
  /* The same width as the well, so the two read as one instrument. */
  width: min(100%, var(--cd-well-w));
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

/* Keycaps: a standing edge underneath, which is what the press takes away. */
.cd-control {
  min-width: 0;
  min-height: 44px;
  padding: 8px 6px;
  font-size: 15px;
  line-height: 1.2;
  border-radius: var(--la-radius-sm);
  box-shadow:
    0 2px 0 var(--la-line-strong),
    var(--la-shadow-sm),
    inset 0 1px 0 var(--la-surface-edge);
}

.cd-control:hover {
  transform: translateY(-1px);
  box-shadow:
    0 3px 0 var(--la-line-strong),
    var(--la-shadow-md),
    inset 0 1px 0 var(--la-surface-edge);
}

.cd-control:active {
  transform: translateY(2px);
  box-shadow:
    0 0 0 var(--la-line-strong),
    inset 0 2px 4px rgb(0 0 0 / 0.16);
}

/* Drop takes the width of two, so the second row fills out evenly, and it
   carries .la-btn--primary from game.js: it is the action of the game. */
.cd-control--drop {
  grid-column: span 2;
  letter-spacing: 0.02em;
}

.cd-control--drop:active {
  box-shadow: inset 0 2px 5px rgb(0 0 0 / 0.3);
}

@media (min-width: 620px) {
  .cd-stage {
    flex-wrap: nowrap;
    justify-content: center;
  }
  /*
   * The column sits to the right of the well, top edge level with the top of
   * the shaft and no taller than the information in it — a card stretched to
   * the full height of the well would be mostly empty space, which is the
   * look this pass exists to get rid of.
   */
  .cd-hud {
    order: 2;
    width: auto;
    flex: 0 0 196px;
    flex-direction: column;
    align-self: flex-start;
    padding: 14px;
    gap: 14px;
  }
  .cd-play { order: 1; flex: 0 1 auto; }
  .cd-panel { flex: 0 0 auto; }
  .cd-panel--stats {
    border-left: 0;
    border-top: 1px solid var(--la-line);
    padding-left: 0;
    padding-top: 14px;
  }
  .cd-stats { flex-direction: column; gap: 6px; }
  .cd-stat { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .cd-danger,
  .cd-danger::before { transition: none; }
  .cd-field.is-danger .cd-danger::before { animation: none; opacity: 0.3; }
  .cd-cell.is-clearing { animation: none; opacity: 0; }
  .cd-cell.is-clearing::after { animation: none; box-shadow: none; }
  .cd-control:hover { transform: none; }
}
