/* Color Path — design tokens forked from the "coral set" in
   apps/games/docs/maze-game-for-kids-grd.md Appendix B, the same values baked
   into /shared/game-header.css. Light-only by design: no game in this app has
   a dark mode and the portal pins color-scheme: light. */

:root {
  --coral: #ff8a5b;
  --sky: #4db8ff;
  --leaf: #5bc98c;
  --rose: #ff6fa5;
  --sun: #ffd166;
  --lav: #9c8cff;
  --ink: #2e2a3a;
  --ink-soft: #6b6478;
  --paper: #fffdf6;
  --card: #ffffff;
  --line: #efe6d8;
  --shadow: 0 10px 30px rgba(46, 42, 58, 0.12);
  --shadow-soft: 0 4px 14px rgba(46, 42, 58, 0.09);
  --radius: 22px;
  --radius-sm: 14px;
  --font-display: 'Fredoka', 'Baloo 2', 'Arial Rounded MT Bold', Arial, sans-serif;
  --font-body: 'Nunito', system-ui, -apple-system, Arial, sans-serif;
  --fs-sm: clamp(13px, 2.6vmin, 15px);
  --fs-md: clamp(15px, 3vmin, 18px);
  --fs-lg: clamp(22px, 5vmin, 32px);
  --fs-xl: clamp(30px, 8vmin, 56px);
  color-scheme: light;
}

* {
  box-sizing: border-box;
}

html {
  min-height: 100%;
  touch-action: manipulation;
}

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background:
    radial-gradient(circle at 12% 8%, rgba(77, 184, 255, 0.16), transparent 42%),
    radial-gradient(circle at 88% 12%, rgba(255, 138, 91, 0.16), transparent 40%),
    linear-gradient(180deg, var(--paper) 0%, #fff6f0 60%, #f2fbff 100%);
  -webkit-tap-highlight-color: transparent;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 200;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  background: var(--card);
  box-shadow: var(--shadow);
}
.skip-link:focus {
  left: 12px;
}

#particles {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 90;
}

/* ---- layout ------------------------------------------------------------- */

#app {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/*
 * On the play screen the board must size itself DOWN to the space available,
 * which only works if every ancestor has a definite height. The shared header
 * kit gives #app `flex: 1 1 auto; height: auto`, i.e. content-sized — so the
 * page happily grew past the viewport and scrolled instead of shrinking the
 * board. Forcing a zero flex-basis makes the height come from the flex
 * container rather than from the content, which bounds the whole chain.
 */
body[data-screen='play'] {
  overflow: hidden;
}

body[data-screen='play'].ppgh-body > #app {
  flex: 1 1 0;
  height: 0;
  min-height: 0;
}

.cp-screen {
  flex: 1 1 auto;
  display: flex;
  min-height: 0;
  animation: cp-fade 180ms ease-out;
}

.cp-page {
  flex: 1 1 auto;
  width: 100%;
  max-width: 1120px;
  margin: 0 auto;
  padding: 16px max(16px, calc((100% - 1120px) / 2));
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
}

.cp-page--center {
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: 18px;
}

.cp-page--play {
  gap: 10px;
  padding-top: 10px;
}

.cp-topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.cp-topbar__title {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 600;
}

/* ---- buttons ------------------------------------------------------------ */

.cp-btn {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 600;
  min-height: 56px;
  padding: 12px 22px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow-soft);
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.cp-btn:active {
  transform: scale(0.95);
}
.cp-btn:focus-visible {
  outline: 3px solid #facc15;
  outline-offset: 2px;
}
.cp-btn--primary {
  background: linear-gradient(140deg, var(--coral), var(--rose));
  color: #fff;
  box-shadow: var(--shadow);
}
.cp-btn--ghost {
  background: rgba(255, 255, 255, 0.86);
}
.cp-btn--icon {
  min-width: 56px;
  padding: 12px;
}
.cp-btn--sm {
  min-height: 46px;
  padding: 8px 16px;
  font-size: var(--fs-sm);
}
.cp-btn--danger {
  color: #b91c1c;
}

.cp-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

/* ---- home --------------------------------------------------------------- */

