/* Catkuro marketing site.
 *
 * Its own identity, deliberately not the sibling app's: this one sells a
 * story about adopting cats, so the page is warm cream, rounded, and built
 * around the nine cat colors rather than a difficulty ramp. Whimsical
 * without being childish means generous type, real spacing, and color used
 * in flat confident blocks rather than in gradients and glitter.
 *
 * The wordmark is the app's own: Nunito at weight 900, navy "Cat" and a
 * coral gradient on "kuro". Keep it in step with CatkuroWordmark in
 * lib/src/widgets/.
 */

@font-face {
  font-family: "Brand Rounded";
  src: url("../fonts/nunito-variable.ttf") format("truetype-variations");
  font-weight: 200 1000;
  font-display: swap;
}

@font-face {
  font-family: "Notes Hand";
  src: url("../fonts/notes-hand.ttf") format("truetype");
  font-display: swap;
}

:root {
  --cream: #fff6e4;
  --cream-deep: #f7f1e6;
  --paper: #fffdf7;
  --ink: #07132d;
  --ink-soft: #46506a;
  --line: #e7dcc6;

  --pip: #ffc225;
  --beans: #ff4868;
  --clementine: #ff8400;
  --juniper: #1faa1f;
  --rusty: #d83320;
  --marina: #08a9b2;
  --storm: #0870e5;
  --shadow: #7b2bc5;
  --onyx: #24252d;

  /* The fur colors above are for cat art. These are the same hues held down
   * until each clears 4.5:1 on white, for anything shaped like text: a digit
   * in a board cell, a figure, a label. The app draws the same distinction
   * (CatIdentity.color against CatIdentity.inkOn) and for the same reason,
   * which is that four of the nine cats are unreadable as words. */
  --pip-ink: #9b6f00;
  --beans-ink: #c9184a;
  --clementine-ink: #b75f00;
  --juniper-ink: #198919;
  --rusty-ink: #d83320;
  --marina-ink: #06838a;
  --storm-ink: #0870e5;
  --shadow-ink: #7b2bc5;
  --onyx-ink: #24252d;

  /* The board's ground, sampled from the app rather than guessed. The app
   * does not have one board color: a story chapter is forest green, the
   * daily is this plum, and other tiers run slate and brick. This is the
   * plum, chosen because the worked board sits on the pale blue band and a
   * dark green against cornflower blue is two cool mid-darks fighting each
   * other. A warm dark reads as a board on the blue and on the guides' cream
   * alike. Any of the app's real grounds is a legitimate swap; a color the
   * app does not use is not. */
  --board-ground: #452a44;

  /* The store screenshots' own four combos, so the page and the listing read
   * as one piece of design. Each combo is a pale half and a strong half.
   *
   * **Which half carries text is not a taste call.** Measured against navy
   * ink, the pale halves run 8.4:1 to 13.7:1, so they are the section
   * backgrounds. White on the strong halves only clears on purple (8.9:1);
   * pink, blue and green sit at 2.8, 4.2 and 3.6, so those are for blocks,
   * rules and ribbons that carry no small text, or for navy at large sizes.
   * Reversing that is how a colorful page becomes an unreadable one. */
  --pale-pink: #f9a5aa;
  --strong-pink: #fd6870;
  --pale-blue: #9bbce5;
  --strong-blue: #2e7cda;
  --pale-purple: #c3a2de;
  --strong-purple: #67299a;
  --pale-green: #cee5c5;
  --strong-green: #529835;

  --coral-a: #ff365f;
  --coral-b: #ff785a;

  --radius: 26px;
  --radius-lg: 40px;
  --shadow-soft: 0 18px 44px rgba(7, 19, 45, 0.10);
  --shadow-lift: 0 26px 60px rgba(7, 19, 45, 0.16);
  --measure: 68ch;
  --shell: 1180px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
  font-size: 18px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, .wordmark, .eyebrow, .btn, .nav a, .badge-row {
  font-family: "Brand Rounded", "Nunito", "Avenir Next", system-ui, sans-serif;
}

h1, h2, h3 { line-height: 1.08; letter-spacing: -0.02em; margin: 0 0 0.5em; }
h1 { font-size: clamp(2.6rem, 6vw, 4.2rem); font-weight: 900; }
h2 { font-size: clamp(1.9rem, 3.6vw, 2.9rem); font-weight: 900; }
h3 { font-size: 1.32rem; font-weight: 800; }
p { margin: 0 0 1.1em; max-width: var(--measure); }
img { max-width: 100%; height: auto; display: block; }
/* Grid and flex children default to min-width:auto, which lets a wide image
 * push its track past the container. Every layout here caps it. */
.hero-inner > *, .screens figure, .feature-grid > *, .clowder li { min-width: 0; }
a { color: #c2334c; }
a:hover { color: var(--ink); }

:focus-visible {
  outline: 3px solid var(--storm);
  outline-offset: 3px;
  border-radius: 8px;
}

.shell {
  width: min(100% - 2.5rem, var(--shell));
  margin-inline: auto;
}

.skip-link {
  position: absolute;
  left: -9999px;
  background: var(--ink);
  color: #fff;
  padding: 0.7rem 1.1rem;
  border-radius: 0 0 12px 0;
  z-index: 100;
}
.skip-link:focus { left: 0; top: 0; }

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--ink-soft);
  margin: 0 0 0.9rem;
}

