/* Lanternsong
   ----------------------------------------------------------------------------
   The brief, in Kevin's words: camping was frightening as a kid, but when the
   lantern was on the picnic table next to you it was okay. Just the warm glow
   of it being safety.

   The first build of this site took that literally: a near black page with a
   gold wash. The trouble is that warm-on-black is also what every generated
   landing page looks like, and the glow was so faint it read as a vignette.

   So the light is on the other side of it now. The page is the lit thing, and
   the four games are the dark, luminous objects sitting on it. The apps' own
   icons are the only imagery, because they are the best thing the studio has
   and they were nowhere on the old site. */

:root {
  /* Paper, and ink that is brown-black rather than black. */
  --paper:  oklch(0.945 0.013 85);
  --ink:    oklch(0.26 0.015 70);
  --soft:   oklch(0.46 0.014 70);
  --faint:  oklch(0.58 0.012 70);

  /* One accent, and it is cool on purpose: everything warm on this page is
     already being done by the paper. */
  --accent: oklch(0.47 0.15 262);

  --edge:   oklch(0.26 0.015 70 / 0.16);

  /* Each game keeps its own color, pulled down into ink range so it holds
     against paper. These belong to the games, not to the site. */
  --nil:       oklch(0.50 0.15 158);
  --carousel:  oklch(0.54 0.16 48);
  --slackwire: oklch(0.52 0.14 68);
  --hemiola:   oklch(0.46 0.18 300);

  --display: 'Bricolage Grotesque', ui-sans-serif, system-ui, sans-serif;
  --sans: 'Archivo', ui-sans-serif, system-ui, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --gap: 1.5rem;
  --slow: 520ms;
  --ease: cubic-bezier(0.22, 0.61, 0.24, 1);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 16px/1.7 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---- the two atmosphere layers ---- */

/* The lantern, still here, now falling on paper instead of into a room. */
.lamp {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(48rem 48rem at 20% 24%, oklch(0.99 0.02 85 / 0.85), transparent 66%);
}

/* Paper has tooth. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.22;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

/* ---- the shell ---- */

.page {
  position: relative;
  z-index: 1;
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 1.5rem 4.5rem;
}

header.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 2.5rem 0 0;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--display);
  font-weight: 800;
  font-size: 17px;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  border: none;
}

.wordmark .flame {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px oklch(0.47 0.15 262 / 0.18);
}

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

h1 {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.9rem, 9.2vw, 7rem);
  line-height: 0.93;
  letter-spacing: -0.035em;
  max-width: 15ch;
  text-wrap: balance;
}

.lit { font-style: normal; color: var(--accent); }

.lede {
  max-width: 46ch;
  margin: clamp(1.5rem, 3vw, 2.25rem) 0 0;
  font-size: clamp(1rem, 1.5vw, 1.18rem);
  color: var(--soft);
  text-wrap: pretty;
}

p { margin: 0 0 1.3rem; max-width: 60ch; text-wrap: pretty; }

a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--edge);
  transition: border-color var(--slow) var(--ease), color var(--slow) var(--ease);
}
a:hover { border-bottom-color: var(--accent); color: var(--accent); }

/* Anything that is data rather than voice is set in the mono, in plain
   sentence case. Uppercase and tracking on a small label is the house style of
   generated "tasteful" design, and Kevin called it in September: it reads as
   fashionable rather than as his. The hairline rule under the hero went for
   the same reason. */
.meta {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--faint);
}

/* ---- the hero ---- */

.hero { padding: clamp(3.5rem, 11vw, 8rem) 0 clamp(2.5rem, 7vw, 5rem); }

/* The lede and the board share a line. The board is pushed down so it sits
   against the prose rather than lining up with the headline: the asymmetry is
   what stops it reading as a stock hero. */
.heroRow { display: grid; gap: 2.5rem; }
.play { width: 100%; max-width: 300px; }

@media (min-width: 900px) {
  .heroRow {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
    gap: 4rem;
    align-items: start;
  }
  .play { width: 280px; margin-top: 0.5rem; justify-self: end; }
}

/* ---- a playable Nil, the fifth object on the table ---- */

.board {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  /* Matched by PAD in board.js: an absolutely positioned child resolves
     against the padding box, so the inset is applied in the transform too. */
  padding: 8px;
  border-radius: 18px;
  background: linear-gradient(180deg, oklch(0.21 0.012 280), oklch(0.15 0.010 280));
  box-shadow:
    0 22px 44px oklch(0.30 0.02 70 / 0.26),
    0 2px 6px oklch(0.30 0.02 70 / 0.16);
  cursor: grab;
  touch-action: none;
  outline: none;
  transition: box-shadow var(--slow) var(--ease);
}
.board:focus-visible { box-shadow: 0 0 0 3px oklch(0.47 0.15 262 / 0.35); }

