/* twoloons.io
 *
 * A paper page. Aged cream, black ink, red suits - the ground the logo was drawn for, and the
 * one that reads as an invitation rather than as a product you're already inside of.
 *
 * The lake green is the game's, and it stays the game's. It shows up in exactly two places:
 * the Frustration band, which is a window into the app, and the footer, which is where the
 * brand signs off. That's what `.band-lake` is for - it flips a handful of local custom
 * properties, and every component below inherits the new ground without knowing about it.
 *
 * Palette is the brand kit's swatches, except the gold: the kit's accent is #E0A100 and at
 * this page's volume of gold it shouts, so labels and buttons use the app's paler sand.
 */

:root {
  --paper: #f4ede1;       /* the page - brand cream */
  --paper-deep: #ece2d0;  /* the alternating band, one step down */
  --card: #faf6ee;        /* a card face on top of the page */
  --ink: #111111;
  --ink-soft: #575348;
  --rule: rgba(17, 17, 17, 0.16);
  --brick: #bb0a0c;       /* brand red. Correct on cream. */
  --brick-lit: #d93a32;   /* the kit's lightened cut, for red sitting on green */
  --lake-deep: #17301e;   /* brand deep green */
  --lake: #1e3d28;
  --gold: #d9c9a3;

  /* What every component actually reads. .band-lake overrides these and nothing else has to
     know which ground it landed on. */
  --fg: var(--ink);
  --fg-soft: var(--ink-soft);
  --accent: var(--brick);
  --line: var(--rule);
  --tile-bg: rgba(255, 255, 255, 0.62);
  --tile-line: var(--rule);

  --measure: 1120px;
  --pad: clamp(1.1rem, 5vw, 3rem);

  /* Georgia for reading, because that's what the game sets its text in, and it suits an aged
     card face better than anything grotesque would. Headings go heavy sans: the logo is a fat
     brush script no system font approximates, and a heavy sans at least shares its weight
     instead of pretending to share its hand. */
  --font-body: Georgia, 'Iowan Old Style', 'Times New Roman', serif;
  --font-display: 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif;

  color-scheme: light;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.09rem);
  line-height: 1.62;
  color: var(--fg);
  background-color: var(--paper);
  /* Faint speckle so a full-bleed cream page doesn't read as "unstyled white-ish". Two tiny
     repeating gradients rather than a texture image - a couple of hundred bytes, and it
     survives any zoom level. */
  background-image:
    radial-gradient(rgba(17, 17, 17, 0.045) 1px, transparent 1px),
    radial-gradient(rgba(17, 17, 17, 0.03) 1px, transparent 1px);
  background-size: 22px 22px, 34px 34px;
  background-position: 0 0, 11px 17px;
  -webkit-text-size-adjust: 100%;
}

img { max-width: 100%; height: auto; }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.022em;
  line-height: 1.06;
  margin: 0 0 0.55rem;
  text-wrap: balance;
}

a { color: inherit; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 99;
  background: var(--lake-deep); color: var(--paper); padding: 0.6rem 1rem; border-radius: 0 0 8px 0;
}
.skip:focus { left: 0; }

:where(a, button):focus-visible {
  outline: 3px solid var(--brick);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ── The lake ────────────────────────────────────────────────────────────────────────────
   The game's ground, used where the page is talking about the game. Everything inside reads
   its colours off these, so no component needs a dark-mode variant of its own. */

.band-lake {
  --fg: var(--paper);
  --fg-soft: rgba(244, 237, 225, 0.82);
  --accent: var(--gold);
  --line: rgba(217, 201, 163, 0.28);
  --tile-bg: rgba(12, 24, 15, 0.55);
  --tile-line: rgba(217, 201, 163, 0.34);
  color: var(--fg);
  background-color: var(--lake-deep);
  /* The table canvas itself, dimmed. It's the actual felt the game deals onto, which is the
     point - this band is a window into the app, not a dark section. */
  /* Dimmed enough to read cream text over, light enough that the canvas is still visibly a
     painted lake with a canoe and a no-wake sign in the corners. Any heavier and there was no
     point using the real felt. */
  background-image:
    linear-gradient(rgba(23, 48, 30, 0.72), rgba(23, 48, 30, 0.82)),
    url('/assets/lake.png');
  background-size: cover, cover;
  background-position: center, center;
}

/* ── Top bar ─────────────────────────────────────────────────────────────────────────── */

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 0.6rem 1.4rem;
  max-width: var(--measure); margin: 0 auto; padding: 1.1rem var(--pad) 0.4rem;
}
.topbar-mark img { display: block; width: 190px; height: auto; }

.topbar-nav {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem 1.15rem;
  font-family: var(--font-display); font-size: 0.84rem; font-weight: 700;
  letter-spacing: 0.02em;
}
/* :not(.btn) matters. `.topbar-nav a` outranks `.btn`, so without it this rule strips the
   Play button's own horizontal padding and it collapses to a disc with the word hanging
   out of it. */
.topbar-nav a:not(.btn) {
  text-decoration: none; padding: 0.2rem 0; border-bottom: 2px solid transparent;
}
.topbar-nav a:not(.btn):hover { border-bottom-color: var(--brick); }

/* The section links are the first thing to go when there's no room: on a phone the Play
   button is the only one actually load-bearing. */
@media (max-width: 640px) {
  .topbar { justify-content: center; }
  .topbar-nav a:not(.btn) { display: none; }
}

/* ── Buttons ─────────────────────────────────────────────────────────────────────────── */

