@tailwind base;
@tailwind components;
@tailwind utilities;

@font-face {
  font-family: 'Manrope Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url('@fontsource-variable/manrope/files/manrope-latin-wght-normal.woff2') format('woff2-variations');
}

/*
 * PIG's token layer, with the accent inlined as literals rather than written
 * onto the root at runtime. This site has no user-chosen accent, so a runtime
 * theme module would be a moving part that buys nothing here.
 *
 * Every colour is an HSL channel triple — `H S% L%` — so Tailwind can compose
 * it with an opacity modifier (`bg-brand/10`). Writing `hsl(...)` into the
 * variable breaks that silently: the class compiles and the opacity is ignored.
 */
:root {
  --bg: 240 5% 98%;
  --surface: 0 0% 100%;
  --surface-2: 240 5% 97%;
  --border: 240 6% 90%;
  --fg: 240 10% 4%;
  --muted: 240 4% 46%;

  --accent: 262 83% 55%;
  --accent-fg: 263 70% 46%;
  --accent-on: 0 0% 100%;
  --accent-subtle: 270 100% 97%;

  --positive: 160 84% 24%;
  --warning: 32 95% 31%;
  --danger: 0 72% 45%;
  --info: 201 90% 32%;
  --shadow: 240 10% 4%;

  /*
   * Board tiles. Foregrounds are stated explicitly and measured, because the
   * board is the one place on the site where colour carries meaning: an unlit
   * tile and a grey tile differ only by fill, and a letter that fails contrast
   * against its own tile makes the result unreadable exactly when it matters.
   *
   * The present tile does NOT reuse --warning. That token is 32 95% 31%, darkened
   * until white text clears 4.5:1 on it, and at that lightness it reads BROWN.
   * On a board where people arrive with a hard prior that this square is yellow,
   * a brown square reads as a bug in the scorer rather than as a design choice —
   * which on a page whose whole argument is "the grader is correct" is the
   * single worst thing it could look like. So the fill stays a real yellow and
   * the GLYPH goes dark instead, which is both more expected and higher contrast
   * than the version that was trying to be careful.
   *
   * Measured against each tile's own fill:
   *   exact   white     on 160 84% 24% ->  6.03:1
   *   present near-black on  45 93% 47% -> 10.02:1
   *   absent  white     on 240 4% 46%  ->  4.61:1
   */
  --tile-exact: 160 84% 24%;
  --tile-exact-fg: 0 0% 100%;
  --tile-present: 45 93% 47%;
  --tile-present-fg: 240 10% 4%;
  --tile-absent: 240 4% 46%;
  --tile-absent-fg: 0 0% 100%;

  --dur-1: 120ms;
  --dur-2: 180ms;
  --dur-3: 240ms;
  --ease-out: cubic-bezier(0.2, 0, 0, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);

  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);

  --app-header-h: calc(3.5rem + var(--safe-top));
}

@media (min-width: 1024px) {
  :root { --app-header-h: calc(4rem + var(--safe-top)); }
}

:root[data-theme='dark'] {
  --bg: 240 10% 4%;
  --surface: 240 8% 7%;
  --surface-2: 240 6% 11%;
  --border: 240 5% 18%;
  --fg: 0 0% 98%;
  --muted: 240 5% 60%;

  --accent: 258 90% 70%;
  --accent-fg: 255 92% 78%;
  --accent-on: 264 80% 12%;
  --accent-subtle: 260 35% 20%;

  --positive: 158 64% 48%;
  --warning: 38 92% 58%;
  --danger: 0 84% 65%;
  --info: 199 89% 58%;

  /*
   * Dark tiles lighten and take a dark glyph. Keeping the light theme's dark
   * fills would have made the board the only near-black object on a near-black
   * page, so a solved row read as an empty one.
   *   exact   240 10% 4% on 158 64% 48% -> 7.31:1
   *   present 240 10% 4% on  38 92% 58% -> 9.24:1
   *   absent  0 0% 98%   on 240 5% 28%  -> 8.94:1
   */
  --tile-exact: 158 64% 48%;
  --tile-exact-fg: 240 10% 4%;
  --tile-present: 38 92% 58%;
  --tile-present-fg: 240 10% 4%;
  --tile-absent: 240 5% 28%;
  --tile-absent-fg: 0 0% 98%;
}

/*
 * High contrast. Green/yellow/grey is a colour-only distinction, which is
 * exactly why the original game ships a high-contrast mode. Toggled from the
 * header and persisted per-viewer; the tiles also carry a glyph in this mode,
 * so the result survives being printed, screenshotted or seen by someone with
 * deuteranopia.
 */
:root[data-contrast='high'] {
  --tile-exact: 209 100% 40%;
  --tile-present: 27 100% 42%;
  --tile-absent: 240 4% 38%;
  --tile-exact-fg: 0 0% 100%;
  --tile-present-fg: 0 0% 100%;
  --tile-absent-fg: 0 0% 100%;
}

@layer base {
  * { @apply border-border; }
  body {
    @apply bg-bg text-fg font-sans antialiased;
    /* Painted explicitly: the viewer's chrome shows through a transparent body
       and the page would borrow the host's theme instead of its own. */
    text-rendering: optimizeLegibility;
  }
  /* Tabular figures everywhere a number is compared to another number. */
  .nums { font-variant-numeric: tabular-nums; }
  :focus-visible { @apply outline-none ring-2 ring-ring ring-offset-2 ring-offset-bg; }
}

@layer components {
  .card { @apply rounded-xl border border-border bg-surface; }
  /* 44px minimum touch target — the board keys and step chips are the two
     places this is load-bearing on a phone. */
  .tap { @apply min-h-11 min-w-11; }
}

/*
 * Reduced motion clamps everything to effectively zero. That is correct, and
 * it is also why every state change in this app must ALSO be announced: with
 * the flip gone, colour alone carries the result, and colour alone is not a
 * result for a screen-reader user.
 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/*
 * Board motion. Declared here rather than in the demo because Tailwind's
 * arbitrary `animate-[...]` needs the keyframes to exist in the stylesheet, and
 * a demo-local <style> block would be a second place for the token values to
 * drift from.
 */
@keyframes tile-flip {
  0% { transform: rotateX(0deg); }
  50% { transform: rotateX(90deg); }
  100% { transform: rotateX(0deg); }
}
@keyframes tile-pop {
  0% { transform: scale(1); }
  60% { transform: scale(1.06); }
  100% { transform: scale(1); }
}
@keyframes tile-shake {
  0%, 100% { transform: translateX(0); }
  15%, 45%, 75% { transform: translateX(-5px); }
  30%, 60%, 90% { transform: translateX(5px); }
}
