/* Ruler Games — design system.
   ---------------------------------------------------------------------------
   Direction: the page is an instrument, not a poster.

   The product's whole claim is that it is measured in real centimetres, so the
   site is built on that: a cm scale runs down the spine, measurements are set
   in mono because they are data, and the type is geometric and precise rather
   than cartoonish. A "kids' game" reflex would reach for a bouncy display face;
   that reads cheap and fights the premium half of the brief. The play comes
   from one hand-drawn marker mark and a live demo, not from the lettering.

   Palette is lifted straight off the printed sheets. Ink on paper dominates;
   green, orange and marker-red appear in small, deliberate hits. */

:root {
  /* --- paper and ink ---------------------------------------------------- */
  --paper: #f7f3e3;
  --paper-deep: #efe9d4;
  --ink: #2f281d;
  --graphite: #6b6152;
  --pencil: #b9ae97;
  --rule: rgba(47, 40, 29, 0.14);

  /* --- accents, used sparingly ------------------------------------------ */
  --marker: #d8452f;   /* whiteboard marker red — annotation, never furniture */
  --fairway: #4a8a4b;
  --leaf: #bcdd77;
  --sand: #f2e2ac;
  --water: #7cc0e6;
  --start: #f3b12e;

  /* --- type -------------------------------------------------------------- */
  /* Geometric sans for display: mid-century schoolroom, and it echoes the
     instrument. Warm serif for reading. Mono for anything measurable. */
  --display: 'Futura', 'Futura PT', 'Century Gothic', 'Avenir Next',
    'Trebuchet MS', system-ui, sans-serif;
  --body: 'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua',
    Georgia, serif;
  --mono: 'SF Mono', ui-monospace, 'Menlo', 'Consolas', monospace;

  --spine: 58px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --measure: 1180px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  padding-left: var(--spine);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 18px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

/* The 15mm grid of the printed sheet, whispered under everything. */
body::before {
  content: '';
  position: fixed;
  inset: 0 0 0 var(--spine);
  pointer-events: none;
  z-index: 0;
  background-image:
    linear-gradient(to right, rgba(47, 40, 29, 0.035) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(47, 40, 29, 0.035) 1px, transparent 1px);
  background-size: 56px 56px;
}

/* ---------------------------------------------------------------------------
   The spine: a centimetre scale down the left edge.
   Not decoration — it is the one true thing about this product, standing where
   a nav rail or a progress bar would otherwise go.
   --------------------------------------------------------------------------- */

.spine {
  position: fixed;
  left: 0; top: 0; bottom: 0;
  width: var(--spine);
  z-index: 5;
  border-right: 1px solid var(--rule);
  background: var(--paper-deep);
  /* Minor ticks every 11px, major every 55px. */
  background-image:
    repeating-linear-gradient(to bottom, var(--pencil) 0 1px, transparent 1px 11px),
    repeating-linear-gradient(to bottom, var(--ink) 0 1px, transparent 1px 55px);
  background-size: 9px 100%, 17px 100%;
  background-position: right 0 top 0, right 0 top 0;
  background-repeat: repeat-y;
}

.spine-label {
  position: absolute;
  top: 14px; left: 0; right: 8px;
  text-align: right;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  color: var(--graphite);
}

/* A ball travelling down the scale as you read. Chrome/Edge honour the scroll
   timeline; elsewhere it simply rests at the top, which is fine. */
.spine-ball {
  position: absolute;
  left: 9px;
  top: 40px;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--paper);
  border: 2px solid var(--ink);
}
@supports (animation-timeline: scroll()) {
  .spine-ball {
    animation: spineTravel linear both;
    animation-timeline: scroll(root block);
  }
  @keyframes spineTravel {
    from { top: 40px; }
    to   { top: calc(100vh - 40px); }
  }
}

@media (max-width: 760px) {
  :root { --spine: 0px; }
  .spine { display: none; }
}

/* ---------------------------------------------------------------------------
   Type
   --------------------------------------------------------------------------- */

h1, h2, h3 {
  font-family: var(--display);
  overflow-wrap: break-word;
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin: 0 0 0.55em;
}