.btn {
  display: inline-block; text-decoration: none;
  font-family: var(--font-display); font-weight: 800; letter-spacing: -0.01em;
  background: var(--lake-deep); color: var(--paper);
  border: 2px solid var(--lake-deep); border-radius: 999px;
  padding: 0.62rem 1.5rem;
  /* The hard offset shadow is the one deliberately un-slick thing here: same
     hand-drawn-sticker energy as the logo, and it makes a button look pressable without a
     gradient anywhere near it. */
  box-shadow: 3px 3px 0 rgba(17, 17, 17, 0.82);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.btn:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0 rgba(17, 17, 17, 0.82); }
.btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 rgba(17, 17, 17, 0.82); }

.btn-sm { padding: 0.36rem 1.05rem; font-size: 0.86rem; box-shadow: 2px 2px 0 rgba(17,17,17,0.82); }
.btn-lg { font-size: 1.02rem; padding: 0.75rem 1.8rem; }

.btn-quiet { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-quiet:hover { background: rgba(17, 17, 17, 0.06); }

/* On the lake the filled button has to stop being lake-coloured. */
.band-lake .btn {
  background: var(--gold); color: #1d1a14; border-color: #1d1a14;
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.55);
}
.band-lake .btn:hover { box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.55); }
.band-lake .btn:active { box-shadow: 1px 1px 0 rgba(0, 0, 0, 0.55); }

@media (prefers-reduced-motion: reduce) {
  .btn, .btn:hover, .btn:active { transform: none; transition: none; }
}

.cta-row {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.75rem 1.1rem;
  margin: 1.5rem 0 0;
}
.cta-note { font-size: 0.92rem; color: var(--fg-soft); font-style: italic; }

/* ── Cards ───────────────────────────────────────────────────────────────────────────────
   A card face, for the things that want to read as one. The inner rule is an inset
   pseudo-element rather than a second border, so it can sit a few pixels in from the edge
   the way it does on a real card. */

.card {
  position: relative;
  background: var(--card); color: var(--ink);
  border: 1px solid rgba(17, 17, 17, 0.5); border-radius: 12px;
  box-shadow: 5px 6px 0 rgba(17, 17, 17, 0.1);
  padding: 1.4rem 1.5rem;
}
.card::after {
  content: ''; position: absolute; inset: 7px;
  border: 1px solid rgba(17, 17, 17, 0.16); border-radius: 6px;
  pointer-events: none;
}
/* Inside a card the ground is cream again, whatever band it happens to be sitting on. */
.card { --fg: var(--ink); --fg-soft: var(--ink-soft); --accent: var(--brick); }
.band-lake .card { box-shadow: 0 16px 34px rgba(0, 0, 0, 0.42); }

/* ── Hero ────────────────────────────────────────────────────────────────────────────── */

.hero {
  display: grid; align-items: center; gap: clamp(1.5rem, 4vw, 3rem);
  /* The mark is wide and short - better than 2:1 - so it needs a genuinely wide column or it
     reads as a small drawing parked beside a wall of text. */
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  max-width: var(--measure); margin: 0 auto;
  padding: clamp(1.5rem, 5vw, 3.2rem) var(--pad) clamp(2rem, 5vw, 3.4rem);
}
/* Capped well below what the viewport would allow. The headline is the brand tagline, it
   carries its own line break, and it has to stay small enough that "We take card games
   seriously." holds together on one line - the whole joke is the turn onto the second line.
   That holds everywhere down to about 420px, below which the first line wraps and it sets on
   three. Left alone deliberately: the size that would force two lines on a 390px phone is
   smaller than the paragraph underneath it, and the beat survives the wrap anyway because
   "Not ourselves." still gets a line of its own. */
.hero h1 { font-size: clamp(1.75rem, 2.75vw, 2.05rem); margin-bottom: 1rem; }
/* NOT is underlined in the lockup, so it's underlined here. Not italic - <em> is carrying the
   emphasis semantically, and the drawn tagline sets it upright. */
.hero h1 em { font-style: normal; text-decoration: underline; text-underline-offset: 0.12em; }

.hero-art img { display: block; width: 100%; }

/* Art above words on a phone. It's the loons - they say what the company is faster than the
   headline does, and they're the reason anybody clicked. */
@media (max-width: 800px) {
  .hero { grid-template-columns: minmax(0, 1fr); }
  .hero-art { order: -1; max-width: 460px; margin: 0 auto; }
}

.kicker {
  font-family: var(--font-display); font-weight: 800;
  font-size: 0.78rem; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 0.7rem;
}

.lede { font-size: 1.12em; max-width: 34em; margin: 0; }

.quip {
  margin: 1.4rem 0 0; min-height: 1.5em;
  font-style: italic; color: var(--fg-soft); font-size: 0.95rem;
  opacity: 1; transition: opacity 0.3s ease;
}
.quip.is-fading { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .quip { transition: none; } }

/* ── Bands ───────────────────────────────────────────────────────────────────────────── */

.band { padding: clamp(2.6rem, 7vw, 4.6rem) var(--pad); }
.band > * { max-width: var(--measure); margin-inline: auto; }
.band-alt { background: var(--paper-deep); }

.band h2 { font-size: clamp(1.85rem, 4.2vw, 2.7rem); }

/* The hand-drawn rule under a heading, faked with a skewed block. Cheaper than an SVG and it
   inherits whatever colour the band wants. */
