/*
 * The "paper party" look, shared by every page this server renders.
 *
 * Risograph: a bone stock, near-black ink, and one fluorescent signature. The
 * values are mirrored in Utilities/PaperTheme.swift, which has no build step to
 * generate it from here, so the two change in one commit or not at all.
 *
 * Tokens are named for their ROLE, not their hue. The palette has already moved
 * once (coral to yellow); a token called --coral holding a yellow is how a
 * stylesheet becomes a liar.
 *
 * The one rule worth knowing: --accent and --accent-deep are FILLS. They
 * measure 1.39:1 and 1.79:1 as text on the canvas, which is unreadable.
 * Anything that has to be read on paper uses --ink, or --amber when it wants to
 * be warm and --danger when something has gone wrong; anything sitting ON
 * --accent uses --on-accent.
 *
 * Every colour here that is ever used as text clears 7:1 on all three paper
 * colours, which is the strict contrast bar rather than the ordinary one.
 * --slate, --amber, --teal and --blue were all darkened to reach it: --teal was
 * below even the ordinary bar, at 3.79 as text on the canvas, and --accent-deep
 * was the colour of every error message at 1.79.
 * `functions/test/contrast.test.ts` is what holds them there, and it also
 * refuses to let a fill be used as a text colour again, on either client.
 *
 * Phone first. A laptop gets the same single column, centred, because a party
 * game is played standing up.
 */

/* Space Grotesk, variable across 400-700 in one 22KB file, so headings and the
   wordmark share a face and nothing is fetched from a third party. Only
   headings and the wordmark use it: body text stays in the system font, which
   is tuned for small sizes. */
@font-face {
  font-family: "Space Grotesk";
  src: url(/fonts/space-grotesk.woff2) format("woff2-variations");
  font-weight: 400 700;
  font-display: swap;
}

:root {
  --canvas: #F7F3E8;
  --canvas-deep: #EFE7D5;
  --surface: #FFFDF7;
  --ink: #16181D;
  --slate: #504B41;

  /* The signature. Fills only -- see the note above. */
  --accent: #FFC93C;
  --accent-deep: #E8AF17;
  --on-accent: #16181D;
  /* Warm, and readable as text: 8.03:1 on the canvas. */
  --amber: #614500;
  /* The secondary action, and deep enough to carry a white label as a fill
     (8.86) as well as to be read on paper (7.20 on the deeper canvas). It was
     #2F4BFF, which was 4.78 on that canvas and carried ink labels at 3.02. */
  --blue: #0036C9;
  /* Something has gone wrong: an error, or a button armed to destroy
     something. Never a fill behind text; --accent-deep used to do this job at
     1.79:1, which is a yellow error message on cream paper. */
  --danger: #99001A;
  /* One focus ring, one colour, everywhere. Four rules used to name
     `--cobalt`, which is the app's word for this and is defined in no
     stylesheet, so the browser dropped the declaration and those controls
     showed no ring at all. */
  --focus: var(--blue);
  --teal: #005163;
  --tape: #F3D9A0;

  /* Two corners, and only two. A sheet of paper, and anything you tap.
     `PaperTheme.radiusPaper` and `.radiusControl` are the same two numbers in
     the app, and `test/app-design.test.ts` fails if they stop being. */
  --radius-card: 22px;
  --radius-btn: 16px;

  /* Clears the fixed language pill: 44 of control plus the 10 it sits down
     from the top, or the safe area, whichever is further. */
  --top-clearance: max(64px, calc(env(safe-area-inset-top) + 54px));

  --display: "Space Grotesk", -apple-system, system-ui, sans-serif;
  --wordmark: var(--display);
  --text: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Segoe UI", Roboto, sans-serif;
}

* { margin: 0; box-sizing: border-box; }