h1 { font-size: clamp(2rem, 7.2vw, 4.6rem); }
h2 { font-size: clamp(1.55rem, 4vw, 3rem); letter-spacing: -0.02em; }
h3 {
  font-size: 1.06rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  line-height: 1.25;
}

p { margin: 0 0 1.1em; max-width: 62ch; }
a { color: inherit; }
strong { font-weight: 600; }

/* Measurements are data, so they are set as data — everywhere, without
   exception. This is the rule that keeps the page honest. */
.m {
  font-family: var(--mono);
  font-size: 0.9em;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

.eyebrow {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--graphite);
  margin: 0 0 1.4rem;
}

.lede {
  font-size: clamp(1.1rem, 1.6vw, 1.32rem);
  color: var(--graphite);
  line-height: 1.55;
}

/* The single hand-drawn moment on the page: a marker stroke under one phrase.
   It appears twice on the whole site. Any more and it stops being a gesture. */
.drawn {
  position: relative;
  white-space: nowrap;
}
.drawn::after {
  content: '';
  position: absolute;
  left: -0.04em; right: -0.04em;
  bottom: -0.16em;
  height: 0.2em;
  background: no-repeat center/100% 100%
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 12' preserveAspectRatio='none'%3E%3Cpath d='M2 8 C34 3 58 10 92 6 C126 2 154 9 198 4' fill='none' stroke='%23d8452f' stroke-width='4' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------------- */

.wrap { width: min(100% - var(--gutter) * 2, var(--measure)); margin-inline: auto; }
.band { padding-block: clamp(2.75rem, 5.5vw, 4.75rem); }
.band + .band { border-top: 1px solid var(--rule); }

/* Section heads sit in a narrow left rail, content to the right. The asymmetry
   keeps the reading column at a sane width without centring everything. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 17.5rem) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: start;
}
@media (max-width: 860px) { .split { grid-template-columns: minmax(0, 1fr); } }

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

.masthead {
  display: flex;
  align-items: flex-start;
  gap: 2rem;
  padding-block: 1.6rem 1.2rem;
}

/* Name over maker's mark. A container rather than one big link, because the
   byline credits sunnyheartsclub.com and an <a> cannot legally nest inside
   another <a> — the browser silently unnests them and you get two siblings and
   a layout you did not ask for. Aligned to flex-start so the nav sits level
   with the logo and not with the byline under it. */
.brand {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  align-items: flex-start;
}

/* Sized by height so the artwork's aspect ratio decides the width. The mark is
   an SVG, so this is a resolution-independent crop of nothing — it is simply
   drawn at whatever size we ask for. */
.wordmark {
  display: block;
  text-decoration: none;
}
.wordmark img {
  display: block;
  height: 2.1rem;
  width: auto;
}

.masthead nav {
  margin-left: auto;
  display: flex;
  align-items: baseline;
  gap: 1.6rem;
}
.masthead nav a {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--graphite);
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
}
.masthead nav a:hover { color: var(--ink); border-bottom-color: var(--ink); }
@media (max-width: 700px) {
  .masthead { flex-wrap: wrap; gap: 0.8rem 1.2rem; }
  .masthead nav { margin-left: 0; width: 100%; gap: 1.1rem; }
}

/* ---------------------------------------------------------------------------
   Actions
   --------------------------------------------------------------------------- */

.actions { display: flex; gap: 0.9rem; flex-wrap: wrap; align-items: center; }

.action {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--mono);
  font-size: 0.82rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.85rem 1.35rem;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.action:hover { background: var(--ink); color: var(--paper); }
.action-filled { background: var(--start); border-color: var(--ink); }
.action-filled:hover { background: var(--ink); color: var(--paper); }
.action-quiet { border-color: var(--rule); color: var(--graphite); }
.action-quiet:hover { background: transparent; border-color: var(--ink); color: var(--ink); }

a:focus-visible, button:focus-visible {
  outline: 2px solid var(--marker);
  outline-offset: 3px;
}

