/* ============================================================
   Locality of behavior: everything Tailwind utilities can say
   lives inline in Main.hs (arbitrary properties, starting:,
   after:). This file is ONLY what utilities can't express:
   @property/@function registrations, pseudo-elements,
   cross-element selectors, and if()/typed-attr() declarations.
   Chrome-first by design; support notes per block.
   ============================================================ */

/* -- @property: typed, ANIMATABLE custom properties (Baseline 2024).
      Registration is global — the odometer/pie utilities inline reference these. */
@property --num { syntax: "<integer>"; initial-value: 0; inherits: false; }
@property --t { syntax: "<number>"; initial-value: 100; inherits: false; }

/* -- @function (Chrome 139+): real CSS functions, typed params + defaults.
      --shade derives hover colors; --share turns raw counts into a %. */
@function --shade(--c <color>, --by <number>: 0.12) returns <color> {
  result: oklch(from var(--c) calc(l - var(--by)) c h);
}
@function --share(--n <number>, --total <number>) returns <percentage> {
  result: calc(var(--n) / max(var(--total), 1) * 100%);
}

:root {
  /* the actual Kahoot answer palette + stage purple */
  --k0: #e21b3c; --k1: #1368ce; --k2: #d89e00; --k3: #26890c;
  background: radial-gradient(circle at 50% 35%, #57209e 0%, #46178f 55%, #38126f 100%);
  font-family: "Montserrat", system-ui, sans-serif;
  interpolate-size: allow-keywords; /* lets height animate to `auto` (details below) */
}

/* -- sibling-index() (Chrome 138): per-tile color, glyph, and stagger keyed
      off DOM position — inexpressible per-element, so it lives here. */
.answers > .tile {
  --c: var(--k0);
  --glyph: "▲";
  animation: pop 0.45s backwards calc((sibling-index() - 1) * 90ms);
}
.answers > .tile:nth-child(2) { --c: var(--k1); --glyph: "◆"; }
.answers > .tile:nth-child(3) { --c: var(--k2); --glyph: "●"; }
.answers > .tile:nth-child(4) { --c: var(--k3); --glyph: "■"; }
@keyframes pop { from { opacity: 0; scale: 0.85; } }
.tile::before {
  content: var(--glyph, "▲");
  font-size: 2.2rem;
  line-height: 1;
  text-shadow: 0 2px 4px oklch(0% 0 0 / 0.3);
}

/* -- if() (Chrome 137+): server sets --state; declarations pick the style.
      Tiles KEEP their identity color on reveal: wrong answers dim to a dark
      version of their own hue; the correct one keeps full color, a ring,
      and a ✓ (yes, if() inside content). */
.tile {
  background: if(
    style(--state: wrong): oklch(from var(--c) 32% calc(c * 0.25) h);
    else: var(--c)
  );
  outline: if(style(--state: correct): 5px solid white; else: 0 solid transparent);
  transition: scale 0.15s, filter 0.3s, background 0.3s;
}
.tile:hover { background: --shade(var(--c)); scale: 1.02; }
.tile::after {
  content: if(style(--state: correct): "✓"; else: "");
  position: absolute;
  inset: 0.5rem 0.9rem auto auto;
  font-size: 1.6rem;
  font-weight: 900;
}

/* -- :has(): the selection interaction — ring the pick, dim the rest.
      Sibling/parent reach is beyond any utility. */
.answers:has(input:checked) .tile:not(:has(input:checked)) { filter: grayscale(0.8) opacity(0.4); scale: 0.97; }
.tile:has(input:checked) { outline: 4px solid white; }
/* :has() quantity query: 5+ options -> 3 columns */
.answers:has(> .tile:nth-child(5)) { grid-template-columns: repeat(3, 1fr); }

/* -- container style queries (Baseline 2026): the grid publishes --phase
      (container-name is inline); descendants restyle per game phase */
@container quiz style(--phase: reveal) {
  .tile { animation: settle 0.5s; }
  .tile:has(input:checked) { outline-color: oklch(90% 0.2 95); }
}
@keyframes settle { 50% { scale: 1.04; } }

/* -- typed attr() (Chrome 133+): server data-n/data-total flow into CSS math
      via --share() */
.count-bar { width: max(2.5rem, --share(attr(data-n type(<number>), 0), attr(data-total type(<number>), 1))); }

/* -- the Kahoot timebar fill: duration comes straight from server-rendered
      data-secs via typed attr(). Track styling is inline; ::before isn't. */
.timebar::before {
  content: "";
  display: block;
  height: 100%;
  border-radius: 999px;
  background: #864cbf;
  animation: shrink calc(attr(data-secs type(<number>), 20) * 1s) linear forwards;
}
@keyframes shrink { from { width: 100%; } to { width: 0%; } }

/* -- conic pie timer: @keyframes animating the registered --t, painted by
      conic-gradient(). No canvas, no rAF. (Sizing is inline.) */
.pie {
  background: conic-gradient(var(--k2) calc(var(--t) * 1%), oklch(100% 0 0 / 0.15) 0);
  animation: drain calc(attr(data-secs type(<number>), 20) * 1s) linear forwards;
}
@keyframes drain { to { --t: 0; } }

/* -- View transitions: htmx wraps SSE swaps in startViewTransition().
      Named rows FLIP to new positions; the stage (named inline) slides. */
::view-transition-group(*) { animation-duration: 0.5s; }
::view-transition-old(stage) { animation: 0.3s ease-in both leave; }
::view-transition-new(stage) { animation: 0.3s 0.1s ease-out both enter; }
@keyframes leave { to { opacity: 0; translate: -3rem 0; } }
@keyframes enter { from { opacity: 0; translate: 3rem 0; } }
@keyframes rise { from { opacity: 0; translate: 0 1.5rem; } }
@keyframes grow { from { scale: 0 1; } to { scale: 1 1; } }

/* -- CSS carousel (Chrome 135): browser-GENERATED prev/next buttons and dot
      markers — pure pseudo-elements, the flex/snap/timeline setup is inline.
      Buttons pin to the carousel's edges via anchor positioning. */
.recap::scroll-button(left), .recap::scroll-button(right) {
  position: fixed;
  position-anchor: --recap;
  width: 2.5rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background: oklch(100% 0 0 / 0.15);
  color: white;
  font-size: 1.3rem;
  font-weight: 900;
  cursor: pointer;
}
.recap::scroll-button(left) { content: "←"; position-area: inline-start center; }
.recap::scroll-button(right) { content: "→"; position-area: inline-end center; }
.recap::scroll-button(*):disabled { opacity: 0.3; }
.recap::scroll-marker-group { display: flex; gap: 0.5rem; justify-content: center; padding: 0.75rem; }
.recap > *::scroll-marker {
  content: "";
  width: 0.7rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background: oklch(100% 0 0 / 0.3);
}
.recap > *::scroll-marker:target-current { background: white; }

/* -- ::details-content + interpolate-size: accordion animating to height auto */
.review::details-content {
  height: 0;
  overflow: clip;
  transition: height 0.4s, content-visibility 0.4s allow-discrete;
}
.review[open]::details-content { height: auto; }

/* -- text-box (Chrome 133): optically center text in buttons/tiles */
.tile span, button { text-box: trim-both cap alphabetic; }

/* Markdown slides: generated HTML can't carry utility classes */
.slide { font-size: 1.5rem; line-height: 1.6; display: grid; gap: 1rem; align-content: center; }
.slide h1 { font-size: 3.5rem; font-weight: 900; text-wrap: balance; }
.slide h2, .slide h3 { font-size: 2.2rem; font-weight: 800; }
.slide li { list-style: disc inside; }
.slide code { background: oklch(100% 0 0 / 0.15); padding: 0.1em 0.35em; border-radius: 0.35em; }