/* ---------- Wordmark ------------------------------------------------- */

.wordmark {
  font-weight: 900;
  letter-spacing: -0.045em;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
  display: inline-block;
}
.wordmark .kuro {
  background: linear-gradient(90deg, var(--coral-a), var(--coral-b));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* `background-clip: text` paints the gradient only inside the element's
   * own background box, and the negative letter-spacing above is applied
   * after the final glyph too, so that box ends a fraction of an em short of
   * the "o" it has to fill. The result was a wordmark with its last letter
   * sliced down the right edge, at every size, on every page. The padding
   * gives the gradient the room the glyph actually occupies and the matching
   * negative margin gives the layout back, so nothing else moves. */
  padding-right: 0.06em;
  margin-right: -0.06em;
}

/* ---------- Header ---------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(255, 246, 228, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.header-inner {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  padding: 0.85rem 0;
}
.header-inner .wordmark { font-size: 1.75rem; }
.nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 1.5rem;
}
.nav a {
  text-decoration: none;
  color: var(--ink);
  font-weight: 800;
  font-size: 0.98rem;
}
.nav a:hover { color: #c2334c; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 900;
  font-size: 1.02rem;
  text-decoration: none;
  padding: 0.85rem 1.6rem;
  border-radius: 999px;
  border: 2px solid transparent;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.btn-primary {
  background: linear-gradient(90deg, var(--coral-a), var(--coral-b));
  color: #fff;
  box-shadow: 0 10px 24px rgba(255, 70, 104, 0.30);
}
.btn-primary:hover { transform: translateY(-2px); color: #fff; }
.btn-ghost { border-color: var(--ink); color: var(--ink); }
.btn-ghost:hover { background: var(--ink); color: var(--cream); }

/* ---------- Hero ------------------------------------------------------ */

.hero {
  position: relative;
  overflow: hidden;
  padding: clamp(3rem, 7vw, 5.5rem) 0 clamp(3rem, 6vw, 5rem);
}
.hero-dots {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
/* Polka dots rather than the four diagonal ribbons this shipped with (owner,
 * 2026-09-15), matching the Play feature graphic so the two front doors are
 * one picture. Few and far apart on purpose, at four sizes, so they read as
 * confetti behind the phones rather than as a pattern fill. Nothing legible
 * is printed over them. */
.hero-dots span {
  position: absolute;
  border-radius: 50%;
  opacity: 0.92;
}
/* Kept right of the copy column, where the phone collage sits: a dot behind
 * the lede would fight the text it is under. Sizes are viewport-relative so
 * the scatter keeps its proportions from a laptop down to a tablet. */
.hero-dots span:nth-child(1) {
  left: 45%; top: 3%;
  width: clamp(70px, 9vw, 132px); aspect-ratio: 1;
  background: var(--strong-pink);
}
.hero-dots span:nth-child(2) {
  left: 62%; top: 56%;
  width: clamp(54px, 7vw, 104px); aspect-ratio: 1;
  background: var(--strong-blue);
}
.hero-dots span:nth-child(3) {
  left: 52%; top: 84%;
  width: clamp(34px, 4vw, 62px); aspect-ratio: 1;
  background: var(--strong-green);
}
.hero-dots span:nth-child(4) {
  left: 78%; top: 4%;
  width: clamp(44px, 5.5vw, 84px); aspect-ratio: 1;
  background: var(--strong-purple);
}
.hero-dots span:nth-child(5) {
  left: 90%; top: 66%;
  width: clamp(64px, 8vw, 118px); aspect-ratio: 1;
  background: var(--strong-pink);
}
.hero-dots span:nth-child(6) {
  left: 95%; top: 22%;
  width: clamp(38px, 4.5vw, 70px); aspect-ratio: 1;
  background: var(--strong-green);
}
.hero-dots span:nth-child(7) {
  left: 68%; top: 86%;
  width: clamp(46px, 6vw, 90px); aspect-ratio: 1;
  background: var(--strong-purple);
}

/* Below the hero's two-column breakpoint the copy sits on top of the dots
 * rather than beside them, so they step back out of the way. */
@media (max-width: 900px) {
  .hero-dots span { opacity: 0.32; }
}

.hero-inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
.hero-copy .wordmark { font-size: clamp(3.4rem, 8vw, 5.6rem); display: block; }
.hero-tagline {
  font-family: "Brand Rounded", sans-serif;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: clamp(0.82rem, 1.6vw, 0.95rem);
  color: var(--beans-ink);
  margin: 0 0 0.7rem;
}
.hero-rule {
  width: min(100%, 320px);
  height: 14px;
  border-radius: 999px;
  background: var(--strong-pink);
  margin: 0.4rem 0 1.4rem;
}
.hero-copy h1 {
  font-size: clamp(1.9rem, 3.4vw, 2.7rem);
  margin-bottom: 0.6rem;
}
.hero-lede { font-size: 1.14rem; color: var(--ink-soft); }
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-top: 1.6rem;
}