/* ---------------------------------------------------------------------------
   The backdrop — a real generated course, behind everything
   ---------------------------------------------------------------------------
   Not a texture: it is hole 3 of an actual course, rendered by the same engine
   that prints the sheets. It proves the product before a word is read. The wash
   over it is the paper colour, weighted toward the left, so the headline never
   has to compete with a bunker.
   --------------------------------------------------------------------------- */

.backdrop {
  position: absolute;
  inset: 0 0 auto 0;
  height: min(148vh, 1500px);
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

/* Sized by HEIGHT, not width: the sheet is portrait and the viewport is not, so
   filling the width crops it to an unreadable strip of green. Standing a whole
   hole up behind the copy is what makes it read as a real course. */
.backdrop-art {
  position: absolute;
  top: -13%;
  right: -7%;
  left: auto;
  height: 126%;
  width: auto;
  opacity: 0.38;
  transform: rotate(-1.8deg);
  will-change: transform;
}

/* Paper laid back over the artwork: opaque where the words are, thinning to
   the right, and closing entirely before the next section starts. */
.backdrop-wash {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(101deg,
      rgba(247, 243, 227, 0.96) 10%,
      rgba(247, 243, 227, 0.72) 34%,
      rgba(247, 243, 227, 0.34) 64%,
      rgba(247, 243, 227, 0.22) 100%),
    linear-gradient(to bottom,
      transparent 46%,
      rgba(247, 243, 227, 0.7) 76%,
      var(--paper) 96%);
}

/* Parallax, where the browser can do it on the compositor. Elsewhere the
   backdrop simply sits still, which is no loss. */
@supports (animation-timeline: scroll()) {
  .backdrop-art {
    animation: courseDrift linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 130vh;
  }
  @keyframes courseDrift {
    from { transform: translateY(0) rotate(-1.6deg); }
    to   { transform: translateY(-11%) rotate(-1.6deg); }
  }
}

@media (max-width: 760px) {
  /* On a phone the artwork is mostly off-screen and only costs bandwidth. */
  .backdrop { display: none; }
}

/* An absolutely-positioned element paints above static in-flow siblings no
   matter what a z-index of 0 suggests, so the content sections have to be
   positioned themselves to sit on top of the artwork. */
header, footer { position: relative; z-index: 1; }
.hero, .band { position: relative; z-index: 1; }

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

.hero { padding-block: clamp(1.25rem, 3vw, 2.25rem) clamp(2.25rem, 5vw, 3.75rem); }
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
}
@media (max-width: 940px) { .hero-grid { grid-template-columns: minmax(0, 1fr); } }

.hero h1 { margin-bottom: 0.45em; }
.hero .lede { margin-bottom: 2rem; }

/* An aside in the maker's own voice. Set apart by a rule and a hanging
   attribution rather than by a box, so it reads as a note, not a testimonial
   card. */
.aside {
  margin-top: 2.4rem;
  padding-left: 1.4rem;
  border-left: 2px solid var(--marker);
}
.aside p {
  font-size: 1.05rem;
  line-height: 1.5;
  margin-bottom: 0.5rem;
}
.aside cite {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--graphite);
  font-style: normal;
}

/* Plain, unglamorous reassurance — the kit list and the timings live here,
   well below the hook, where a buyer goes looking for them. */
.checklist { list-style: none; padding: 0; margin: 0; }
.checklist li {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: 0.6rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.98rem;
}
.checklist li::before {
  content: '';
  width: 9px; height: 9px;
  margin-top: 0.55em;
  border: 1.5px solid var(--ink);
}

/* ---------------------------------------------------------------------------
   The sheet — the demo sits on a facsimile of the printed article
   --------------------------------------------------------------------------- */

.sheet {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 12px 12px 0;
  box-shadow: 0 1px 0 var(--rule), 0 18px 40px -28px rgba(47, 40, 29, 0.55);
}
/* The faint sheen of a laminated page, caught at an angle. */
.sheet::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(104deg, rgba(255,255,255,0) 40%, rgba(255,255,255,0.16) 50%, rgba(255,255,255,0) 60%);
}
.sheet-caption {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 0.1rem 0.8rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--graphite);
}

/* ---------------------------------------------------------------------------
   Editorial listings — games and rules
   --------------------------------------------------------------------------- */