.cp-hero-mark {
  font-size: clamp(56px, 14vmin, 96px);
  line-height: 1;
}
.cp-title {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  margin: 0;
  background: linear-gradient(120deg, var(--coral), var(--lav));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.cp-tagline {
  font-size: var(--fs-md);
  color: var(--ink-soft);
  max-width: 30ch;
}
.cp-stat-line {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  color: var(--ink-soft);
}

/* ---- tier cards --------------------------------------------------------- */

.cp-tier-list {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.cp-tier {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px;
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow-soft);
  border: 2px solid var(--line);
}
.cp-tier.is-locked {
  opacity: 0.72;
}
.cp-tier__head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.cp-tier__emoji {
  font-size: 30px;
}
.cp-tier__name {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
}
.cp-tier__meta,
.cp-tier__lock {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
}

/* ---- level chips -------------------------------------------------------- */

.cp-level-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
}
.cp-level {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-height: 74px;
  padding: 8px 4px;
  border: 2px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  cursor: pointer;
  font-family: var(--font-display);
}
.cp-level.is-locked {
  opacity: 0.5;
  cursor: default;
}
.cp-level.is-next {
  border-color: var(--coral);
  box-shadow: 0 0 0 3px rgba(255, 138, 91, 0.22);
}
.cp-level__num {
  font-size: var(--fs-md);
  font-weight: 700;
}
.cp-level__stars {
  display: flex;
  gap: 1px;
}
.cp-star {
  color: #d9d2c6;
  font-size: 12px;
}
.cp-star.is-on {
  color: var(--sun);
}
.cp-star--big {
  font-size: clamp(30px, 7vmin, 46px);
}

/* ---- HUD ---------------------------------------------------------------- */

.cp-chip {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 8px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: var(--shadow-soft);
  font-family: var(--font-display);
  font-size: var(--fs-sm);
}
.cp-chip__label {
  color: var(--ink-soft);
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.04em;
}

.cp-progress {
  height: 12px;
  border-radius: 999px;
  background: rgba(46, 42, 58, 0.1);
  overflow: hidden;
}
.cp-progress__fill {
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--leaf), var(--sky));
  transition: width 180ms ease;
}
.cp-progress__label {
  font-size: var(--fs-sm);
  color: var(--ink-soft);
  text-align: center;
}

.cp-controls {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
}

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

/* Absorbs all remaining column height; min-height:0 is what lets it actually
   shrink below its content instead of forcing the page to overflow.
   `position: relative` anchors the absolutely-positioned board below, and
   `container-type: size` lets the board measure BOTH of the host's axes via
   cq units — which is how it picks the largest square that fits. */
.cp-board-host {
  position: relative;
  container-type: size;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  overflow: hidden;
}

/*
 * Square board that fills whatever space the flex column leaves, with no JS
 * resize listener and — deliberately — no hard-coded "chrome height" constant.
 * An earlier version subtracted a magic 290px and was wrong by ~50px once the
 * control buttons wrapped to two rows, pushing them off-screen. Letting the
 * host absorb the remaining height and clamping the board with max-width/
 * max-height means the layout corrects itself whenever the chrome changes.
 */
.cp-board {
  /* Absolutely positioned so it contributes NOTHING to its host's size. That
     is the crux: while the board was in flow, its aspect-ratio inflated the
     host, the host's height then fed back into the board's max-height, and the
     circular constraint resolved by overflowing the viewport instead of
     shrinking.

     The side length is computed directly from BOTH host axes rather than
     pinned to its edges. An earlier version used `inset: 0; margin: auto`,
     which sets left AND right (and top AND bottom) — that over-constrains the
     used width and height, so `aspect-ratio` is ignored outright and the board
     stretched to the host's full rectangle: 384x548 cells on a phone, with
     oval dots. Centring by transform leaves width free for min() to decide. */
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(100cqw, 100cqh);
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  padding: 6px;
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow);
  /* We own every gesture on this surface; without it the page scrolls out
     from under a drag and the ribbon breaks mid-stroke. */
  touch-action: none;
  user-select: none;
}

/* Container query units carry this layout, so a browser without them would
   otherwise get a zero-width board. Fall back to width-bound sizing: still
   square, just not height-aware on very short screens. */
