/* =====================================================
   star-map.css — 🌌 Star Map Skip-Count (Abby)

   Loaded after css/style.css and css/kid-games.css, so anything here that
   names a shared class does so only in combination with a kg-star-map- one
   and only to re-skin it for a night sky. Every class this file introduces
   is prefixed kg-star-map-, and so is every @keyframes name — six per-game
   sheets share one document and none of them may reach into another.

   prefers-reduced-motion is handled globally at style.css:559-562 and is
   deliberately not repeated here. The one thing that rule cannot see is
   motion the game drives itself, so js/kidgames/games/star-map.js reads
   ctx.reducedMotion once and puts .kg-star-map-still on the field.

   THE SIZING THAT MATTERS (brief §4, Abby: >= 57px targets, >= 30px gutters)
   The sky is one coordinate space, 100 x 125, rendered at a matching aspect
   ratio, and star-map-rules.js guarantees no two star centres are closer than
   34 of those units. So the gap between two neighbouring tap targets is
     0.34 * (field width) - (button size)
   The narrowest the field ever gets is 296px — below 560px viewport width it
   spills out over the card's own padding to claim it — which puts the closest
   two centres 100px apart and leaves 38px of clear air around a 62px button.
   The visible star is 22px. The button around it is not.
   ===================================================== */

.kg-star-map-round {
  font-weight: 800;
  color: var(--ink-soft);
  font-size: 1rem;
  margin-top: -6px;
}

.kg-star-map-tokens { display: flex; gap: 8px; justify-content: center; }
.kg-star-map-token {
  font-size: 1.25rem; line-height: 1;
  opacity: .2; filter: grayscale(1);
  transition: opacity .3s var(--ease), filter .3s var(--ease);
}
.kg-star-map-token.kg-star-map-lit { opacity: 1; filter: none; }

/* A count that only ever climbs, kept across visits. Never a score, never
   something a wrong tap can take away. */
.kg-star-map-lifetime {
  font-size: .85rem; font-weight: 700; color: var(--ink-soft); opacity: .8;
  margin-top: -8px;
}

/* ---------------- The step, three ways ----------------
   Numeral, words and that many pips, so "counting by six" arrives whether
   she reads it, hears it or counts the dots. A muted iPad loses nothing. */