/*
 * Read aloud, never drawn. Clipped to a single pixel rather than hidden with
 * `display: none`, which would take it out of the accessibility tree and stop
 * it being read at all, and kept in the layout so a live region's updates are
 * announced.
 */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--text);
  background: var(--canvas);
  color: var(--ink);
  min-height: 100svh;
  line-height: 1.5;
  /* Six languages, one of which is German. `Geschichtenmaschine` is a single
     word 19 characters long, and at 200% zoom it was wider than the phone, so
     the page scrolled sideways rather than breaking it.

     `break-word` and not `anywhere`: `anywhere` also shrinks the box to its
     narrowest, and the result was every heading broken mid-word on a phone
     ("So funktionier t's") whether or not it needed to be. Where a box has to
     be allowed to shrink, it is told so where it lives. */
  overflow-wrap: break-word;
  /* The language pill is fixed over the top corner on every page, so the page
     has to begin underneath it. It did not: the pill sat on the lobby's table
     code and on the logo of all three pages, which also made part of the way
     home unclickable. Occlusion is the one thing the audit cannot see, since
     it measures what an element is rather than what is on top of it. */
  padding: var(--top-clearance) 16px max(24px, env(safe-area-inset-bottom));
  -webkit-font-smoothing: antialiased;
}

/*
 * Very narrow: a small phone, or an ordinary one at 200% zoom, which leaves a
 * 390pt screen 195 CSS pixels wide. Twenty points of card padding each side is
 * a third of the usable width there, and the words left over were being broken
 * mid-syllable to fit. Giving the padding back is worth more than the breathing
 * room it costs.
 */
@media (max-width: 340px) {
  body { padding-inline: 10px; }
  .card { padding: 14px; }
}

/* Narrower than about 250px the pill wraps onto two lines, which is 200% zoom
   on a phone. */
@media (max-width: 250px) {
  :root { --top-clearance: max(92px, calc(env(safe-area-inset-top) + 82px)); }
}

/* The only corners that are not one of the two tokens, and each is a shape
   rather than a size: 999px is a pill, 50% is a circle, 2px is a hairline rule
   where a 22 would be a semicircle. The app keeps the same three exceptions. */

/* Faint hand-drawn party motifs, as on the app's canvas. Purely decorative. */
.doodles { position: fixed; inset: 0; pointer-events: none; z-index: 0; }
.doodles span {
  position: absolute;
  color: var(--accent);
  opacity: 0.55;
  font-size: 30px;
  line-height: 1;
}

.wrap { position: relative; z-index: 1; width: 100%; max-width: 440px; margin: 0 auto; }

h1 {
  font-family: var(--display);
  /* The wordmark does not wrap, so at 200% zoom it has to get smaller instead
     of pushing the page sideways. */
  font-size: min(30px, 11vw);
  font-weight: 700;
  letter-spacing: -0.4px;
  margin: 4px 0 6px;
}

h2 {
  font-family: var(--display);
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -0.2px;
  margin: 0 0 4px;
}

.tagline { font-size: 15px; color: var(--slate); margin: 0 0 18px; }
.muted { color: var(--slate); font-size: 14px; }
.centre { text-align: center; }
.big { font-size: 40px; line-height: 1; margin-bottom: 8px; }

.fineprint { font-size: 12px; color: var(--slate); margin: 12px 0 0; text-align: center; }

.card {
  background: var(--surface);
  border: 1px solid rgb(22 24 29 / 0.07);
  border-radius: var(--radius-card);
  box-shadow: 0 8px 14px rgb(22 24 29 / 0.09);
  padding: 20px;
  margin-bottom: 14px;
}

/* MARK: Buttons */

.btn {
  font-family: var(--text);
  font-size: 16px;
  font-weight: 700;
  display: block;
  width: 100%;
  min-height: 52px;
  /* Padding as well as a minimum height: the 52 was doing all the spacing on
     its own, and at 200% zoom or in a longer language the label went flush to
     the border. `PartyButtonStyle` carries the same two numbers. */
  padding: 14px 20px;
  margin-top: 12px;
  border: none;
  border-radius: var(--radius-btn);
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 5px 10px rgb(232 175 23 / 0.35);
  cursor: pointer;
  transition: transform 0.18s, background 0.18s;
}
.btn:active { transform: scale(0.97); }
.btn:hover:not(:disabled) { background: var(--accent-deep); }
.btn:disabled { opacity: 0.5; box-shadow: none; cursor: default; }