@supports not (container-type: size) {
  .cp-board-host {
    display: flex;
  }
  .cp-board {
    position: relative;
    top: auto;
    left: auto;
    transform: none;
    width: 100%;
    max-height: 100%;
  }
}

.cp-row {
  display: flex;
  flex: 1 1 0;
  min-height: 0;
}

.cp-cell {
  position: relative;
  flex: 1 1 0;
  display: grid;
  place-items: center;
  border-radius: 6px;
  outline: 1px solid rgba(46, 42, 58, 0.07);
  outline-offset: -1px;
}

.cp-cell.has-cursor {
  outline: 3px solid #facc15;
  outline-offset: -3px;
  z-index: 3;
}

.cp-dot {
  display: none;
  position: relative;
  z-index: 2;
  width: 66%;
  height: 66%;
  border-radius: 50%;
  background: var(--dot);
  box-shadow: 0 2px 6px rgba(46, 42, 58, 0.25);
  /* The glyph is transparent until colour-blind mode turns it on. */
  color: transparent;
  font-size: clamp(9px, 2.2vmin, 15px);
  line-height: 1;
  place-items: center;
}

.cp-cell.is-endpoint .cp-dot {
  display: grid;
  animation: cp-pulse 2.4s ease-in-out infinite;
}

.cp-cell.is-joined .cp-dot {
  animation: none;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.9), 0 2px 8px rgba(46, 42, 58, 0.3);
}

/* The ribbon overlay. Never receives pointer events — hit-testing is
   arithmetic against the board rect, not the DOM. */
.cp-paths {
  position: absolute;
  inset: 6px;
  pointer-events: none;
  z-index: 1;
  overflow: visible;
}

/* Stroke width is in viewBox units, where 1 unit == 1 cell. Keeping it
   proportional (not non-scaling-stroke in px) means the ribbon reads the same
   on a 5x5 and a 10x10 board and at any viewport size. */
.cp-path {
  stroke-width: 0.46;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.95;
}

.cp-path.is-complete {
  filter: drop-shadow(0 0 0.06px currentColor);
}

.cp-lead {
  stroke-width: 0.46;
  stroke-linecap: round;
  opacity: 0;
}
.cp-lead.is-active {
  opacity: 0.8;
}

.cp-cell.is-hinted {
  animation: cp-hint 1.2s ease-in-out;
}

/* Shown only once every pair is joined, so it never competes with the puzzle
   itself — at that point the bare squares ARE the remaining puzzle. */
.cp-cell.is-needed {
  background: rgba(255, 209, 102, 0.28);
  outline: 2px dashed rgba(214, 158, 46, 0.85);
  outline-offset: -3px;
  animation: cp-needed 1.6s ease-in-out infinite;
}

/* ---- modals ------------------------------------------------------------- */

.cp-overlay {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: grid;
  place-items: center;
  padding: 18px;
  background: rgba(46, 42, 58, 0.44);
  backdrop-filter: blur(4px);
  animation: cp-fade 180ms ease-out;
}

.cp-modal {
  width: min(420px, 100%);
  max-height: 90dvh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 24px;
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow);
  text-align: center;
  animation: cp-rise 260ms cubic-bezier(0.2, 0.8, 0.3, 1);
}
.cp-modal--sheet {
  align-items: stretch;
  text-align: left;
}
.cp-modal h2 {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  margin: 0;
}
.cp-modal__emoji {
  font-size: clamp(46px, 11vmin, 72px);
  line-height: 1;
}
.cp-star-row {
  display: flex;
  gap: 10px;
}
.cp-modal__stats {
  display: flex;
  gap: 18px;
  justify-content: center;
  flex-wrap: wrap;
}
.cp-stat {
  display: flex;
  flex-direction: column;
  font-family: var(--font-display);
}
.cp-stat__label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

.cp-setting {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
  font-size: var(--fs-md);
}

/* 44px minimum on BOTH axes — the switches were 68x38, which reads fine but
   is under the tap-target floor for the smallest fingers this game targets. */