.entry {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr) auto;
  gap: clamp(1rem, 3vw, 2.2rem);
  align-items: start;
  padding: 1.9rem 0;
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
}
.entry:first-of-type { border-top: 1px solid var(--ink); }
.entry:hover .entry-name { color: var(--fairway); }
.entry-name {
  font-family: var(--display);
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin: 0 0 0.35rem;
  transition: color 0.15s ease;
}
.entry-glyph { color: var(--fairway); }
.entry p { margin: 0 0 0.5rem; font-size: 0.98rem; }
.entry-teach {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--graphite);
}
.entry-go {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--graphite);
  white-space: nowrap;
}
.entry-muted { opacity: 0.55; }
@media (max-width: 720px) {
  .entry { grid-template-columns: 3rem minmax(0, 1fr); }
  .entry-go { display: none; }
}

/* Numbered because the rules genuinely are a sequence — you cannot roll before
   you have called the driver. Nothing else on the page gets numbers. */
.steps { counter-reset: step; display: grid; gap: 0; }
.step {
  counter-increment: step;
  display: grid;
  grid-template-columns: 3.2rem minmax(0, 1fr);
  gap: 1.4rem;
  padding: 1.35rem 0;
  border-bottom: 1px solid var(--rule);
}
.step:first-child { border-top: 1px solid var(--ink); }
.step::before {
  content: counter(step);
  font-family: var(--mono);
  font-size: 0.82rem;
  color: var(--marker);
  padding-top: 0.32rem;
}
.step h3 { margin-bottom: 0.25rem; }
.step p { margin: 0; font-size: 0.97rem; color: var(--graphite); }

/* Alternatives, not a sequence — so they get a label rather than a number.
   Numbering these would imply you do one and then the other. */
.ways { display: grid; }
.way {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  gap: 1.4rem;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--rule);
}
.way:first-child { border-top: 1px solid var(--ink); }
.way-tag {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--marker);
  padding-top: 0.34rem;
}
.way h3 { margin-bottom: 0.3rem; }
.way p { margin: 0; font-size: 0.97rem; color: var(--graphite); }
@media (max-width: 620px) { .way { grid-template-columns: minmax(0, 1fr); gap: 0.4rem; } }

/* ---------------------------------------------------------------------------
   Terrain key
   --------------------------------------------------------------------------- */

.key { display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); gap: 0; }
.key-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0.9rem 0.6rem 0;
  border-bottom: 1px solid var(--rule);
}
.key-sw {
  width: 30px; height: 30px;
  flex: 0 0 auto;
  border: 1px solid rgba(47,40,29,0.4);
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 0.74rem;
}
.key-name { font-family: var(--display); font-weight: 600; font-size: 0.88rem; letter-spacing: 0.02em; }
.key-note { font-size: 0.78rem; color: var(--graphite); line-height: 1.3; }

/* ---------------------------------------------------------------------------
   Moments — what actually happens at the table
   --------------------------------------------------------------------------- */

.moments { display: grid; gap: 0; }
.moment {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  padding: 1.6rem 0;
  border-bottom: 1px solid var(--rule);
}
.moment:first-child { border-top: 1px solid var(--ink); }
.moment h3 {
  font-size: clamp(1.3rem, 2.4vw, 1.85rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  margin-bottom: 0.4rem;
}
.moment p { margin: 0; color: var(--graphite); }

/* ---------------------------------------------------------------------------
   Worlds — the themes, which are the thing people actually want more of
   --------------------------------------------------------------------------- */

.worlds {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: clamp(1rem, 2.5vw, 1.8rem);
}
.world { display: block; text-decoration: none; }
.world-plate {
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 7px;
  box-shadow: 0 1px 0 var(--rule), 0 14px 30px -24px rgba(47, 40, 29, 0.55);
  overflow: hidden;
  transition: transform 0.18s ease;
}
.world:hover .world-plate { transform: translateY(-3px); }
.world-plate img {
  display: block;
  width: 100%;
  height: 230px;
  object-fit: cover;
  object-position: 50% 32%;
}
.world-name {
  font-family: var(--display);
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  margin: 0.8rem 0 0.15rem;
}
.world-note { font-size: 0.9rem; color: var(--graphite); margin: 0; }

.world-soon .world-plate {
  display: grid;
  place-items: center;
  height: 244px;
  border-style: dashed;
  border-color: var(--pencil);
  box-shadow: none;
  background: transparent;
}
.world-soon span {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--pencil);
  text-align: center;
  padding: 0 1rem;
}