.btn.ghost {
  background: var(--surface);
  color: var(--ink);
  border: 1.5px solid rgb(22 24 29 / 0.25);
  box-shadow: none;
}
.btn.ghost:hover:not(:disabled) { background: var(--canvas-deep); }

/* MARK: Focus, and the two controls both pages carry */

/*
 * One focus ring: one width, one colour, one offset, on everything focusable
 * on either page.
 *
 * There were eight rules across three stylesheets with two widths between
 * them, and four of them named `--cobalt`, which is the app's word for this
 * colour and is defined in no stylesheet, so the browser dropped the
 * declaration and those controls showed no ring at all. The text fields and
 * the selects went further and set `outline: none`, replacing the ring with a
 * yellow glow that measures 1.4:1 against the paper behind it. Keyboard
 * players had no idea where they were.
 *
 * One selector, because a ring is not a per-control decision: 3px rather than
 * 2 so it is not mistaken for the 1.5px border several controls already have,
 * and offset so it sits outside a rounded corner instead of across it.
 */
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

/*
 * The mark, as the way home from anywhere on the site.
 *
 * Shrunk to the mark rather than left as a full-width block, so the clickable
 * area is the logo and not the empty space either side of it. The image keeps
 * its own margins, so the layout is exactly what it was before the link.
 *
 * This and the language corner below were byte-for-byte copies in home.css and
 * play.css. Two copies of one component is how two screens come to disagree,
 * and both pages already load this file.
 */
.home-link {
  display: block;
  width: fit-content;
  margin-inline: auto;
  text-decoration: none;
  border-radius: var(--radius-btn);
}

/*
 * The language corner: the same control in the same place on every page, so
 * somebody who has seen it once knows where it is.
 */
.ui-language {
  position: fixed;
  top: max(10px, env(safe-area-inset-top));
  inset-inline-end: 12px;
  z-index: 20;
  display: flex;
  align-items: center;
  /* It wraps onto two lines rather than running off the side of the screen.
     At 200% zoom a phone is 195 CSS pixels wide and this pill wants 222, so
     its left edge and half its label were off the screen entirely. */
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: calc(100vw - 24px);
  gap: 4px;
  padding: 0 8px;
  border-radius: 999px;
  background: rgb(255 253 247 / 0.9);
  backdrop-filter: blur(6px);
  box-shadow: 0 2px 10px rgb(22 24 29 / 0.10);
  color: var(--slate);
}
.ui-language svg { width: 15px; height: 15px; fill: currentColor; opacity: 0.8; }
/*
 * What the control is, said on screen rather than only to a screen reader. In
 * slate against the select's ink, so the pill reads as a name and its current
 * value -- which is what a globe and the word "Deutsch" on their own do not.
 */
.ui-language-label { font-size: 13px; font-weight: 600; white-space: nowrap; }
.ui-language select {
  appearance: none;
  border: 0;
  background: none;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  /* 44 high, which is what the strict target-size rule asks for. It was 24,
     and the pill it sits in has grown to fit it. */
  min-height: 44px;
  padding: 0 2px 0 0;
}

/* MARK: The wordmark */

/*
 * The crease standing in as the capital M.
 *
 * Its viewBox is trimmed to the ink, so the glyph's bottom edge IS the
 * baseline and `vertical-align: baseline` lands it correctly without nudging.
 * Height is set in `em` against the cap height of the face, so the lockup
 * scales with the text rather than needing a size for every context.
 */
.wordmark {
  font-family: var(--wordmark);
  font-weight: 700;
  letter-spacing: -1.5px;
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
  color: var(--ink);
}

.wordmark .wm-m {
  display: inline-block;
  vertical-align: baseline;
  /* Space Grotesk's cap height is 0.7em; the crease matches it exactly. */
  height: 0.7em;
  width: auto;
  margin-right: -0.04em;
}
