/*
 * style.css — Lucky Fortune Numbers
 * Spec 3.2 proportions, 3.3 fit the screen, 3.4 visual rules, 3.6 reduced motion, 11 touch.
 * Part 4 visual skin: self-hosted fonts, tile palette and candy look, found/✕ looks,
 * page and field panel, streak block. Part 5: pop, confetti, end-of-round dissolve (D20: no Next button).
 */

/* ---- Fonts (spec 3.5, D12): self-hosted, Open Font License (fonts/OFL-*.txt).
   Latin subset only, from npm @fontsource 5.3.0. ✕ and ✔ are not in these fonts
   and draw in the device's own font. No request leaves the game's own host. ---- */
@font-face {
  font-family: "Baloo 2";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/baloo-2-latin-600-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Baloo 2";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("fonts/baloo-2-latin-800-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Nunito";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/nunito-latin-400-normal.woff2") format("woff2");
}

/* ---- Constants ---- */
:root {
  /* Spec 3.2: Left / Middle / Right proportions. Set from config.js (FIELD_*) by ui.js;
     these are only the values used before the script runs. */
  --left: 25;
  --middle: 50;
  --right: 25;
  --fade-ms: 1000ms;     /* end-of-round dissolve (D20). Do not edit here: set from config.js DISSOLVE_MS */
  --confetti-ms: 1200ms; /* confetti flight. Do not edit here: set from config.js CONFETTI_MS */
  --ink: #2b2840;
  --muted: #8a86a0;
  --bg: #f7f5fb;         /* page (mockup) */
  --panel: #ffffff;      /* play field background: a rounded panel, no border (spec 3.4) */
  --gutter: 12px;        /* page side margin */
  --display: "Baloo 2", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --body: "Nunito", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Spec 3.4, 6.4: bright tile palette, decorative only (never right/wrong).
     ui.js gives each tile one of these at random, all four different.
     Colours from the mockup. Tile text is --tile-ink, not white: white reads
     at 1.6:1 to 3.0:1 on these colours, dark ink at 4.7:1 to 8.8:1. */
  --c1: #ff5fa2;   /* pink */
  --c2: #7ad83f;   /* green */
  --c3: #2fa8f0;   /* blue */
  --c4: #ffc21a;   /* yellow */
  --c5: #a77bff;   /* purple */
  --tile-ink: #2b2840;
  color-scheme: light;
  -webkit-text-size-adjust: 100%;  /* iPhone landscape must not enlarge text past the fit */
}

* { box-sizing: border-box; }

/* ---- Spec 3.3: whole page fits the viewport, never scrolls ---- */
html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
}
body {
  height: 100vh;            /* fallback */
  height: 100dvh;           /* tracks mobile browser bars */
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body);
  /* Safe-area padding keeps everything clear of the iPhone notch and home bar
     (viewport-fit=cover); side gutters at least --gutter. */
  padding: env(safe-area-inset-top, 0px) max(var(--gutter), env(safe-area-inset-right, 0px)) env(safe-area-inset-bottom, 0px) max(var(--gutter), env(safe-area-inset-left, 0px));
}

#top { flex: 0 0 auto; text-align: center; padding: 6px 8px 8px; }
#top h1 { margin: 0; font-family: var(--display); font-weight: 800; font-size: clamp(20px, 5vmin, 40px); line-height: 1.15; }
#top .note { margin: 2px 0 0; font-size: clamp(12px, 2.4vmin, 16px); color: var(--muted); }

/* ---- Play field (spec 3.2) ---- */
#field {
  flex: 1 1 auto;
  min-height: 0;            /* lets the field take only what remains */
  display: flex;
  flex-direction: row;
  background: var(--panel);
  border-radius: 18px;
  /* Spec 11: no double-tap zoom, no long-press selection or iPhone menu. */
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
#left   { flex: var(--left) 0 0; }
#middle { flex: var(--middle) 0 0; }
#right  { flex: var(--right) 0 0; }
#left, #middle, #right { min-width: 0; min-height: 0; }

/* Portrait: stack vertically, same proportions (Left top, Right bottom). */
@media (orientation: portrait) {
  #field { flex-direction: column; }
}

.side {
  display: flex;
  align-items: center;
  justify-content: center;
  container-type: size;     /* number scales with its own box */
}
.side .num {
  font-family: var(--display);
  font-weight: 800;
  font-size: min(60cqh, 45cqw);
  line-height: 1;
}

/* Middle: 2 x 2 invisible cells. */
#middle {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}
.cell { position: relative; min-width: 0; min-height: 0; }