.ruled { position: relative; padding-bottom: 0.75rem; margin-bottom: 0.9rem; }
.ruled::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  width: 84px; height: 5px; background: var(--accent);
  transform: skewX(-14deg); border-radius: 2px;
}

.section-lede { max-width: 46em; font-size: 1.06em; margin: 0 0 2.2rem; color: var(--fg-soft); }

.aside {
  max-width: 46em; margin: 2.2rem 0 0;
  font-style: italic; color: var(--fg-soft);
  border-left: 3px solid var(--line); padding-left: 1rem;
}

/* ── The cast ────────────────────────────────────────────────────────────────────────── */

.cast {
  display: grid; gap: clamp(1.2rem, 3vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
}
/* Room top and bottom for the two corner indices, so the last line of copy doesn't run under
   the upside-down one. */
.loon-card { margin: 0; padding-top: 1.9rem; padding-bottom: 2.6rem; text-align: center; }
.loon-card img {
  display: block; height: clamp(170px, 20vw, 235px); width: auto; margin: 0 auto 0.9rem;
  transition: transform 0.18s ease;
}
/* A small lift on hover, because they're characters and characters should react. Not a loop -
   the page shouldn't be moving while you read it - and not a tilt either: the brand doc is
   explicit that the loons sit on a waterline and stay level. */
.loon-card:hover img { transform: translateY(-6px); }
@media (prefers-reduced-motion: reduce) { .loon-card:hover img { transform: none; } }

/* The corner index, same as a face card carries. Two of them, opposite corners, the second
   upside down - which is the detail that makes it read as a card rather than as a box with a
   letter in it. */
.card-index {
  position: absolute; top: 0.85rem; left: 1rem; z-index: 1;
  font-family: var(--font-display); font-weight: 800; font-size: 1.05rem;
  line-height: 1; text-align: center; color: var(--ink);
}
.card-index span { display: block; font-size: 0.9rem; margin-top: 0.1rem; }
.card-index--flip { top: auto; left: auto; right: 1rem; bottom: 0.85rem; transform: rotate(180deg); }
.card-index--red { color: var(--brick); }

.loon-card h3 { font-size: 1.75rem; margin-bottom: 0.1rem; }
.loon-role {
  font-family: var(--font-display); font-weight: 700; font-size: 0.74rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--brick);
  margin: 0 0 0.6rem;
}
.loon-card p:last-child { margin: 0 auto; max-width: 30em; }

/* ── Split (art beside words) ────────────────────────────────────────────────────────── */

.split {
  display: grid; align-items: center; gap: clamp(1.6rem, 4vw, 3.2rem);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
}
.split-art { padding: 0.7rem; }
.split-art img { display: block; width: 100%; border-radius: 6px; }
.split-words h1, .split-words h2 { font-size: clamp(1.7rem, 3.8vw, 2.4rem); }
@media (max-width: 800px) {
  .split { grid-template-columns: minmax(0, 1fr); }
}

.ticks { list-style: none; padding: 0; margin: 1.3rem 0 0; }
.ticks li { position: relative; padding-left: 1.6rem; margin-bottom: 0.55rem; }
/* A suit pip instead of a bullet. It's a card company; the list may as well know it. The reds
   use the kit's lightened cut, since these sit on green - the brand red goes muddy there. */
.ticks li::before {
  content: '♠'; position: absolute; left: 0; top: -0.05em;
  color: var(--accent); font-size: 0.95em;
}
.ticks li:nth-child(2)::before { content: '♥'; color: var(--brick-lit); }
.ticks li:nth-child(3)::before { content: '♦'; color: var(--brick-lit); }
.ticks li:nth-child(4)::before { content: '♣'; }

/* ── The shapes of game ──────────────────────────────────────────────────────────────────
   Six presets. The name is what's being chosen and the sentence after it is why you'd choose
   it, so the name gets its own line and its own treatment rather than running into the blurb. */

.ways {
  list-style: none; padding: 0; margin: 0;
  display: grid; gap: 0.55rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
}
.ways li {
  margin: 0; padding: 0.7rem 0.9rem; font-size: 0.95rem;
  background: var(--tile-bg); border: 1px solid var(--tile-line); border-radius: 9px;
}
.ways b {
  display: block; font-family: var(--font-display); font-weight: 800; font-size: 0.8rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent);
  margin-bottom: 0.15rem;
}
.ways-note, .badges-note {
  max-width: 44em; margin: 0.9rem 0 0; font-size: 0.93rem; color: var(--fg-soft);
}
.backs-note {
  max-width: 40em; margin: 0.9rem auto 0; text-align: center;
  font-size: 0.93rem; color: var(--fg-soft);
}

/* ── Screenshots ─────────────────────────────────────────────────────────────────────────
   Real captures of the game, in a frame that says "phone" without drawing a phone: a rounded
   dark bezel and a shadow. Deliberately no notch, no home bar and no hand holding it - the
   mock-up furniture is the part that dates, and the screenshots underneath are the claim.

   Heights are allowed to differ, and the row is top-aligned, because forcing one height would
   mean cropping the hand off the table shot to match a settings list. See tools/make_screens.py
   for the other half of that decision. */

.screens {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: center;
  gap: clamp(1rem, 3vw, 2rem); margin: 2.6rem 0 0; padding: 0;
}
/* The basis is picked so four of these wrap 2+2 rather than 3+1. Three at this width plus
   their gaps overflow the 1120 measure and two don't, which is the whole trick: an even split
   reads as a deliberate pair of rows, while a lone fourth shot reads as a mistake. */