/* Emptying it is the one thing in the whole page that is allowed to flash. */
.board.emptied { box-shadow: 0 0 0 2px oklch(0.47 0.15 262 / 0.5), 0 22px 54px oklch(0.47 0.15 262 / 0.3); }

.tile {
  position: absolute;
  top: 0;
  left: 0;
  border-radius: 12px;
  box-shadow: inset 0 1px 0 oklch(1 0 0 / 0.22), 0 2px 6px oklch(0 0 0 / 0.35);
  transition: transform 190ms var(--ease), opacity 380ms var(--ease);
}
.tile.new { opacity: 0; }
.tile.gone { opacity: 0; transform-origin: center; }

.playrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 0.75rem;
}

.hint { font-family: var(--mono); font-size: 0.72rem; color: var(--faint); }

.sound {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--soft);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color var(--slow) var(--ease);
}
.sound:hover { color: var(--accent); }

/* ---- the shelf: four lit objects on a table ---- */

.shelf {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: clamp(2.1rem, 5vw, 3.6rem) clamp(2rem, 5vw, 4.5rem);
  padding: clamp(3.5rem, 8vw, 6rem) 0 clamp(5.5rem, 13vw, 11rem);
}

.game {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: clamp(1.1rem, 2.4vw, 1.75rem);
  padding: 4px;
  border: none;
  isolation: isolate;
}

/* The second column rides low, so the shelf is a composition rather than a
   table of contents. */
@media (min-width: 760px) {
  .game:nth-child(2), .game:nth-child(4) { margin-top: 3.25rem; }
}

/* The room warms toward whichever game you are near. */
.game::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: -14%;
  top: -36%;
  width: 72%;
  padding-bottom: 72%;
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--hue), transparent 72%);
  opacity: 0.10;
  transition: opacity var(--slow) var(--ease);
}
.game:hover::before { opacity: 0.24; }

.shot {
  flex: 0 0 auto;
  width: clamp(96px, 13vw, 148px);
  aspect-ratio: 1;
  border-radius: 23%;
  overflow: hidden;
  box-shadow:
    0 18px 38px oklch(0.30 0.02 70 / 0.24),
    0 2px 5px oklch(0.30 0.02 70 / 0.14);
  transform: rotate(-2deg);
  transition: transform var(--slow) var(--ease);
}
.game:nth-child(even) .shot { transform: rotate(2deg); }
.game:hover .shot { transform: rotate(0deg) translateY(-7px) scale(1.035); }
.shot img { display: block; width: 100%; height: 100%; object-fit: cover; }

.gname {
  margin: 2px 0 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.6rem, 2.8vw, 2.3rem);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--hue);
}

.gline {
  margin: 0.75rem 0 0;
  max-width: 36ch;
  font-size: 0.95rem;
  color: var(--soft);
  text-wrap: pretty;
}

.gmeta { display: block; margin-top: 0.875rem; }

/* ---- the written pages ---- */

.doc { margin-top: 3.5rem; }
.doc h1 { font-size: clamp(2.2rem, 5.5vw, 3.6rem); max-width: 18ch; margin-bottom: 1.5rem; }

/* One section: its name hangs in the first column, its prose fills the rest.
   The way a field manual sets a section. */
.section {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap);
  margin-top: 3rem;
}

.section h2 {
  grid-column: 1 / 2;
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0.3rem 0 0;
  line-height: 1.6;
}

.section .body { grid-column: 2 / -1; }
.section .body > :last-child { margin-bottom: 0; }

@media (max-width: 760px) {
  .section { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; margin-top: 2.5rem; }
  .section h2, .section .body { grid-column: 1 / -1; }
}

.doc p, .doc ul { max-width: 62ch; color: var(--soft); font-size: 0.95rem; }
/* On a written page the lede is the first paragraph, not a standalone
   block, so it needs the space under it that every other paragraph has. */
.doc .lede { color: var(--ink); font-size: 1.1rem; max-width: 44ch; margin-bottom: 1.6rem; }
.doc p strong { color: var(--ink); font-weight: 500; }
.doc ul { padding-left: 1.1rem; }
.doc li { margin-bottom: 0.6rem; color: var(--soft); font-size: 0.95rem; }
.doc li strong { color: var(--ink); font-weight: 500; }

.updated {
  font-family: var(--mono);
  color: var(--faint);
  font-size: 0.68rem;
  margin-top: 1.5rem;
  max-width: 56ch;
  line-height: 1.6;
}

/* ---- footer ---- */

footer {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.4rem;
  margin-top: 4rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--edge);
}
footer .spacer { flex: 1 1 auto; }
footer a { border-bottom: none; }

/* ---- one load, staggered ---- */

.rise { animation: rise 900ms var(--ease) both; }
@keyframes rise {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 560px) {
  .page { padding: 0 1.25rem 3.5rem; }
  header.top { padding-top: 2rem; }
}

@media (prefers-reduced-motion: reduce) {
  .rise { animation: none; }
  .game, .game::before, .shot, .tile, a { transition-duration: 1ms !important; }
}