/* ---------------------------------------------------------------------------
   Evidence — the solver's own working, shown rather than described
   --------------------------------------------------------------------------- */

.evidence {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: clamp(1.5rem, 3vw, 2.6rem);
  align-items: start;
}
@media (max-width: 780px) { .evidence { grid-template-columns: minmax(0, 1fr); } }

.evidence-plate {
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: 9px;
  box-shadow: 0 1px 0 var(--rule), 0 16px 34px -26px rgba(47, 40, 29, 0.5);
}
.evidence-plate img { display: block; width: 100%; height: auto; }

.evidence-scale {
  display: flex;
  margin-top: 0.85rem;
  border: 1px solid var(--rule);
}
.evidence-scale i { flex: 1; height: 11px; }

.evidence-ends {
  display: flex;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--graphite);
  margin-top: 0.35rem;
}

/* ---------------------------------------------------------------------------
   Pull quote — the one place the page raises its voice
   --------------------------------------------------------------------------- */

.pull {
  font-family: var(--display);
  font-size: clamp(1.4rem, 3vw, 2.15rem);
  line-height: 1.16;
  letter-spacing: -0.02em;
  max-width: 22ch;
  margin: 0;
}
.pull-note { font-size: 0.95rem; color: var(--graphite); max-width: 46ch; }

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

footer {
  border-top: 1px solid var(--ink);
  padding-block: 2.4rem 4rem;
  font-size: 0.88rem;
  color: var(--graphite);
}
footer p { max-width: 54ch; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .spine-ball, .backdrop-art { animation: none; }
}

/* ---------------------------------------------------------------------------
   Shop, accounts and downloads
   ---------------------------------------------------------------------------
   The storefront reuses the marketing page's type, buttons and world cards. The
   only genuinely new furniture is a form and a list of files, because until
   there was something to buy the site had neither.
   --------------------------------------------------------------------------- */

.stack {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  align-items: flex-start;
  max-width: 26rem;
  margin-top: 1.6rem;
}

.stack label {
  display: block;
  width: 100%;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--pencil);
}

.stack input {
  width: 100%;
  margin-top: 0.4rem;
  padding: 0.7rem 0.8rem;
  font: inherit;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--ink);
  border-radius: 0;
}

/* The marker-drawn look the sheets have, applied to focus so keyboard users get
   the same emphasis a whiteboard pen would give. */
.stack input:focus-visible {
  outline: 2px solid var(--flag);
  outline-offset: 1px;
}

.sheet-list {
  list-style: none;
  padding: 0;
  margin: 1.6rem 0 0;
}

.sheet-list li {
  padding: 1rem 0;
  border-top: 1px solid var(--rule);
  line-height: 1.6;
}

.sheet-list li:last-child { border-bottom: 1px solid var(--rule); }

.muted {
  color: var(--pencil);
  font-family: var(--mono);
  font-size: 0.78rem;
}

/* ---------------------------------------------------------------------------
   The maker's mark — "by sunnyheartsclub"
   ---------------------------------------------------------------------------
   Sits under the wordmark in the masthead, not under the headline: it is
   attribution for the name above it, and putting it there stops it competing
   with the hero for the first thing you read. Deliberately quiet — the game is
   what is being sold, the brand is the reassurance underneath it.

   Laid out as a flex row with a fixed-height logo slot, so dropping a proper
   wordmark in beside the text will not shift the words or reflow the header.
   --------------------------------------------------------------------------- */

.byline {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: lowercase;
  color: var(--pencil);
}

/* The credit is a real link out to the parent brand's shop. Underlined only on
   hover so it stays a maker's mark at rest rather than reading as navigation. */