.phone {
  flex: 0 1 clamp(230px, 30vw, 380px); margin: 0;
}
.phone img {
  display: block; width: 100%; height: auto;
  border-radius: 16px; border: 3px solid rgba(10, 16, 11, 0.9);
  box-shadow: 0 18px 38px rgba(0, 0, 0, 0.4);
}
.phone figcaption {
  margin-top: 0.7rem; font-size: 0.87rem; line-height: 1.45;
  color: var(--fg-soft); text-align: center;
}
/* The three faces of one idea, so they get more room than a row of four would allow.
   Bottom-aligned captions rather than top-aligned frames: these three are within about 20% of
   each other in height, and a caption sitting higher than the two beside it reads as a mistake.
   The general row above stays top-aligned, where the table shot is half again as tall as the
   others and equalising would open a hole under the short ones. */
.screens-pair { justify-content: center; align-items: stretch; margin-top: 2.2rem; }
.screens-pair .phone {
  flex: 0 1 clamp(240px, 34vw, 330px);
  display: flex; flex-direction: column;
}
.screens-pair .phone figcaption { margin-top: auto; padding-top: 0.7rem; }

/* ── Clips ───────────────────────────────────────────────────────────────────────────────
   Same frame as a screenshot, because they are screenshots that move. Sized off the GIF's own
   300px rather than scaled up: there is no retina version of an animation you can afford, and
   a GIF drawn larger than it was encoded is a GIF that looks soft for no benefit. */

.clips {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start;
  gap: clamp(1rem, 3vw, 2.4rem); margin: 2.6rem 0 0;
}
.clip { flex: 0 1 300px; margin: 0; }
/* The hand-over is a band rather than a screenful - it is only the top of the board - so it is
   shorter than the two beside it and would leave its caption floating halfway up the row. Given
   its own line under them instead, at the width of both together. */
.clip-wide { flex: 1 1 100%; max-width: 640px; }
.clip-wide img { max-width: 420px; margin: 0 auto; }
.clip-wide figcaption { max-width: 42em; margin-inline: auto; text-align: center; }
.clip img {
  display: block; width: 100%; height: auto;
  border-radius: 16px; border: 3px solid rgba(10, 16, 11, 0.9);
  box-shadow: 0 18px 38px rgba(0, 0, 0, 0.45);
}
.clip figcaption {
  margin-top: 0.7rem; font-size: 0.9rem; line-height: 1.45; color: var(--fg-soft);
}
.clip figcaption b { color: var(--fg); }

/* Not a phone: for the things that only ever appear as a band across a screen, where framing a
   band as a whole phone would be a picture of something that never happens. */
.strip {
  max-width: 560px; margin: 2rem auto 0; padding: 0;
}
.strip img {
  display: block; width: 100%; height: auto;
  border-radius: 12px; border: 2px solid rgba(10, 16, 11, 0.85);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.3);
}
.strip figcaption {
  margin-top: 0.8rem; font-size: 0.93rem; line-height: 1.55; color: var(--fg-soft);
}

/* ── The argument ────────────────────────────────────────────────────────────────────────
   Six claims, each with its reasoning under it. Not tiles: a tile is a feature and these are
   positions, so they get a rule above them and room to be paragraphs. */

.prose { max-width: 44em; margin: 0 0 1rem; }
.prose p { margin: 0 0 1rem; font-size: 1.03em; }
.prose p:last-child { margin-bottom: 0; }

.reasons {
  display: grid; gap: 0 clamp(1.6rem, 4vw, 3rem); margin: 2.8rem 0 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
}
.reason { border-top: 2px solid var(--line); padding: 1.4rem 0 1.6rem; }
.reason h3 { font-size: 1.2rem; margin-bottom: 0.5rem; }
/* The one emphasised word in a heading gets underlined rather than italicised - same treatment
   as the hero's "Not", so the page has one way of leaning on a word instead of two. */
.reason h3 em {
  font-style: normal; text-decoration: underline; text-underline-offset: 0.1em;
}
.reason p { margin: 0 0 0.7rem; font-size: 0.96rem; color: var(--fg-soft); }
.reason p:last-child { margin-bottom: 0; }
/* Quoted lines from the game itself, and the one derived number. Italic rather than quoted
   marks-and-bold, so a sentence lifted out of the app reads as a quotation and not as emphasis
   we added. */
.reason em { font-style: italic; }


/* ── The card backs ──────────────────────────────────────────────────────────────────── */

.backs {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-end;
  gap: 0.4rem; margin: 2.6rem 0 0; padding: 0; list-style: none;
}
.backs li { margin: 0; }
.backs img {
  /* TEN of these now, and both ends of the clamp are chosen so the row wraps evenly rather than
     leaving strays. At 106 the row is 10 × 106 + 9 × 6.4 ≈ 1018, inside the 1120 measure, so the
     whole cupboard is one line on a desktop. At 64 a 390px phone fits five, so it wraps 5+5.
     The previous 128/74 pair was tuned for eight and put the last two on a line of their own. */
  display: block; width: clamp(64px, 9vw, 106px); height: auto;
  border-radius: 7px; box-shadow: 0 10px 22px rgba(0, 0, 0, 0.45);
  transition: transform 0.18s ease;
}
/* Fanned, not stacked in a row - alternating tilt reads as cards on a table, a flat row reads
   as a product grid. (These are card backs, not the mark; the no-tilt rule is about the
   loons.) */