.hero-phones {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: end;
  gap: 0.6rem;
}
.hero-phones img {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 22px 34px rgba(7, 19, 45, 0.20));
}
.hero-phones img:nth-child(2) { transform: translateY(-6%); }

.badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Both badges are sized by height, which is the only axis either store's
 * guidelines speak about and the only one that makes two differently
 * proportioned pills read as a matched pair. That is not by itself enough:
 * Google ships its badge with 41px of transparent clear space baked into all
 * four edges of a 646x250 canvas, so an equal CSS height used to render a
 * black pill about a third shorter than Apple's beside it. The asset in this
 * repo is cropped to its own ink (564x168) and the clear space is spacing in
 * the row instead, where it belongs. Re-export the badge and it has to be
 * cropped again. */
.badge-row img { height: 54px; width: auto; }
.store-badge { display: inline-block; transition: transform 0.18s ease; }
.store-badge:hover { transform: translateY(-2px); }
.store-note {
  font-size: 0.92rem;
  color: var(--ink-soft);
  margin-top: 0.9rem;
}

/* ---------- Sections -------------------------------------------------- */

section { padding: clamp(3.2rem, 7vw, 5.6rem) 0; }
.section-head { max-width: 46rem; margin-bottom: 2.6rem; }
.section-head p { color: var(--ink-soft); font-size: 1.08rem; }

.band-cream { background: var(--cream-deep); }
.band-paper { background: var(--paper); }
/* Full-width color. The pale halves only, because these carry body
 * copy; see the palette note at the top. */