/* ---- Tiles (ui.js sets size, position, rotation, font size) ---- */
.tile {
  position: absolute;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 14%/18%;
  /* Candy look (spec 6.4): glossy highlight top-left, darker lip at the bottom,
     soft drop shadow. --c is set per tile by ui.js. */
  background-color: var(--c, var(--c4));
  background-image: radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0) 48%);
  box-shadow: inset 0 -0.12em 0 rgba(0, 0, 0, 0.16), 0 0.12em 0.3em rgba(0, 0, 0, 0.18);
  text-shadow: 0 0.04em 0 rgba(255, 255, 255, 0.45);
  color: var(--tile-ink);
  font: inherit;
  font-family: var(--display);
  font-weight: 800;
  line-height: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4%;
  cursor: pointer;
  transition: transform 150ms ease;
  touch-action: manipulation;
}
#middle.placing .tile { transition: none; } /* new round: tiles placed at once (ui.js newRound) */
.tile .val { font-size: 1em; }
.tile .val, .tile .lbl { position: relative; z-index: 1; } /* text above the burst dots */
.tile .lbl { font-size: 0.5em; font-weight: 600; white-space: nowrap; }
.tile.found .val { font-size: 0.8em; }
/* Found (spec 7.1): smaller (scale set in ui.js), partly desaturated but keeps some
   colour so it never reads as a ✕, flatter lip and shadow, shows its formula. */
.tile.found {
  filter: saturate(0.45);
  opacity: 0.85;
  box-shadow: inset 0 -0.06em 0 rgba(0, 0, 0, 0.1), 0 0.05em 0.15em rgba(0, 0, 0, 0.1);
  cursor: default;
}
/* ---- Pop on a correct click (spec 7.1, 8; D7) ----
   Three sizes from data-level (set by ui.js; the level is never shown as a word).
   Animates the separate `scale` property, which multiplies with the rotate/scale
   `transform` that ui.js sets, so the two never fight. The found tile rests at
   transform scale 0.9; the peak here is 1.1, so the tile is never larger than its
   unfound size, which layout() already fits inside its cell: no tile leaves its
   cell or touches another at any moment of the pop.
   The pop also lends the tile its full colour for a moment before it settles to
   the found look. Big adds a burst of dots (the ::after) that spread to about the
   tile's own edge and fade; they are decoration and catch no taps. */
.tile.found[data-level="plain"]  { animation: pop-plain 260ms ease-out; }
.tile.found[data-level="medium"] { animation: pop-medium 420ms ease-out; }
.tile.found[data-level="big"]    { animation: pop-big 560ms ease-out; }
@keyframes pop-plain {
  0%   { scale: 1; }
  45%  { scale: 1.05; }
  100% { scale: 1; }
}
@keyframes pop-medium {
  0%   { scale: 1;    filter: saturate(1) brightness(1.08); opacity: 1; }
  35%  { scale: 1.1;  filter: saturate(1) brightness(1.12); opacity: 1; }
  65%  { scale: 0.96; }
  100% { scale: 1; }
}
@keyframes pop-big {
  0%   { scale: 1;    filter: saturate(1.15) brightness(1.15); opacity: 1; }
  25%  { scale: 1.1;  filter: saturate(1.15) brightness(1.2);  opacity: 1; }
  50%  { scale: 0.93; filter: saturate(1) brightness(1.08);    opacity: 1; }
  72%  { scale: 1.04; }
  100% { scale: 1; }
}
.tile::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0.24em;
  height: 0.24em;
  margin: -0.12em 0 0 -0.12em;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  /* Eight dots around the centre; ::after's transform spreads them out. */
  /* White sparkles: they show on every palette colour (dots in the tile's own colour vanish into it). */
  --spark: rgba(255, 255, 255, 0.95);
  box-shadow:
    1.2em 0 0 var(--spark), -1.2em 0 0 var(--spark), 0 0.9em 0 var(--spark), 0 -0.9em 0 var(--spark),
    0.85em 0.65em 0 var(--spark), -0.85em 0.65em 0 var(--spark), 0.85em -0.65em 0 var(--spark), -0.85em -0.65em 0 var(--spark);
}
.tile.found[data-level="big"]::after { animation: burst 560ms ease-out; }
@keyframes burst {
  0%   { opacity: 1; transform: scale(0.3); }
  60%  { opacity: 1; }
  100% { opacity: 0; transform: scale(1.15); }
}

/* ✕ (spec 7.1): flat, colourless (pure grey, no tint, no red), low contrast, no gloss. */
.tile.wrong {
  background: #e6e6e6;
  background-image: none;
  box-shadow: none;
  text-shadow: none;
  color: #b5b5b5;
  font-weight: 600;
  cursor: default;
}