.backs li:nth-child(odd) img { transform: rotate(-3deg); }
.backs li:nth-child(even) img { transform: rotate(2.5deg); }
.backs li img:hover { transform: rotate(0) translateY(-6px); }
@media (prefers-reduced-motion: reduce) {
  .backs img, .backs li img:hover { transform: none; transition: none; }
}

/* ── The court ───────────────────────────────────────────────────────────────────────────
   Twelve figures mounted the way the game mounts them: the art on card stock, with the corner
   index drawn in markup rather than baked into the picture. That's not styling for its own
   sake - it's why a queen of hearts and a queen of spades can be the same crop, and why the
   index can never disagree with the card it's sitting on.

   Worth knowing if these are ever used anywhere else: two of the four source sheets have the
   wrong suit painted into the bottom corner. Drawing the pip here is what makes that harmless.

   Held to a max-width that wraps them 6+6 on a desktop and 3+3+3+3 on a phone, so the four
   families never split across a line boundary in a way that implies a fifth. */

.court {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem;
  padding: 0; margin: 0 auto; list-style: none; max-width: 46rem;
}
.court li { margin: 0; }
.royal {
  position: relative; display: block;
  width: clamp(88px, 13vw, 116px); aspect-ratio: 5 / 7;
  /* The jokers' paper, which is the stock the game prints these on too. */
  background: #f6efdd;
  border: 1px solid rgba(27, 24, 19, 0.45); border-radius: 6px;
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.34);
  overflow: hidden;
  transition: transform 0.18s ease;
}
.court li:hover .royal { transform: translateY(-6px); }
@media (prefers-reduced-motion: reduce) {
  .royal, .court li:hover .royal { transform: none; transition: none; }
}
/* Bottom-anchored and slightly oversized: these are cut to the bird, and a bird floated in the
   middle of a card reads as a sticker. Sitting it on the bottom edge reads as a court card. */
.royal img {
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 96%; height: auto; max-height: 88%; object-fit: contain; object-position: bottom;
}
.royal-ix {
  position: absolute; top: 4%; left: 7%; z-index: 1;
  font-family: var(--font-display); font-weight: 800; font-size: 0.78rem; line-height: 1;
  color: #1b1813;
}
.royal-ix i { display: block; font-style: normal; font-size: 0.72rem; margin-top: 0.08em; }
.royal-ix.is-red { color: var(--brick); }
/* ── The jokers ──────────────────────────────────────────────────────────────────────────
   Lined up by height, not width. They come off two card templates whose proportions don't
   quite match, and matching their tops and bottoms hides that; matching their sides would
   leave one of them visibly shorter than the rest. */

.jokers {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-end;
  gap: 0.55rem; margin: 0; padding: 0; list-style: none;
}
.jokers li { margin: 0; }

/* One cell of the sprite sheet, picked by --i. TEN cells across, so the sheet is sized to
   1000% of the element and the horizontal position runs 0%..100% over the nine gaps between
   them - the standard trick, and the reason the whole row costs one request.

   Keep this in step with jokers.json: it's the same index arithmetic, written twice, and the
   two numbers below are both derived from its `columns`. It went from eight to ten when the
   game paired every deck design with two jokers, which is why they are both here rather than
   inferred - a sheet that grows silently draws nine cards and a sliver of a tenth. */
/* The ceiling is arithmetic, not taste. Ten cards at 5:7 plus nine 0.55rem gaps have to fit the
   1120px measure: 10 × 0.714h + 9 × 8.8 <= 1120 puts h at 145 max, so 140 leaves a little air.
   Above that the tenth card wraps and sits alone on a second line, which reads as a mistake.
   Narrower viewports wrap to 5+5, which is even and looks deliberate. */
.jokers { --jh: clamp(112px, 12vw, 140px); }
.joker {
  position: relative;
  display: block; height: var(--jh); aspect-ratio: 300 / 420;
  background-image: url('/assets/jokers-sheet.png');
  background-size: 1000% 100%;
  background-position: calc(var(--i) * 100% / 9) 0;
  border-radius: 5px; box-shadow: 0 8px 18px rgba(0, 0, 0, 0.4);
  transition: transform 0.18s ease;
}
.jokers li:hover .joker { transform: translateY(-6px); }

/* The names USED to be drawn here, into the `caption` rect jokers.json declared, because the
   cards shipped with an empty nameplate and the generator that drew them could not spell.
   The re-cut art prints its own name, correctly, and the manifest dropped `caption` to say so -
   so drawing text now would print every name twice.
 
   Except on two cards. Delicious Deer and Southie Lobster came off a different template: art to
   the edges, no nameplate, no name. The game shows them nameless (createCardFaceEl draws no
   text on a joker, on the stated grounds that "the drawing names the character along the
   bottom"). The manifest still names all ten, so the page draws a name only where the art has
   none, and .joker b appears on exactly those two in the markup.
 
   This is a patch over an art inconsistency, not a feature. When the two are redrawn to match
   the other eight, delete this rule and the two <b> tags with it. */
.joker b {
  position: absolute; left: 4%; right: 4%; bottom: 3%;
  display: flex; align-items: center; justify-content: center;
  padding: 0.18em 0;
  font-family: var(--font-display); font-weight: 800;
  font-size: calc(var(--jh) * 0.058); letter-spacing: 0.01em;
  text-transform: uppercase; color: #f6efdd;
  background: rgba(20, 16, 10, 0.72); border-radius: 3px;
  white-space: nowrap; overflow: hidden;
  transform: scaleX(0.92);
}

/* Dealt: each card slides up into place, staggered by its position in the row. --deal is set
   per item by site.js, which also shuffles them. */
