/* ============================================================
   Nolan's Playground — an illustrated landscape you play in.
   A painted map, with each game as a station island on top of it.
   ============================================================ */

:root {
  --paper:    #FBF7EE;
  --ink:      #3D372C;
  --ink-soft: #5B5B4A;
  --butter:   #F6E9AE;

  --ar: 1.7769;                 /* games.js keeps this in step with the map */

  /* Type. Every option below is a system face on macOS and iOS, so nothing
     is downloaded and nothing can fail to load. Switch with
     <html data-font="chalk"> — see the table in the README. */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino,
                  "Book Antiqua", Georgia, serif;
  --font-body:    ui-rounded, "SF Pro Rounded", "Avenir Next", "Nunito",
                  system-ui, sans-serif;
}

/* ---------- type sets ------------------------------------------ */

html[data-font="chalk"] {
  --font-display: "Chalkboard SE", "Comic Sans MS", ui-rounded, system-ui, sans-serif;
  --font-body:    "Chalkboard SE", ui-rounded, system-ui, sans-serif;
}
html[data-font="marker"] {
  --font-display: "Marker Felt", "Comic Sans MS", ui-rounded, system-ui, sans-serif;
  --font-body:    ui-rounded, "Avenir Next", system-ui, sans-serif;
}
html[data-font="hand"] {
  --font-display: "Noteworthy", "Bradley Hand", ui-rounded, system-ui, sans-serif;
  --font-body:    ui-rounded, "Avenir Next", system-ui, sans-serif;
}
html[data-font="typewriter"] {
  --font-display: "American Typewriter", "Courier New", Courier, monospace;
  --font-body:    "American Typewriter", Georgia, serif;
}
html[data-font="ghibli"] {
  --font-display: "Optima", "Gill Sans", "Avenir Next", system-ui, sans-serif;
  --font-body:    "Avenir Next", system-ui, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  overflow: hidden;             /* one screen — the whole map is always visible */
  -webkit-text-size-adjust: 100%;
}

/* ---------- the world ------------------------------------------ */

.world {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100dvh;
  overflow: hidden;

  --stage-w: min(100vw, calc(100dvh * var(--ar)));
  --u: calc(var(--stage-w) / 100);   /* 1% of map width — scales type and spacing */
}

/* the map again, blown up and blurred, so the space around a wide map
   reads as painted air rather than empty margin */
.world__bleed {
  position: absolute; inset: 0; z-index: 0;
  background: none center / cover no-repeat;   /* image set by games.js */
  filter: blur(calc(var(--u) * 3)) saturate(.8) brightness(1.05);
  transform: scale(1.2);
}

.stage {
  position: relative; z-index: 1;
  width: var(--stage-w);
  aspect-ratio: var(--ar);
  box-shadow: 0 calc(var(--u) * .5) calc(var(--u) * 2.5) rgb(61 55 44 / .22);
}

/* the fence, in front of every island (they top out at z-index 10 on hover)
   but behind the name plates and buttons, which sit clear of it at 25 */
.stage__fence {
  position: absolute; inset: 0; z-index: 15;
  width: 100%; height: 100%;
  object-fit: cover;
  pointer-events: none;          /* taps fall through to the island behind */
  user-select: none; -webkit-user-drag: none;
}

.stage__scene {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  user-select: none; -webkit-user-drag: none;
}

/* ---------- a station ------------------------------------------ */

.station {
  position: absolute;
  left: calc(var(--x) * 1%);
  top:  calc(var(--y) * 1%);
  width: calc(var(--w) * 1%);
  translate: -50% -50%;
  display: block;
  text-decoration: none;
  color: var(--ink);
  -webkit-tap-highlight-color: transparent;

  /* The box is inert. Only the traced silhouette below takes taps, so the
     transparent corners of one island can't swallow taps meant for its
     neighbour. Events still bubble up to this <a>, so the link works. */
  pointer-events: none;
}

.station__hit {
  position: absolute; inset: 0;
  clip-path: var(--clip);
  pointer-events: auto;
  cursor: pointer;
}

.station__art {
  display: block;
  width: 100%; height: auto;
  user-select: none; -webkit-user-drag: none;
  transform-origin: 50% 78%;    /* grows from where it sits on the ground */
  transition: transform .22s cubic-bezier(.2,.9,.3,1.2);
}

/* whatever you're pointing at comes to the front of the map */
.station:hover, .station:focus-visible, .station:active { z-index: 10; }

@media (hover: hover) {
  .station:hover .station__art { transform: scale(1.05); }
}
.station:active .station__art { transform: scale(.98); transition-duration: .09s; }

.station:focus-visible { outline: none; }
.station:focus-visible .station__art {
  transform: scale(1.05);
  /* an outline would draw a box round a round island — a drop shadow follows
     the artwork's own alpha, so the focus ring is the island's real shape */
  filter: drop-shadow(0 0 calc(var(--u) * .5) var(--ink))
          drop-shadow(0 0 calc(var(--u) * .2) var(--ink));
}

/* ---------- the title sign ------------------------------------- */
/* The board is a self-contained SVG that boils from the inside; the words
   are plain DOM text sitting on top of it and never move (§20). */