.byline a {
  color: var(--ink);
  letter-spacing: 0.1em;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.byline a:hover,
.byline a:focus-visible {
  border-bottom-color: var(--ink);
}

/* Logo slot. Height-constrained so any aspect ratio drops in cleanly. At the
   byline's own type size the badge collapses to a coloured dot, so it gets a
   little more room than the words beside it — still quiet, but legible as a
   mark rather than a speck. */
.byline-mark {
  height: 1.15rem;
  width: auto;
  display: block;
  flex-shrink: 0;
}

@media (max-width: 760px) {
  .byline { font-size: 0.58rem; letter-spacing: 0.1em; }
  .byline-mark { height: 1rem; }
}

/* ---------------------------------------------------------------------------
   The worlds backdrop — the page becomes the world you are reading about
   ---------------------------------------------------------------------------
   Fixed behind everything, holding one layer per world. Scrolling the worlds
   section fades the matching course up behind the cards, so the difference
   between a lagoon and an ash plain is something you feel at full size rather
   than infer from a thumbnail.

   Sits at z-index 0; the content sections are positioned at z-index 1 and paint
   over it. The wash on top is the paper colour again, because a course at any
   readable opacity will still fight body text without it.
   --------------------------------------------------------------------------- */

.world-backdrop {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  /* Nothing to see until a world card is actually in view. */
  opacity: 0;
  transition: opacity 520ms ease;
}

.world-backdrop.is-lit { opacity: 1; }

.wb {
  position: absolute;
  top: 50%;
  left: 50%;
  /* Sized by height: the sheet is portrait and the viewport is not, so filling
     the width crops it to a strip of green with no course legible in it. */
  height: 132vh;
  width: auto;
  opacity: 0;
  transform: translate(-50%, -50%) scale(1.04) rotate(-1.6deg);
  transition: opacity 620ms ease, transform 900ms ease;
  will-change: opacity, transform;
}

/* Each world settles slightly as it arrives, so a swap reads as a change of
   place rather than a crossfade between two flat pictures. */
.wb.is-on {
  opacity: 0.34;
  transform: translate(-50%, -50%) scale(1) rotate(-1.6deg);
}

.world-backdrop-wash {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg,
      rgba(247, 243, 227, 0.94) 8%,
      rgba(247, 243, 227, 0.66) 38%,
      rgba(247, 243, 227, 0.42) 72%,
      rgba(247, 243, 227, 0.5) 100%);
}

@media (max-width: 760px) {
  /* On a phone one card fills the screen anyway, so the art can come up
     stronger — and the cards themselves are already carrying these files. */
  .wb { height: 108vh; }
  .wb.is-on { opacity: 0.26; }
}

@media (prefers-reduced-motion: reduce) {
  /* The script bails out too; this is belt and braces. */
  .world-backdrop { display: none; }
}


/* ---------------------------------------------------------------------------
   Courses being designed
   ---------------------------------------------------------------------------
   Cards rather than list rows, because this is the one part of the page that is
   about to change. As plain headings it read like another section of history —
   indistinguishable from the sheets below it, which are finished and static.
   A card with a border and a pulsing dot says "this is happening now" without
   any copy having to claim it. */
.job-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 0.9rem;
  margin-top: 1rem;
}

.job-card {
  padding: 1rem 1.05rem 1.1rem;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: rgba(251, 248, 238, 0.75);
}

/* The one being worked on right now gets the tee orange and a live dot. */
.job-card--working {
  border-color: var(--start);
  background: rgba(243, 177, 46, 0.09);
}

.job-card--failed {
  border-color: var(--marker);
  background: rgba(216, 69, 47, 0.06);
}

.job-card__world {
  margin: 0;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 600;
}

.job-card__state {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0.35rem 0 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
}

.job-card__note {
  margin: 0.35rem 0 0;
  font-size: 0.88rem;
  color: var(--graphite);
}

/* A dot that breathes. The only moving thing on the page, which is the point —
   twenty seconds of nothing moving is what made the old version feel dead. */
.job-card__pulse {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--marker);
  flex-shrink: 0;
  animation: job-pulse 1.4s ease-in-out infinite;
}

@keyframes job-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.35; transform: scale(0.75); }
}

@media (prefers-reduced-motion: reduce) {
  .job-card__pulse { animation: none; }
}