.jokers[data-dealt] li { animation: dealIn 0.42s ease both; animation-delay: var(--deal, 0s); }
@keyframes dealIn {
  from { opacity: 0; transform: translateY(18px) rotate(-4deg); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .joker, .jokers li:hover .joker { transform: none; transition: none; }
  .jokers[data-dealt] li { animation: none; }
}

.backs-label, .flock-label, .badges-label {
  font-family: var(--font-display); font-weight: 800; font-size: 0.72rem;
  letter-spacing: 0.15em; text-transform: uppercase; color: var(--accent);
  margin: 2.4rem 0 0.9rem;
}
.backs-label { text-align: center; }
.flock-label { text-align: center; }
.flock-note {
  max-width: 38em; margin: 1rem auto 0; text-align: center;
  font-size: 0.93rem; color: var(--fg-soft);
}

/* ── Tiles ───────────────────────────────────────────────────────────────────────────── */

.grid-4, .grid-2 { display: grid; gap: 1.1rem; }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 215px), 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }

.tile {
  background: var(--tile-bg);
  border: 1px solid var(--tile-line); border-radius: 10px;
  padding: 1.25rem 1.3rem;
}
.tile h3 { font-size: 1.16rem; margin-bottom: 0.35rem; }
.tile p { margin: 0; font-size: 0.96rem; }
.tile p + p { margin-top: 0.7rem; }

.tile-lead { border-color: var(--lake-deep); border-width: 2px; background: rgba(255,255,255,0.85); }

.tile-tag {
  font-family: var(--font-display); font-weight: 800; font-size: 0.68rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--lake-deep);
  margin: 0 0 0.4rem;
}
.tile-tag-soon { color: var(--ink-soft); }

/* ── The campfire ────────────────────────────────────────────────────────────────────────
   The one painting the page shows at full size rather than using as a ground. Framed like a
   picture, because on a cream page that's what it is. */

.scene {
  display: block; margin: 0 0 1.8rem; padding: 0.6rem;
  background: var(--card); border: 1px solid rgba(17, 17, 17, 0.5); border-radius: 12px;
  box-shadow: 5px 6px 0 rgba(17, 17, 17, 0.1);
}
.scene img { display: block; width: 100%; border-radius: 6px; }
.scene figcaption {
  margin-top: 0.55rem; text-align: center;
  font-size: 0.85rem; font-style: italic; color: var(--ink-soft);
}

/* ── The ladder ──────────────────────────────────────────────────────────────────────── */

.ladder { list-style: none; padding: 0; margin: 0; display: grid; gap: 0; }
.ladder li {
  display: grid; gap: 0.2rem 1.6rem;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  padding: 1.3rem 0;
  border-top: 1px solid var(--line);
}
.ladder li:first-child { border-top: none; }
.ladder h3 { grid-column: 2; font-size: 1.3rem; }
.ladder p { grid-column: 2; margin: 0; max-width: 44em; color: var(--fg-soft); }
.step {
  grid-row: 1 / span 2; align-self: start;
  font-family: var(--font-display); font-weight: 800; font-size: 0.78rem;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent);
  padding-top: 0.4rem;
}
/* Stacked on a phone: a 7.5rem label column beside a paragraph leaves the paragraph about
   four words wide. */
@media (max-width: 640px) {
  .ladder li { grid-template-columns: minmax(0, 1fr); }
  .ladder h3, .ladder p { grid-column: 1; }
  .step { grid-row: auto; padding-top: 0; margin-bottom: 0.3rem; }
}

/* ── The flock ───────────────────────────────────────────────────────────────────────────
   Held in at six across so twenty-four wrap into four clean rows of six. Left to the full
   measure they fit eleven per line and the last row ends up with two on it, which reads as a
   mistake rather than as a roster. Six is also what divides 24 without a remainder at every
   breakpoint that matters: 6, then 4+4+4+4+4+4 on a tablet, then 3 across on a phone. */

.flock {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.9rem 1rem;
  padding: 0; margin: 0 auto; list-style: none; max-width: 39rem;
}
.flock li { width: 5.2rem; text-align: center; }
.flock img {
  display: block; width: 100%; height: auto; border-radius: 12px;
  border: 1px solid rgba(17, 17, 17, 0.35);
  transition: transform 0.16s ease;
}
.flock li:hover img { transform: translateY(-4px); }
@media (prefers-reduced-motion: reduce) { .flock li:hover img { transform: none; } }
.flock span {
  display: block; margin-top: 0.35rem;
  font-family: var(--font-display); font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--fg-soft);
  line-height: 1.25;
}

/* ── Badges ──────────────────────────────────────────────────────────────────────────── */

.badges { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0; margin: 0; }
.badges li {
  font-family: var(--font-display); font-weight: 700; font-size: 0.85rem;
  border: 2px solid var(--ink); border-radius: 999px;
  padding: 0.28rem 0.9rem;
  transform: rotate(-1deg);
}
/* Alternating tilt so a row of them looks pinned up rather than laid out. */
.badges li:nth-child(even) { transform: rotate(1.4deg); }
/* The glyph the game actually shows on the card, so the badge on the page and the badge in the
   app are recognisably the same object. */
.badges li i { font-style: normal; margin-right: 0.28rem; }

/* ── Follow line ─────────────────────────────────────────────────────────────────────── */