.kg-star-map-banner {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  padding: 10px 22px; border-radius: 999px;
  background: var(--kg-tint, #f4efff);
  border: 3px solid var(--accent-2);
  max-width: 100%;
}
.kg-star-map-stepnum {
  font-size: 2.1rem; font-weight: 800; line-height: 1; color: var(--accent);
  min-width: 1.2em; text-align: center;
}
.kg-star-map-stepbody { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.kg-star-map-steptext { font-size: 1.05rem; font-weight: 800; color: var(--ink); }
.kg-star-map-pips { display: flex; flex-wrap: wrap; gap: 5px; max-width: 160px; }
.kg-star-map-pip {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 2px #fff;
}

/* ---------------- The sequence so far ----------------
   Always on screen. She can see the pattern she is extending, so the next
   number is arithmetic rather than a guess. */
.kg-star-map-strip {
  display: flex; flex-wrap: wrap; gap: 10px;
  align-items: center; justify-content: center;
  min-height: 52px;
}
.kg-star-map-chip {
  min-width: 46px; padding: 7px 14px;
  border-radius: 999px; background: #fff;
  border: 3px solid var(--accent-2);
  font-size: 1.1rem; font-weight: 800; color: var(--ink);
  box-shadow: var(--shadow);
}
.kg-star-map-chip-next {
  border-style: dashed; border-color: var(--accent);
  color: var(--accent); background: var(--kg-tint, #f4efff);
}

.kg-star-map-msg { font-weight: 700; max-width: 34em; }

/* ---------------- The sky ----------------
   Fixed aspect ratio, because that is what makes one separation number in
   the rules module mean a fixed number of CSS pixels on the glass. */
.kg-star-map-field {
  --kg-star-map-hit: 62px;
  --kg-star-map-ink: #ffeec2;
  position: relative;
  width: min(100%, 380px);
  aspect-ratio: 100 / 125;
  border-radius: var(--radius);
  background:
    radial-gradient(130% 95% at 50% 0%, #4a3b7c 0%, #2b2a5a 46%, #191640 100%);
  box-shadow: var(--shadow-lg), inset 0 0 0 3px rgba(255, 255, 255, .12);
  margin: 2px auto;
  outline-offset: 6px;
}

/* Below 560px the sky takes back the card's horizontal padding rather than
   shrinking: at a 320px viewport that is the difference between 88px and
   100px between star centres, and 88px does not leave 30px of gutter. */
@media (max-width: 560px) {
  .kg-star-map-field {
    --kg-star-map-bleed: clamp(18px, 5vw, 28px);
    width: min(calc(100% + var(--kg-star-map-bleed) * 2), 380px);
    margin-left: calc(-1 * var(--kg-star-map-bleed));
    margin-right: calc(-1 * var(--kg-star-map-bleed));
  }
}

@media (max-width: 360px) {
  .kg-star-map-field { --kg-star-map-hit: 58px; }
  .kg-star-map-chip { min-width: 40px; padding: 6px 11px; font-size: 1rem; }
  .kg-star-map-stepnum { font-size: 1.8rem; }
}

.kg-star-map-field.kg-star-map-complete {
  box-shadow: var(--shadow-lg), inset 0 0 0 3px var(--accent-2), 0 0 26px rgba(179, 136, 255, .5);
}

.kg-star-map-sky {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* Clips the dust to the rounded corners without clipping a focus ring on
     an edge star, which is what overflow:hidden on the field would do. */
  clip-path: inset(0 round var(--radius));
  pointer-events: none;
}

.kg-star-map-taps { position: absolute; inset: 0; }

/* Background dust: decoration only, never a target, never announced. */
.kg-star-map-dust circle {
  fill: #dfe4ff;
  animation: kg-star-map-twinkle 4.2s ease-in-out infinite;
}
.kg-star-map-still .kg-star-map-dust circle { animation: none; }
@keyframes kg-star-map-twinkle {
  0%, 100% { opacity: .25; }
  50% { opacity: .9; }
}

/* ---------------- The growing line ----------------
   Two strokes: a soft wide one for the glow, a crisp one on top. Each new
   segment is dashed to its own length and the offset transitioned to zero,
   so it draws from the old star to the new one. style.css:559 flattens that
   transition for a child who asks for reduced motion, and the game skips the
   dash entirely in that case, so the segment simply appears. */
.kg-star-map-line {
  stroke-linecap: round;
  fill: none;
  transition: stroke-dashoffset .55s var(--ease);
}
.kg-star-map-line-glow { stroke: var(--accent-2); stroke-width: 2.8; opacity: .45; }
.kg-star-map-line-core { stroke: var(--kg-star-map-ink, #ffeec2); stroke-width: .9; opacity: .95; }

/* ---------------- A star ----------------
   Re-skins the shared .kg-target for a night sky. .kg-target keeps doing the
   work that matters — the minimum size, the :active touchdown, .kg-dim and
   the one reserved .kg-hint glow — and loses only the white card look. */
.kg-star-map-star {
  position: absolute;
  width: var(--kg-star-map-hit);
  height: var(--kg-star-map-hit);
  /* Centred on its coordinate by margin, NOT by transform: .kg-target:active
     owns transform for the touchdown squeeze and would throw the star across
     the sky the moment a finger landed on it. */
  margin-left: calc(var(--kg-star-map-hit) / -2);
  margin-top: calc(var(--kg-star-map-hit) / -2);
  padding: 0;
  gap: 3px;
  background: transparent;
  border: none;
  border-radius: 50%;
  box-shadow: none;
  color: #fff;
  transition: opacity .25s var(--ease), transform .15s var(--ease);
}

/* The art: a 22px sparkle inside a 62px button. Small and pretty, big and
   forgiving — the two are not the same rectangle. */
.kg-star-map-mark {
  width: 22px; height: 22px;
  background: var(--kg-star-map-ink, #ffeec2);
  clip-path: polygon(50% 0%, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0% 50%, 39% 39%);
  filter: drop-shadow(0 0 5px rgba(255, 238, 194, .85));
  transition: transform .2s var(--ease), background .2s var(--ease), filter .2s var(--ease);
}
.kg-star-map-num {
  font-size: 1.05rem; font-weight: 800; color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8), 0 0 10px rgba(0, 0, 0, .6);
  line-height: 1;
}

/* Far from the line she is drawing: still there, still legible, quietly out
   of the way, so at most four candidates read as live at any moment. */
.kg-star-map-star.kg-star-map-far { opacity: .38; }
.kg-star-map-star.kg-star-map-far .kg-star-map-mark { transform: scale(.72); filter: none; }
.kg-star-map-star.kg-star-map-far .kg-star-map-num { font-size: .92rem; }

/* Joined. Gold, glowing, and no longer a target. */
.kg-star-map-star.kg-star-map-on { opacity: 1; }
.kg-star-map-star.kg-star-map-on .kg-star-map-mark {
  width: 30px; height: 30px;
  background: #fff;
  filter: drop-shadow(0 0 10px var(--kg-star-map-ink, #ffeec2)) drop-shadow(0 0 18px rgba(255, 210, 100, .7));
}
.kg-star-map-star.kg-star-map-on .kg-star-map-num { color: var(--kg-star-map-ink, #ffeec2); }

/* The shared .kg-correct paints a pale card, which would be a white blob on
   a night sky. Keep the meaning, drop the paper. */
.kg-star-map-star.kg-correct { background: transparent; }

/* A wrong tap is amber and obvious, and stays obvious with every animation
   in the document switched off. Nothing is lost, nothing is scored. */
.kg-star-map-star.kg-wrong { opacity: 1; }
.kg-star-map-star.kg-wrong .kg-star-map-mark {
  background: var(--amber);
  transform: scale(1.1);
  filter: drop-shadow(0 0 8px rgba(255, 159, 28, .9));
}

/* .kg-dim from the scaffold's second miss, on a dark field. */
.kg-star-map-star.kg-dim { opacity: .16; }

/* The reserved highlight, shared by the hint and the reveal. Circular here
   because the button is. */
.kg-star-map-star.kg-hint { opacity: 1; }
.kg-star-map-star.kg-hint .kg-star-map-mark {
  transform: scale(1.25);
  filter: drop-shadow(0 0 12px var(--accent-2)) drop-shadow(0 0 22px var(--accent-2));
}

/* Purple on midnight blue is a weak ring. Same 4px treatment, warmer colour,
   so the keyboard focus is unmistakable against the sky. */
.kg-star-map-star:focus-visible {
  outline: 4px solid #ffe27a;
  outline-offset: 3px;
  border-radius: 50%;
}

@media (hover: hover) {
  /* Decoration only. Everything it says is also said by :focus-visible and
     by the brightness states, and none of it is needed to understand the
     board (brief §4). */
  .kg-star-map-star:hover .kg-star-map-mark { transform: scale(1.18); }
  .kg-star-map-star.kg-star-map-far:hover { opacity: .7; }
}

/* ---------------- Completion ---------------- */
.kg-star-map-sparks { position: absolute; inset: 0; pointer-events: none; }
.kg-star-map-spark {
  position: absolute;
  width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, var(--kg-star-map-ink, #ffeec2) 45%, rgba(255, 238, 194, 0) 72%);
  animation: kg-star-map-spark 1s var(--ease) forwards;
}
@keyframes kg-star-map-spark {
  from { transform: scale(.3); opacity: 1; }
  to { transform: scale(3.2); opacity: 0; }
}

.kg-star-map-name {
  font-size: 1.2rem; font-weight: 800; color: var(--accent);
  letter-spacing: .01em;
}

.kg-star-map-pulse { animation: kg-star-map-pulse .7s var(--ease); }
@keyframes kg-star-map-pulse {
  0%, 100% { transform: scale(1); }
  40% { transform: scale(1.05); }
}

/* ---------------- Wider screens ---------------- */
@media (min-width: 700px) {
  .kg-star-map-msg { font-size: 1.2rem; }
  .kg-star-map-name { font-size: 1.35rem; }
}