/* End of round (spec 7.2, D20): the whole old screen dissolves, found tiles included.
   Fading the three areas, not the tiles, so a tile's own pop animation (which also
   sets opacity) cannot hold it on screen. The fade runs only on the way out; when
   ui.js removes .ending for the new round, the areas return at once, no fade-in.
   The field panel itself stays: it is the stage, not part of the old screen. */
#field.ending > #left,
#field.ending > #middle,
#field.ending > #right { opacity: 0; transition: opacity var(--fade-ms) ease; }

/* ---- Streak block (spec 9) ---- */
#streak {
  flex: 0 0 auto;
  display: grid;
  justify-content: center;
  text-align: center;
  font-size: clamp(14px, 3vmin, 20px);
  line-height: 1.3;
  padding: 6px 8px 4px;
  font-variant-numeric: tabular-nums;
}
/* Label left, number right (mockup); the number in the display font. */
#streak .row { display: flex; justify-content: space-between; gap: 0.8em; min-width: 11em; }
#streak .row b { font-family: var(--display); font-weight: 800; }
/* D23: the whole block is hidden when streaks can't be saved (ui.js sets [hidden]);
   needed because the display: grid above would otherwise override [hidden]. */
#streak[hidden] { display: none; }
/* Confetti (spec 9; D7 draft): ui.js adds .confetti with pieces when a record of 10+
   is passed. #streak clips them (overflow hidden), so they stay inside the block. */
#streak { position: relative; overflow: hidden; }
#streak .confetti { position: absolute; inset: 0; pointer-events: none; }
#streak .row { position: relative; z-index: 1; } /* text above the pieces */
#streak .confetti i {
  position: absolute;
  bottom: -0.4em;
  width: 0.45em;
  height: 0.28em;
  border-radius: 0.06em;
  background: var(--c);
  opacity: 0;
  animation: piece var(--confetti-ms) cubic-bezier(0.2, 0.7, 0.4, 1) both;
}
@keyframes piece {
  0%   { opacity: 1; transform: translate(0, 0) rotate(0deg); }
  45%  { opacity: 1; transform: translate(calc(var(--dx) * 0.6), var(--up)) rotate(calc(var(--spin) * 0.5)); }
  100% { opacity: 0; transform: translate(var(--dx), 0) rotate(var(--spin)); }
}
/* The ✔ arrives with a small bounce (inline-block so it can scale). */
#bestStreak { display: inline-block; }
#streak.cheer #bestStreak { animation: tick-in 500ms ease-out; }
@keyframes tick-in { 0% { scale: 0.4; opacity: 0; } 60% { scale: 1.3; opacity: 1; } 100% { scale: 1; } }

/* ---- Footer: placeholder links (spec 3.1, D6) ---- */
#foot {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
  gap: 1.2em;
  padding: 2px 8px 6px;
  font-size: clamp(12px, 2.2vmin, 15px);
}
#foot a { color: var(--muted); }

/* ---- Spec 3.3 short landscape: field and streak only ----
   ui.js sets body.short-landscape from config.js SHORT_LANDSCAPE_MAX_HEIGHT. */
body.short-landscape #top, body.short-landscape #foot { display: none; }
body.short-landscape #field { margin-top: 6px; }
/* Two lines side by side: one short row leaves more height for the field. */
body.short-landscape #streak { grid-auto-flow: column; column-gap: 2em; padding: 3px 8px; font-size: 14px; line-height: 1.2; }
body.short-landscape #streak .row { min-width: 0; }

/* ---- Spec 3.6 reduced motion: opacity only ---- */
@media (prefers-reduced-motion: reduce) {
  .tile { transition: none; }
  #streak.cheer #bestStreak { animation: tick-fade 400ms ease-out; }
  /* Pop becomes a short opacity dip, same for all three levels; no burst. */
  .tile.found[data-level] { animation: pop-fade 300ms ease-out; }
  .tile.found[data-level="big"]::after { animation: none; }
}

@keyframes tick-fade { 0% { opacity: 0; } 100% { opacity: 1; } }
@keyframes pop-fade { 0% { opacity: 0.4; } 100% { opacity: 0.85; } }

/* ---- DEBUG_OUTLINES (D16): faint outline on every box; off from part 4 ---- */
body.debug #field,
body.debug #left,
body.debug #middle,
body.debug #right,
body.debug .cell,
body.debug .tile {
  outline: 1px solid rgba(0, 0, 0, 0.18);
  outline-offset: -1px;
}