.follow-line {
  display: inline-flex; align-items: center; gap: 0.42rem;
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem;
  color: var(--lake-deep); text-decoration: none;
  border-bottom: 2px solid var(--brick); padding-bottom: 1px;
}
.follow-line:hover { color: var(--brick); }
/* What it renders as when there's no account to point at - a caption, not a dead link. */
.follow-line.is-soon { color: var(--ink-soft); border-bottom-color: transparent; }
.follow-glyph { flex: 0 0 auto; }

/* ── Footer ──────────────────────────────────────────────────────────────────────────── */

.foot {
  background: var(--lake-deep); color: var(--paper);
  padding: clamp(2rem, 5vw, 3rem) var(--pad);
}
.foot-inner { max-width: var(--measure); margin: 0 auto; }
/* The primary lockup's screen minimum is 220px wide - below that the brand doc says drop to
   the compact cut rather than shrink the tagline into mush. 260 keeps it clear of that. */
.foot-mark img { display: block; width: 260px; height: auto; margin-bottom: 1.2rem; }
.foot-nav {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem 1.5rem;
  margin-bottom: 1.1rem;
}
.foot-nav a {
  font-family: var(--font-display); font-weight: 700; font-size: 0.95rem;
  text-decoration: none; border-bottom: 2px solid var(--gold); padding-bottom: 1px;
}
.foot-nav a:hover { color: var(--gold); }
.foot .follow-line { color: var(--paper); border-bottom-color: var(--gold); }
.foot .follow-line:hover { color: var(--gold); }
.foot .follow-line.is-soon { color: rgba(244, 237, 225, 0.62); }

/* The conservation line, above the fine print rather than inside it: it's an aside in our own
   voice, and folding it into the copyright-ish line would read as boilerplate. */
.foot-loons {
  margin: 0 0 0.55rem; font-size: 0.9rem; color: rgba(244, 237, 225, 0.78);
}
.foot-loons a {
  color: inherit; text-decoration: none; border-bottom: 1px solid var(--gold);
}
.foot-loons a:hover { color: var(--gold); }

.foot-fine { margin: 0; font-size: 0.88rem; color: rgba(244, 237, 225, 0.66); }
.foot-sep { padding: 0 0.35rem; }

/* ── The studio ──────────────────────────────────────────────────────────────────────────
   The front page stopped being Frustration's page and became the studio's when there were two
   things to point at. Each product gets a card in the site's own card idiom, with a window into
   the app on top and two ways in at the bottom: the thing itself, and the page about it. */

.products {
  display: grid; gap: clamp(1.2rem, 3vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr));
}
.product { display: flex; flex-direction: column; padding: 1.3rem 1.4rem 1.5rem; }
.product h3 { font-size: 1.75rem; margin: 0.15rem 0 0.35rem; }
.product p { margin: 0 0 0.7rem; }
.product .cta-row { margin-top: auto; padding-top: 0.6rem; gap: 0.6rem 1rem; }
.product-tag {
  font-family: var(--font-display); font-weight: 800; font-size: 0.7rem;
  letter-spacing: 0.15em; text-transform: uppercase; color: var(--accent);
  margin: 0;
}
/* The card's window. One is a painting, one is a phone; both are cropped to the same band so
   the two cards line up, and the phone fades out at the bottom rather than ending on a hard
   edge, which would read as a screenshot of a screenshot. */
.product-window {
  position: relative; height: 240px; overflow: hidden;
  margin: 0 0 1rem; border-radius: 8px;
  background: var(--paper-deep);
}
.product-window img { display: block; width: 100%; height: 100%; object-fit: cover; }
.product-window.is-phone {
  display: flex; align-items: flex-start; justify-content: center; gap: 1.2rem;
  padding: 1rem 1rem 0; background: var(--hm-sand);
}
.product-window.is-phone img {
  width: 200px; height: auto; flex: 0 0 auto;
  border-radius: 14px 14px 0 0; border: 3px solid rgba(10, 16, 11, 0.9); border-bottom: 0;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.28);
}
.product-window.is-phone::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 60px;
  background: linear-gradient(rgba(245, 234, 217, 0), var(--hm-sand));
}
.product-mark { width: 72px; height: 72px; flex: 0 0 auto; margin-top: 1.4rem; }
.product-mark img { width: 100%; height: 100%; border: 0; box-shadow: none; border-radius: 50%; }
@media (max-width: 480px) {
  .product-mark { display: none; }
}

/* ── Human Mode's ground ─────────────────────────────────────────────────────────────────
   Straight off the Human Mode identity sheet: Pinetree ink on Sand, Lake for anything that
   goes, Golden for the sun. The same trick as .band-lake - flip the local properties and every
   component below inherits the new ground. This is Human Mode's window, the way the lake is
   Frustration's, and it must not leak into the rest of the page. */