.cp-switch {
  flex: 0 0 auto;
  width: 78px;
  height: 46px;
  min-height: 46px;
  border: 0;
  border-radius: 999px;
  background: #d8cfc2;
  padding: 5px;
  cursor: pointer;
  transition: background 160ms ease;
}
/* 78 wide - 10 padding = 68 inner; a 36px knob leaves exactly 32px of travel,
   which is the translateX below. Keep the three numbers in step if resized. */
.cp-switch::before {
  content: '';
  display: block;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-soft);
  transition: transform 160ms ease;
}
.cp-switch[aria-checked='true'] {
  background: var(--leaf);
}
.cp-switch[aria-checked='true']::before {
  transform: translateX(32px);
}
.cp-switch:focus-visible {
  outline: 3px solid #facc15;
  outline-offset: 2px;
}

.cp-error {
  padding: 2rem;
  text-align: center;
  font-size: var(--fs-md);
}

/* ---- accessibility modes ------------------------------------------------ */

/* Glyphs give every colour a second, non-colour signal. On by default at Hard,
   where 12 hues alone stop being reliably distinguishable. */
body.cb-glyphs .cp-cell.is-endpoint .cp-dot {
  color: rgba(255, 255, 255, 0.96);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

body.high-contrast .cp-cell {
  outline-color: rgba(46, 42, 58, 0.35);
}
body.high-contrast .cp-path {
  stroke-width: 0.56;
  opacity: 1;
}
body.high-contrast .cp-dot {
  box-shadow: 0 0 0 3px var(--ink);
}

/* ---- responsive --------------------------------------------------------- */

@media (max-width: 560px) {
  /* On a portrait phone the square board is usually width-bound, so every
     pixel of horizontal padding comes straight out of the cell size. Trim it
     on the play screen only. */
  .cp-page--play {
    padding-left: 8px;
    padding-right: 8px;
  }
  /* Keep the four controls on ONE row. Wrapping to two rows cost 60px of
     height, which came straight off the board on short screens. */
  .cp-controls {
    gap: 6px;
  }
  .cp-controls .cp-btn {
    flex: 1 1 0;
    min-width: 0;
    min-height: 48px;
    padding: 10px 4px;
    font-size: var(--fs-sm);
  }
}

/* Landscape phones: height is the scarce axis, so the board takes the full
   column height on the left and all the chrome stacks into a rail on the
   right. The board host MUST be align-self: stretch here — the board sizes
   itself from its parent's height, and a centred (non-stretched) grid item
   has no definite height, which collapsed the board to a few pixels. */
/*
 * Landscape phones are the tightest case: a 375px-tall window minus the shared
 * site header leaves ~310px for everything. The board therefore gets its own
 * shrinkable row (minmax(0, 1fr) — a plain 1fr will NOT shrink below its
 * content's min-content height, which is what pushed the controls off-screen)
 * and the buttons become a compact 2x2 rail instead of a 210px vertical stack.
 */
@media (orientation: landscape) and (max-height: 560px) {
  .cp-page--play {
    display: grid;
    /* 124px, not 104px: a 2-column rail inside 104px left each button at 42px
       wide, under the tap-target floor. The board is height-bound in landscape,
       so the extra 20px costs it nothing. */
    grid-template-columns: 1fr 124px;
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      'bar bar'
      'board controls'
      'progress controls';
    align-items: start;
    gap: 8px;
    min-height: 0;
    padding-top: 6px;
    padding-bottom: 6px;
  }
  .cp-topbar {
    grid-area: bar;
    flex-wrap: nowrap;
  }
  .cp-progress {
    grid-area: progress;
    align-self: end;
  }
  /* The meter itself still shows and carries the same information in its
     aria-label, so dropping the text line costs nothing but buys ~28px of
     board on the axis that has none to spare. */
  .cp-progress__label {
    display: none;
  }
  .cp-board-host {
    grid-area: board;
    align-self: stretch;
    min-height: 0;
  }
  .cp-controls {
    grid-area: controls;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 6px;
    align-self: center;
    width: 100%;
  }
  .cp-controls .cp-btn {
    width: 100%;
    min-height: 44px;
    padding: 6px 2px;
    font-size: 12px;
  }
}