.sign {
  position: absolute; z-index: 26;
  left: 50%; top: 12.5%;
  translate: -50% -50%;
  width: 36%;
  aspect-ratio: 3.6;              /* the board's own ratio, so it isn't stretched */
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
  pointer-events: none;
}
.sign__board { position: absolute; inset: 0; width: 100%; height: 100%; }
.sign h1 {
  position: relative; margin: 0;
  font-family: var(--font-display);
  font-size: calc(var(--u) * 3.05);
  line-height: 1.05; font-weight: 700;
}
.sign p {
  position: relative; margin: calc(var(--u) * .35) 0 0;
  font-size: calc(var(--u) * 1.4);
  color: var(--ink-soft);
}

/* ---------- name plate + play button ---------------------------- */
/* These sit in their own layer above every station, so an island can never
   bury its neighbour's button. Living Ink §20: the lettering is plain DOM
   text and is never put inside a boil filter — only the badge is. */

.marker {
  position: absolute; z-index: 25;
  left: calc(var(--x) * 1%);
  top:  calc(var(--y) * 1%);
  width: calc(var(--u) * 5.4);
  aspect-ratio: 1;
  translate: -50% -50%;
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
}

.marker__badge {
  display: block; width: 100%; height: auto;
  /* A badge sitting on the fence has to *cast* onto it, or it reads as inset
     rather than in front. Two shadows: a tight one for contact, a wide soft
     one for the lift. */
  filter: drop-shadow(0 calc(var(--u) * .3) calc(var(--u) * .45) rgb(61 55 44 / .42))
          drop-shadow(0 calc(var(--u) * .85) calc(var(--u) * 1.25) rgb(61 55 44 / .3));
  transition: transform .22s cubic-bezier(.2,.9,.3,1.2);
}
@media (hover: hover) {
  .marker:hover .marker__badge { transform: scale(1.12) rotate(7deg); }
}
.marker:active .marker__badge { transform: scale(.94); transition-duration: .09s; }

.marker__name {
  position: absolute;
  bottom: calc(100% + var(--u) * .7);
  left: 50%; translate: -50% 0;
  transform-origin: 50% 100%;
  padding: calc(var(--u) * .3) calc(var(--u) * 1);
  background: var(--paper);
  border: calc(var(--u) * .16) solid var(--ink);
  border-radius: calc(var(--u) * 1.2) calc(var(--u) * .8)
                 calc(var(--u) * 1.1) calc(var(--u) * .9);
  box-shadow: 0 calc(var(--u) * .22) 0 rgb(61 55 44 / .22),
              0 calc(var(--u) * .55) calc(var(--u) * .9) rgb(61 55 44 / .28);
  font-family: var(--font-display);
  font-size: calc(var(--u) * 1.55);
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  rotate: var(--tilt, 0deg);
  color: var(--ink);
  white-space: nowrap;
}
html[data-names="off"] .marker__name { display: none; }

/* while placing stations: <html data-regions="show"> */
html[data-regions="show"] .station__hit {
  background: rgb(209 85 63 / .34);
  outline: calc(var(--u) * .2) dashed rgb(209 85 63 / .8);
}

/* ---------- living ink ----------------------------------------- */
/* The stations' 4-frame redraw is baked into the image files themselves
   (`*-boil.webp`, an animated WebP) rather than applied as a CSS filter.

   feDisplacementMap warps the whole picture — flat panels and painted
   texture ripple along with the linework — and that reads as wobble, not
   boil, at any frequency or amplitude. Baking lets the displacement be
   masked to the drawing's edges, so the fills hold perfectly still and only
   the contours are redrawn. Re-bake with tools/bake-boil.py.

   The map underneath never moves: its title is painted into the image, and
   §20 keeps lettering stable. The SVGs in assets/svg/ still carry their own
   internal boil — they're line art, which takes displacement fine.

   <html data-boil="off">   (or "hold") swaps every station to its still.  */

@media (prefers-reduced-motion: reduce) {
  .station__art { transition: none; }
}

/* ---------- paper ---------------------------------------------- */

.grain {
  position: fixed; inset: 0; z-index: 5; pointer-events: none;
  background-image: url("../assets/tex/paper-grain.png");
  background-size: calc(var(--u) * 16) calc(var(--u) * 16);
  mix-blend-mode: multiply;
  opacity: .34;
}

/* ---------- portrait ------------------------------------------- */
/* Contained in a portrait phone the 16:9 map is a thin strip — the labels
   fall to about 6px and the buttons under the 44px touch minimum. Ask for
   landscape rather than shipping something unusable. Touch devices only:
   a tall desktop window just gets a smaller map. */

.rotate { display: none; }

@media (orientation: portrait) and (pointer: coarse) {
  .rotate {
    position: fixed; inset: 0; z-index: 40;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    gap: 3vmin;
    background: var(--paper);
    color: var(--ink);
    text-align: center;
    padding: 8vmin;
  }
  /* a whisper of tooth, not a field of noise */
  .rotate::before {
    content: ""; position: absolute; inset: 0;
    background: url("../assets/tex/paper-grain.png") 0 0 / 34vmin 34vmin;
    mix-blend-mode: multiply;
    opacity: .35;
    pointer-events: none;
  }
  .rotate > * { position: relative; }

  .rotate__glyph { width: 34vmin; max-width: 190px; color: var(--ink); }
  .rotate__msg {
    margin: 0;
    font-family: var(--font-display);
    font-size: clamp(20px, 6.2vmin, 34px);
    font-weight: 700;
  }
  .rotate__sub {
    margin: 0;
    font-size: clamp(13px, 3.6vmin, 18px);
    color: var(--ink-soft);
  }
}