:root {
  --hm-sand: #f5ead9;
  --hm-lift: #fdf6ea;
  --hm-line: #d8c6a6;
  --hm-ink: #173933;
  --hm-ink-soft: #4f665c;
  --hm-lake: #247486;
  --hm-gold: #f4af43;
}
.band-sand {
  --fg: var(--hm-ink);
  --fg-soft: var(--hm-ink-soft);
  --accent: var(--hm-lake);
  --line: var(--hm-line);
  --tile-bg: var(--hm-lift);
  --tile-line: var(--hm-line);
  color: var(--fg);
  background-color: var(--hm-sand);
  /* The app's own linen: a faint grid, the texture the Human Mode skin draws under everything. */
  background-image:
    linear-gradient(rgba(23, 57, 51, 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(23, 57, 51, 0.045) 1px, transparent 1px);
  background-size: 18px 18px;
}
.band-sand .btn {
  background: var(--hm-lake); color: var(--hm-lift); border-color: var(--hm-ink);
  box-shadow: 3px 3px 0 rgba(23, 57, 51, 0.7);
}
.band-sand .btn:hover { box-shadow: 5px 5px 0 rgba(23, 57, 51, 0.7); }
.band-sand .btn:active { box-shadow: 1px 1px 0 rgba(23, 57, 51, 0.7); }
.band-sand .btn-quiet { background: transparent; color: var(--hm-ink); border-color: var(--hm-ink); }
.band-sand .btn-quiet:hover { background: rgba(23, 57, 51, 0.06); }
.band-sand .tile-lead { border-color: var(--hm-lake); background: var(--hm-lift); }
.band-sand .tile-tag { color: var(--hm-lake); }
.band-sand :where(a, button):focus-visible { outline-color: var(--hm-lake); }
/* Human Mode's phones are lighter than the game's: the app is paper, and a heavy black bezel
   round a sand-coloured screen reads as a hole in the page. */
.band-sand .phone img, .band-sand .strip img {
  border-color: rgba(23, 57, 51, 0.55); box-shadow: 0 14px 30px rgba(23, 57, 51, 0.22);
}
.band-sand .ruled::after { background: var(--hm-gold); }

/* The mark and the eyebrow, as the app's own front door sets them. */
.hm-hero { display: flex; align-items: center; gap: 1.2rem; margin-bottom: 1.2rem; }
.hm-hero img { width: 88px; height: 88px; border-radius: 50%; flex: 0 0 auto; }
.eyebrow {
  font-family: var(--font-display); font-weight: 800;
  font-size: 0.78rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--hm-lake); margin: 0;
}

/* ── The rank ladder ─────────────────────────────────────────────────────────────────────
   Ten ranks, printed the way the app prints them: the name, then the sentence that says what
   you did to earn it. Numbered, because the order is the whole argument - Starter before
   Finisher, Asker before either. */

.ranks {
  list-style: none; counter-reset: rank; padding: 0; margin: 1.6rem 0 0;
  display: grid; gap: 0.5rem 1.4rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
}
.ranks li {
  counter-increment: rank; position: relative; padding: 0.45rem 0 0.45rem 2.4rem;
  border-top: 1px solid var(--line); font-size: 0.95rem; color: var(--fg-soft);
}
.ranks li::before {
  content: counter(rank); position: absolute; left: 0; top: 0.55rem;
  width: 1.7rem; height: 1.7rem; border-radius: 50%; text-align: center; line-height: 1.7rem;
  font-family: var(--font-display); font-weight: 800; font-size: 0.8rem;
  color: var(--hm-lift); background: var(--hm-lake);
}
.ranks b {
  display: block; font-family: var(--font-display); font-weight: 800; font-size: 1rem;
  color: var(--fg);
}

/* ── Accounts ────────────────────────────────────────────────────────────────────────────
   Two columns of what is true today, then the direction. Plain tiles: this section is a
   promise about the future, and a promise should not be dressed up. */

.accounts-now { margin-top: 2rem; }
.accounts-now .tile ul { margin: 0.4rem 0 0; padding-left: 1.1rem; font-size: 0.95rem; }
.accounts-now .tile li { margin-bottom: 0.35rem; }
.accounts-now .tile li:last-child { margin-bottom: 0; }

/* ── Sub-page top bar ────────────────────────────────────────────────────────────────────
   A page about one product says so under the wordmark, so the mark still means "Two Loons,
   home" and the word beside it says where you are. */

.topbar-here {
  font-family: var(--font-display); font-weight: 800; font-size: 0.84rem;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-soft);
  margin: 0; padding-left: 0.9rem; border-left: 2px solid var(--line);
}
.topbar-mark { display: flex; align-items: center; gap: 0.9rem; text-decoration: none; }
@media (max-width: 640px) { .topbar-here { display: none; } }

/* One real photo-sized shot rather than a row: the Human Mode page opens on it. */
.screens-lead { justify-content: center; margin-top: 0; }
.screens-lead .phone { flex-basis: clamp(240px, 34vw, 330px); }

/* Four positions rather than six: at the full measure six wrap 3+3 and four wrap 3+1, and a
   lone fourth reads as a mistake. A wider basis makes four sit 2+2. */
.reasons-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr)); }

/* ── Human Mode on paper ─────────────────────────────────────────────────────────────────
   The app ships in its Paper look, and Paper is the one that tracks the rest of the Two Loons
   properties - so its page sits on the studio's own cream and borrows only Paper's accent, the
   sea green it uses for anything that goes. .band-sand (the Human Mode skin's ground) is kept
   for the day a page wants it; nothing uses it now. */

:root { --paper-go: #3e6b63; }
.band-paper { --accent: var(--paper-go); }
.band-paper .eyebrow, .band-paper .tile-tag { color: var(--paper-go); }
.band-paper .ranks li::before { background: var(--paper-go); }
.band-paper .tile-lead { border-color: var(--paper-go); }
.band-paper .ruled::after { background: var(--paper-go); }
.band-paper .phone img, .band-paper .strip img {
  border-color: rgba(43, 58, 66, 0.6); box-shadow: 0 14px 30px rgba(43, 58, 66, 0.22);
}
/* The studio card's window matches the Paper screens it now shows. */
.product-window.is-phone { background: #f4f1ea; }
.product-window.is-phone::after { background: linear-gradient(rgba(244, 241, 234, 0), #f4f1ea); }