.band-pink { background: var(--pale-pink); }
.band-blue { background: var(--pale-blue); }
.band-purple { background: var(--pale-purple); }
.band-green { background: var(--pale-green); }
.band-pink .eyebrow, .band-blue .eyebrow,
.band-purple .eyebrow, .band-green .eyebrow,
.band-pink .section-head p, .band-blue .section-head p,
.band-purple .section-head p, .band-green .section-head p { color: #2b3550; }

/* The site link color is a crimson tuned for cream. On the pale bands it is
 * both a clash and a contrast failure: 2.8:1 on the blue, which is under the
 * 4.5:1 body text needs. Each band gets a link drawn from its own hue,
 * darkened until it clears, so a link still reads as a link without fighting
 * the panel it sits on. */
.band-blue a { color: #0a2a5e; }
.band-pink a { color: #8c1027; }
.band-purple a { color: #3d1260; }
.band-green a { color: #1c5a3c; }
.band-pink a:hover, .band-blue a:hover,
.band-purple a:hover, .band-green a:hover { color: var(--ink); }
/* Those band colours are for links that are words. A filled button is not,
 * and `.band-green a` outranks `.btn-primary` on specificity, so the press
 * kit's download button came out with dark green text on coral. */
.band-pink .btn-primary, .band-blue .btn-primary,
.band-purple .btn-primary, .band-green .btn-primary,
.band-pink .btn-primary:hover, .band-blue .btn-primary:hover,
.band-purple .btn-primary:hover, .band-green .btn-primary:hover { color: #fff; }

/* ---------- Rule cards ------------------------------------------------ */

.rule-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.4rem;
}
.rule-card {
  background: var(--paper);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 1.8rem;
  position: relative;
}
.rule-card .step {
  font-family: "Brand Rounded", sans-serif;
  font-weight: 900;
  font-size: 2.6rem;
  line-height: 1;
  margin-bottom: 0.5rem;
}
.rule-card:nth-child(1) .step { color: var(--strong-blue); }
.rule-card:nth-child(2) .step { color: var(--strong-pink); }
.rule-card:nth-child(3) .step { color: var(--strong-purple); }
.rule-card p { margin-bottom: 0; color: var(--ink-soft); }

/* A small worked grid, drawn in CSS so it costs no image request.
 *
 * It is drawn to look like the app's own board rather than like a generic
 * table: one dark green ground with the white entry cells inset into it, and
 * a clue cell split by a hairline diagonal with the across sum above the line
 * and the down sum below it. That split is the thing a reader has to
 * recognise, because a single number in a corner does not say which run it
 * clues.
 *
 * The worked corner is 17-across crossing 16-down, which is the one example
 * rule 2 further up the page already spells out, so the two teach the same
 * move rather than two unrelated ones. The four cats it needs (Onyx, Shadow,
 * Storm and Rusty) are also the four whose own colors are legible as text
 * without help: Pip's gold measures 1.6:1 on a white cell and Clementine's
 * orange 2.5:1, so a board built on the first four cats would have needed
 * the darkened `--*-ink` values and lost the colors it was showing off.
 * Changing the digits here means re-checking both things: that the sums
 * still work, and that the cats they name can be read. */
.mini-board {
  display: grid;
  grid-template-columns: repeat(3, 62px);
  gap: 2px;
  width: max-content;
  max-width: 100%;
  background: var(--board-ground);
  border: 6px solid var(--board-ground);
  border-radius: 14px;
  margin: 2.4rem 0 1rem;
}
.mini-board > div {
  aspect-ratio: 1;
  position: relative;
  display: grid;
  place-items: center;
  font-family: "Brand Rounded", sans-serif;
  font-weight: 900;
  font-size: 1.45rem;
  background: var(--board-ground);
}
.mini-board .clue {
  background:
    linear-gradient(to bottom right,
      transparent calc(50% - 0.5px),
      rgba(255, 255, 255, 0.45) calc(50% - 0.5px),
      rgba(255, 255, 255, 0.45) calc(50% + 0.5px),
      transparent calc(50% + 0.5px)),
    var(--board-ground);
  color: #fff;
}
.mini-board .clue i {
  position: absolute;
  font-style: normal;
  font-size: 0.86rem;
  font-weight: 800;
}
.mini-board .clue .across { top: 5px; right: 7px; }
.mini-board .clue .down { bottom: 5px; left: 7px; }
.mini-board .cell { background: #fff; }
.mini-board .cell.rusty { color: var(--rusty-ink); }
.mini-board .cell.storm { color: var(--storm-ink); }
.mini-board .cell.shadow { color: var(--shadow-ink); }
.mini-board .cell.onyx { color: var(--onyx-ink); }
.mini-board-caption {
  font-size: 0.95rem;
  color: #2b3550;
}

/* ---------- Features -------------------------------------------------- */

/* Six cards, so three columns: `auto-fit` gave four across on a wide screen
 * and left the last two sitting alone under them, which reads as a row that
 * failed to fill rather than as a deliberate shape. Three and two both
 * divide six, and the wide promise card spans the full width under either. */
.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.4rem;
}
@media (max-width: 900px) {
  .feature-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .feature-grid { grid-template-columns: minmax(0, 1fr); }
}
.feature {
  border-radius: var(--radius);
  padding: 1.9rem;
  border: 2px solid transparent;
  background: var(--paper);
  box-shadow: var(--shadow-soft);
}
.feature h3 { margin-bottom: 0.45rem; }
.feature p { margin-bottom: 0; color: var(--ink-soft); font-size: 1rem; }
.feature .glyph {
  width: 62px;
  height: 62px;
  border-radius: 18px;
  display: grid;
  place-items: center;
  font-size: 1.9rem;
  margin-bottom: 1.1rem;
}
.feature.f-marina .glyph { background: var(--pale-blue); }
.feature.f-clementine .glyph { background: var(--pale-pink); }
.feature.f-beans .glyph { background: var(--pale-purple); }
.feature.f-juniper .glyph { background: var(--pale-green); }
.feature.f-shadow .glyph { background: var(--pale-purple); }
.feature.f-pip .glyph { background: var(--pale-pink); }

.feature-wide {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.8fr);
  gap: 2rem;
  align-items: center;
  background: var(--ink);
  color: var(--cream);
}
.feature-wide h3 { color: #fff; font-size: 1.9rem; }
.feature-wide p { color: rgba(255, 246, 228, 0.82); }
.feature-wide .promise {
  font-family: "Brand Rounded", sans-serif;
  font-weight: 900;
  font-size: clamp(1.6rem, 3vw, 2.3rem);
  line-height: 1.12;
  color: var(--pale-pink);
  margin: 0;
}

/* ---------- Clowder --------------------------------------------------- */

/* The column floor is set by the longest name, not by the art, and it has
 * to stay at 110px: the shell caps at 1180px, and anything wider than about
 * 115px cannot seat nine cards in one row, which strands the ninth cat on a
 * row of its own. At 110px the card's padding and borders leave 90px inside,
 * and "Clementine" measures 85px at 0.98rem, which is why it ran past its
 * box on the press page at exactly the width where nine first fit. At
 * 0.92rem it measures 80px. The gap is tightened so nine fit from about
 * 1092px rather than 1131px. The wrap rule is a backstop, not the fix: a
 * single word that has to break is still wrong, it is just contained. */
.clowder {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 0.8rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.clowder li {
  background: var(--paper);
  border: 2px solid var(--line);
  border-radius: 22px;
  padding: 1rem 0.5rem 0.9rem;
  text-align: center;
  min-width: 0;
}
/* The nine cat PNGs are not one shape: seven sit upright at about 0.75 wide
 * for every 1 tall, and Marina and Shadow are lying down at 1.30 and 1.55.
 * A fixed height plus `width: auto` looks like it preserves every one of
 * them, and does not, because the global `img { max-width: 100% }` then
 * overrides the computed width on exactly the two that are wider than their
 * column while the fixed height holds: both lying cats were squashed
 * horizontally and only those two. The box is the fixed thing here and the
 * art is fitted inside it, so a wide cat gets shorter rather than narrower. */
.clowder .portrait {
  height: 118px;
  display: block;
  margin-bottom: 0.5rem;
}
/* `object-fit: contain` against a box with both dimensions set, rather than a
 * fixed height and an auto width: a percentage max-height on a grid item does
 * not constrain reliably here, so the tall cats simply overflowed the box and
 * sat on top of their own names. This way the box is the fixed thing, the art
 * is letterboxed inside it, and no cat can be stretched on either axis. */
.clowder img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.clowder span {
  display: block;
  font-family: "Brand Rounded", sans-serif;
  font-weight: 800;
  font-size: 0.92rem;
  overflow-wrap: anywhere;
}

/* ---------- Screens --------------------------------------------------- */

/* Ten screens, laid out as two full rows of five rather than left to
 * `auto-fit`: with ten items an automatic track count lands on whatever the
 * viewport happens to allow, and a row of five followed by a row of three
 * and an orphan pair reads as a mistake. The counts below all divide ten. */
.screens {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1.5rem;
}
@media (max-width: 1080px) {
  .screens { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 1rem; }
}
@media (max-width: 860px) {
  .screens { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.2rem; }
}
.screens figure { margin: 0; }
.screens img {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 18px 30px rgba(7, 19, 45, 0.16));
}
.screens figcaption {
  font-family: "Brand Rounded", sans-serif;
  font-weight: 800;
  font-size: 0.98rem;
  text-align: center;
  margin-top: 0.8rem;
  color: var(--ink-soft);
}

/* ---------- Download -------------------------------------------------- */

.download-card {
  background: linear-gradient(135deg, var(--strong-blue), var(--strong-purple));
  border-radius: var(--radius-lg);
  padding: clamp(2.4rem, 5vw, 4rem);
  text-align: center;
}
.download-card h2 { margin-bottom: 0.4rem; }
.download-card h2 { color: #fff; }
.download-card .eyebrow { color: rgba(255, 255, 255, 0.86); }
.download-card p { margin-inline: auto; color: rgba(255, 255, 255, 0.92); }
.download-card .badge-row { justify-content: center; margin-top: 1.6rem; }

/* ---------- Guides and articles --------------------------------------- */

.page-hero {
  padding: clamp(2.6rem, 6vw, 4.4rem) 0 clamp(1.6rem, 3vw, 2.4rem);
  background: var(--cream-deep);
  border-bottom: 1px solid var(--line);
}
.page-hero p { color: var(--ink-soft); font-size: 1.1rem; }

.prose { padding: clamp(2.4rem, 5vw, 3.6rem) 0; }
.prose h2, .legal-content h2 { margin-top: 2.2rem; }
.prose h3, .legal-content h3 { margin-top: 1.6rem; }
.prose ul, .prose ol,
.legal-content ul, .legal-content ol {
  max-width: var(--measure);
  padding-left: 1.3rem;
}
.prose li, .legal-content li { margin-bottom: 0.5rem; }
/* The legal article is generated by tool/legal_tool/make_legal_pages.py and
 * carries no classes of its own, so it is styled through its wrapper. */
.legal-content h2 {
  font-size: 1.45rem;
  padding-top: 0.4rem;
  border-top: 2px solid var(--line);
}
.legal-content h2:first-of-type { border-top: 0; padding-top: 0; }
.prose table {
  border-collapse: collapse;
  width: 100%;
  max-width: var(--measure);
  margin: 1.4rem 0;
  font-family: "Brand Rounded", sans-serif;
  font-size: 0.98rem;
}
.prose th, .prose td {
  border: 1px solid var(--line);
  padding: 0.6rem 0.8rem;
  text-align: left;
}
.prose th { background: var(--cream-deep); font-weight: 800; }

.callout {
  background: var(--paper);
  border: 2px solid var(--line);
  border-left: 8px solid var(--strong-blue);
  border-radius: 18px;
  padding: 1.3rem 1.5rem;
  margin: 1.8rem 0;
  max-width: var(--measure);
}
.callout p:last-child { margin-bottom: 0; }
.callout strong { font-family: "Brand Rounded", sans-serif; }

.guide-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1.4rem;
  list-style: none;
  padding: 0;
  margin: 0;
}
.guide-cards li {
  background: var(--paper);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 1.7rem;
}
.guide-cards h2 { font-size: 1.35rem; margin-bottom: 0.4rem; }
.guide-cards p { font-size: 0.99rem; color: var(--ink-soft); }
.guide-cards a { font-weight: 800; font-family: "Brand Rounded", sans-serif; }

.notes-hand { font-family: "Notes Hand", Georgia, serif; }

/* ---------- Press ----------------------------------------------------- */

.press-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.3rem;
}
.press-card {
  background: var(--paper);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem;
}
.press-card h3 { margin-bottom: 0.4rem; }
.press-card p { font-size: 0.98rem; color: var(--ink-soft); }
.press-card p:last-child { margin-bottom: 0; }
.press-grid-spaced { margin-top: 2.2rem; }
.section-action { margin-top: 1.6rem; }

/* The Play feature graphic is authored at 1024x500, which is the store's
 * spec and also exactly one CSS pixel per image pixel at this width. Shown
 * any wider it upscales, and on a retina display even 1024 CSS px is 2048
 * device px, which is where the blur came from. So the box is capped at the
 * image's own width, and a 2x render (the same generator run at 2048x1000,
 * everything in it is proportional) is offered through srcset for screens
 * that can use it. */
.press-banner {
  display: block;
  width: 100%;
  max-width: 1024px;
  height: auto;
  margin-inline: auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}

.fact-table { width: 100%; max-width: 46rem; border-collapse: collapse; }
.fact-table th, .fact-table td {
  border-bottom: 1px solid var(--line);
  padding: 0.7rem 0.4rem;
  text-align: left;
  vertical-align: top;
}
.fact-table th {
  font-family: "Brand Rounded", sans-serif;
  font-weight: 800;
  width: 12rem;
}
/* Both store links print their full URL, and the Play one is long enough
 * to push the table wider than a phone. Let it break anywhere. */
.fact-table a { overflow-wrap: anywhere; }

.video-embed {
  width: 100%;
  max-width: 960px;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--ink);
  box-shadow: var(--shadow-soft);
}
.video-embed iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.screens-heading {
  font-size: 1.15rem;
  margin: 2.2rem 0 1rem;
}
.screens-heading:first-of-type { margin-top: 0; }
.screens figure a { display: block; }

/* ---------- Footer ---------------------------------------------------- */

.site-footer {
  background: var(--ink);
  color: rgba(255, 246, 228, 0.78);
  padding: 3rem 0 2.2rem;
}
.site-footer .wordmark { font-size: 1.9rem; color: var(--cream); }
.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: 2rem;
  align-items: start;
}
.site-footer p { color: rgba(255, 246, 228, 0.78); font-size: 0.98rem; }
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem;
  font-family: "Brand Rounded", sans-serif;
  font-weight: 700;
}
.site-footer a { color: var(--pale-pink); text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.footer-bottom {
  border-top: 1px solid rgba(255, 246, 228, 0.18);
  margin-top: 2.2rem;
  padding-top: 1.3rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem;
  justify-content: space-between;
  font-size: 0.9rem;
}

/* ---------- Responsive ------------------------------------------------ */

@media (max-width: 900px) {
  .hero-inner { grid-template-columns: 1fr; }
  .hero-phones { max-width: 480px; }
  .feature-wide { grid-template-columns: 1fr; }
  .footer-top { grid-template-columns: 1fr; }
  .nav a:not(.nav-guides):not(.nav-cta) { display: none; }
}

@media (max-width: 560px) {
  body { font-size: 17px; }
  .mini-board { grid-template-columns: repeat(3, 52px); }
  .demo-grid > * { width: 46px; height: 46px; font-size: 1.1rem; }
  .hero-phones { grid-template-columns: repeat(3, 1fr); gap: 0.35rem; }
  /* The header has to stay one line on a phone: at full size the call to
   * action wrapped to two and swallowed the bar. */
  .header-inner { gap: 0.8rem; }
  .header-inner .wordmark { font-size: 1.45rem; }
  .nav { gap: 0.9rem; }
  .nav a { font-size: 0.9rem; }
  .nav-cta.btn { padding: 0.55rem 1rem; font-size: 0.9rem; white-space: nowrap; }
  .badge-row img, .store-row img { height: 46px; }
}

/* ---------- Guide internals ------------------------------------------- */
/* The guide prose started as the sibling app's and has since been rewritten
 * end to end, which was not a style preference: two sites under one
 * publisher running the same paragraphs about the same puzzle are competing
 * for the same queries with duplicate copy, and only one of them wins. The
 * teaching is the same because the puzzle is; the words, the worked boards
 * and the voice are Catkuro's. Keep it that way when editing either site. */

.kicker {
  display: block;
  font-family: "Brand Rounded", sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--ink-soft);
  margin-bottom: 0.7rem;
}
.lede { font-size: 1.12rem; color: var(--ink-soft); }

/* The grid sits on the board's own dark ground, like the app's and like the
 * homepage's worked corner. Without it the white entry cells were white on
 * cream with a hairline border, which on a marketing page reads as an empty
 * table rather than as a puzzle. */
.demo-grid {
  display: grid;
  gap: 2px;
  margin: 1.6rem 0 0.6rem;
  width: max-content;
  max-width: 100%;
  background: var(--board-ground);
  border: 6px solid var(--board-ground);
  border-radius: 14px;
}
/* The children are `<span>`, not `<div>`. This selector used to say
 * `> div` and matched nothing at all, so every demo grid on every guide page
 * rendered as a loose line of numbers running off across the prose with no
 * cells, no blocks and no clue corners under them. Match on the child rather
 * than on its tag, so the markup can say whatever it likes. */
.demo-grid > * {
  width: 58px;
  height: 58px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  font-family: "Brand Rounded", sans-serif;
  font-weight: 900;
  font-size: 1.3rem;
  background: #fff;
  color: var(--ink);
  position: relative;
}
.demo-grid .cell-block { background: var(--board-ground); }
/* A clue cell is split by a hairline diagonal, with the across sum above the
 * line and the down sum below it, the way the app draws one. Without the
 * diagonal a reader cannot tell which run a lone corner number clues. */
.demo-grid .cell-clue {
  background:
    linear-gradient(to bottom right,
      transparent calc(50% - 0.5px),
      rgba(255, 255, 255, 0.45) calc(50% - 0.5px),
      rgba(255, 255, 255, 0.45) calc(50% + 0.5px),
      transparent calc(50% + 0.5px)),
    var(--board-ground);
  border-color: var(--board-ground);
  color: #fff;
  font-size: 0.82rem;
}
.demo-grid .cell-clue .across {
  position: absolute; top: 4px; right: 6px; font-style: normal; font-weight: 800;
}
.demo-grid .cell-clue .down {
  position: absolute; bottom: 4px; left: 6px; font-style: normal; font-weight: 800;
}
.demo-grid .cell-good { color: var(--juniper-ink); background: #f0faee; }
.demo-grid .cell-note {
  font-family: "Notes Hand", Georgia, serif;
  font-size: 0.95rem;
  color: var(--storm-ink);
  font-weight: 400;
}
.demo-caption {
  font-size: 0.95rem;
  color: var(--ink-soft);
  margin-bottom: 1.8rem;
}

.guide-note {
  background: var(--paper);
  border: 2px solid var(--line);
  border-left: 8px solid var(--strong-pink);
  border-radius: 18px;
  padding: 1.2rem 1.4rem;
  margin: 1.8rem 0;
  max-width: var(--measure);
}
.guide-note p:last-child { margin-bottom: 0; }

.combo-table {
  border-collapse: collapse;
  width: 100%;
  max-width: var(--measure);
  margin: 1.4rem 0;
  font-family: "Brand Rounded", sans-serif;
  font-size: 0.98rem;
}
.combo-table th, .combo-table td {
  border: 1px solid var(--line);
  padding: 0.55rem 0.75rem;
  text-align: left;
}
.combo-table th { background: var(--cream-deep); font-weight: 800; }
.combo-table tbody tr:nth-child(even) { background: rgba(247, 241, 230, 0.55); }

/* Between the full-width card grid above it and the full-width CTA below, a
 * single 68ch column reads as a layout fault rather than as a measure. The
 * block spans the section and splits into two columns instead, which keeps
 * the line length a reader can actually track. One column below the
 * breakpoint, where two would be about thirty characters wide. */
.guide-why { margin: 2.6rem 0 0; }
.guide-why h2 { max-width: none; }
.guide-why p { max-width: none; }
/* The columns go on the wrapper, not on each paragraph: per-paragraph
 * multicol makes every paragraph its own two-column block, so the section
 * reads as four stacked fragments instead of one piece of text flowing
 * down the left column and back up the right. */
@media (min-width: 900px) {
  .guide-why-body {
    columns: 2;
    column-gap: 3rem;
  }
  .guide-why-body p:first-child { margin-top: 0; }
  .guide-why-body p { break-inside: avoid-column; }
}

/* The margin has to be on both ends. On most guide pages the CTA sits inside
 * `.prose`, whose own bottom padding separated it from the footer; on the
 * combinations page it is a direct child of <main> instead, so it had nothing
 * under it and sat flush against the dark footer band. */
.guide-cta {
  background: linear-gradient(135deg, var(--strong-blue), var(--strong-purple));
  border-radius: var(--radius-lg);
  padding: clamp(2rem, 4vw, 3rem);
  margin-top: 3rem;
  margin-bottom: clamp(2.4rem, 5vw, 3.6rem);
  text-align: center;
}
.guide-cta h2 { margin-bottom: 0.4rem; color: #fff; }
.guide-cta .eyebrow { color: rgba(255, 255, 255, 0.86); }
.guide-cta p { margin-inline: auto; color: rgba(255, 255, 255, 0.92); }
.guide-cta a { color: #ffe7a8; }
.guide-cta .badge-row { justify-content: center; margin-top: 1.4rem; }
/* The guide CTAs write `store-row`, which nothing styled, so their badges
 * dropped out of the flex row and rendered at the PNG's own intrinsic width.
 * Style both names rather than only renaming the markup, because a guide
 * page added later will reach for whichever one it saw first. */
.store-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  align-items: center;
  justify-content: center;
  margin-top: 1.4rem;
}
.store-row img { height: 54px; width: auto; }

/* The whole card is one anchor, so the underline and link color have to be
 * pulled back off the body text or every description reads as a link. */
a.guide-card {
  display: block;
  background: var(--paper);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 1.7rem;
  text-decoration: none;
  color: var(--ink);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
a.guide-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-soft);
  border-color: var(--strong-blue);
}
.guide-card h2 { font-size: 1.35rem; margin-bottom: 0.4rem; color: #c2334c; }
.guide-card p {
  font-size: 0.99rem;
  color: var(--ink-soft);
  text-decoration: none;
  margin-bottom: 0;
}

/* ---------- Combinations reference ------------------------------------ */
/* website/guides/combinations.html is generated by
 * tool/site_tool/make_combinations_page.py from the same arithmetic the app
 * uses. Never hand-edit the page; change the generator and rerun it. */

.jump-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: 1.4rem;
}
.jump-row a {
  font-family: "Brand Rounded", sans-serif;
  font-weight: 800;
  font-size: 0.92rem;
  text-decoration: none;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--line);
  border-radius: 999px;
  padding: 0.4rem 0.95rem;
}
.jump-row a:hover { background: var(--ink); color: var(--cream); }

/* These are <section> elements inside the page's own section, so they were
 * picking up the page-level `section { padding: clamp(3.2rem, 7vw, 5.6rem) 0 }`
 * on top of their own margin. Two of those paddings plus a margin plus the
 * heading's own margin put roughly 260px of cream between the last row of one
 * run-length table and the heading of the next, which reads as the page
 * having ended. The padding is for full-width bands, not for a subsection. */
.combo-section {
  padding: 0;
  margin-bottom: 2.4rem;
}
.combo-section h2 { margin-top: 0; }
.combo-range { margin-bottom: 0; }
.combo-range { color: var(--ink-soft); font-size: 0.99rem; }
.table-scroll { overflow-x: auto; }
.combo { font-family: "Brand Rounded", sans-serif; font-weight: 700; }
.unique-row { background: rgba(155, 188, 229, 0.30); }
.unique-tag {
  font-family: "Brand Rounded", sans-serif;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  background: var(--strong-purple);
  color: #fff;
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
  margin-left: 0.4rem;
}
.always { font-weight: 800; color: var(--strong-purple); }
.combo-page h2 { font-size: 1.5rem; }
