/*
 * The comic system — the whole design.
 *
 * This is the ONLY stylesheet. theme.css, app.css and components.css are gone:
 * the editorial system they carried is not layered under this one, it was
 * deleted. Nothing here inherits from it, and there is no migration state left.
 *
 * Ground is white by request. That changed every ratio and every one was
 * re-measured rather than carried over — red on white is 6.11:1 where it was
 * 5.17:1 on the cream it replaces, and the bright red rose to 4.83:1, which is
 * what now lets small text sit on it at all.
 *
 * Yellow and cyan are FILLS and never words: 1.51:1 and 2.70:1 on white. Ink
 * sits on them instead, at 12.40:1 and 6.94:1.
 */

:root {
  /* ---- Ground and ink ---- */
  --paper: #FFFFFF;
  --ink: #16110D;                /* 18.75:1 on white */

  /* Text and fills. 6.11:1 on white; white on it is also 6.11:1. */
  --red: #C41019;

  /* Display type and large fills. 4.83:1 on white — legal at any size now. */
  --red-bright: #E01B24;

  /* Fills only. Never a word, a label or a mark on white. */
  --yellow: #FFCC00;
  --cyan: #1CA9DD;

  /*
   * The room the comic lies in. Two fills, because one was not enough: a single
   * flat tan read as a yellow background rather than as a surface. The wall is
   * behind, the desk is in front, and the edge between them is an ink rule.
   *
   * Both are grounds for ink and never for paper. Ink is 15.59:1 on the wall
   * and 10.44:1 on the desk; white is 1.62:1 on the desk and is never used
   * there. See the R04 block at the foot of this file for the two boundaries
   * that are carried by ink rules rather than by a change in fill.
   */
  --wall: #F4EADA;
  --desk: #DCBE8E;

  /*
   * The focus ring is two colours, and that is not decoration. No single hue
   * clears 3:1 against every ground here — a blue ring is 4.86 on paper and
   * 1.06 on the red fill, vanishing exactly on the buttons. Ink inside, yellow
   * outside: on white, yellow and cyan the ink layer carries it; on ink panels
   * and the red fill the yellow layer does. The layers are 12.40:1 apart.
   */
  --ring-inner: var(--ink);
  --ring-outer: var(--yellow);

  --font-display: "Bangers", "Impact", "Haettenschweiler", sans-serif;
  --font-letter: "Patrick Hand", "Comic Sans MS", "Segoe Print", cursive;

  --outline: 3px;
  --outline-heavy: 5px;

  /*
   * Halftone as a repeating radial gradient rather than an image or a filter.
   * A filter would be recomputed every frame during a page turn; this is
   * painted once and costs nothing while a leaf rotates.
   */
  --halftone: radial-gradient(rgba(22, 17, 13, 0.28) 1.1px, transparent 1.2px);
  --halftone-size: 7px 7px;

  --shadow-hard: 5px 5px 0 var(--ink);
  --shadow-hard-lg: 8px 8px 0 var(--ink);
}

/* ---------- Base ---------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  background-color: var(--paper);
  color: var(--ink);
  font-family: var(--font-letter);
  font-size: 1.0625rem;
  line-height: 1.55;
}

img,
svg {
  max-width: 100%;
}

/*
 * The `hidden` attribute has to beat every class in this file.
 *
 * The browser's own rule for [hidden] is display:none at UA specificity, so any
 * class here that sets display — .mobile-nav, .confirmation, .form-error —
 * silently overrides it and shows an element the markup declared hidden. That
 * is not hypothetical: the mobile menu appeared on desktop, and the contact
 * confirmation would have shown before the form was ever submitted, which is a
 * bug this project has already had and fixed once.
 */
[hidden] {
  display: none !important;
}

.illustration,
.illustration-inline {
  display: block;
  width: 100%;
  height: auto;
}

/*
 * Every illustration is a framed panel. The drawings themselves are still
 * line art, so an ink rule around them is what makes them read as comic
 * panels rather than as loose vector art on a page.
 */
/*
 * Capped in viewport units, because a leaf is exactly one viewport tall. At
 * full width these drawings are tall enough on their own to push the rest of a
 * spread off the page and force the leaf to scroll internally. object-fit
 * keeps the aspect ratio rather than squashing the artwork to fit.
 */
.illustration {
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  background-color: var(--paper);
  box-shadow: var(--shadow-hard);
  padding: 0.75rem;
  max-height: 38vh;
  object-fit: contain;
}

/* ---------- Focus ---------- */

/*
 * One indicator for the whole site, never removed — only replaced. outline
 * draws the inner layer and box-shadow the outer one, so neither costs a
 * layout change when focus moves.
 */
:focus-visible {
  outline: 3px solid var(--ring-inner);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--ring-outer);
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background-color: var(--yellow);
  color: var(--ink);
  font-family: var(--font-display);
  letter-spacing: 0.06em;
  border: var(--outline) solid var(--ink);
  padding: 0.6rem 1rem;
}

.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Grid ---------- */

/*
 * Twelve columns, unchanged in structure from what the markup expects: the
 * templates carry col-12 / lg-col-7 / md-col-5 and re-numbering them would mean
 * editing every page to no purpose. The skin is new; the scaffold is arithmetic.
 */
.site-grid {
  margin-left: auto;
  margin-right: auto;
  display: grid;
  width: 100%;
  max-width: 1440px;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: 1.25rem;
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}

@media (min-width: 768px) {
  .site-grid {
    column-gap: 2rem;
    padding-left: 2rem;
    padding-right: 2rem;
  }
}

@media (min-width: 1280px) {
  .site-grid {
    padding-left: 5rem;
    padding-right: 5rem;
  }
}

.col-12 { grid-column: span 12 / span 12; }

@media (min-width: 768px) {
  .md-col-2 { grid-column: span 2 / span 2; }
  .md-col-3 { grid-column: span 3 / span 3; }
  .md-col-5 { grid-column: span 5 / span 5; }
  .md-col-start-7 { grid-column-start: 7; }
}

@media (min-width: 1024px) {
  .lg-col-4 { grid-column: span 4 / span 4; }
  .lg-col-5 { grid-column: span 5 / span 5; }
  .lg-col-6 { grid-column: span 6 / span 6; }
  .lg-col-7 { grid-column: span 7 / span 7; }
  .lg-col-8 { grid-column: span 8 / span 8; }
  .lg-col-start-6 { grid-column-start: 6; }
  .lg-col-start-7 { grid-column-start: 7; }
}

.grid-start { align-items: start; }

/*
 * Blocks that sit directly inside .site-grid without a col-* class of their own.
 * Without an explicit span each lands in a single one-twelfth track and its
 * contents collapse to one word per line — which is exactly what happened to
 * the three cards before this rule existed. The markup relies on the
 * stylesheet to know they are full-width; nothing in the HTML says so.
 */
.card-grid,
.process-grid,
.aside-panel,
.illustration,
.footer-legal {
  grid-column: span 12 / span 12;
}

/* ---------- Spacing utilities ---------- */

.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-5 { margin-top: 1.25rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-7 { margin-top: 1.75rem; }
.mt-8 { margin-top: 2rem; }
.mt-9 { margin-top: 2.25rem; }
.mt-10 { margin-top: 2.5rem; }
.mt-11 { margin-top: 2.75rem; }
.mt-16 { margin-top: 4rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mb-10 { margin-bottom: 2.5rem; }
.mb-11 { margin-bottom: 2.75rem; }
.stack-3 > * + * { margin-top: 0.75rem; }
.stack-4 > * + * { margin-top: 1rem; }

/* ---------- Typography ---------- */

/*
 * Bangers letters anything that shouts — headings, numbers, button labels.
 * Patrick Hand letters anything that is read as speech. That split is the whole
 * type system, and it is why there are two faces and not five.
 */
.display-heading {
  font-family: var(--font-display);
  font-size: clamp(2.9rem, 8vw, 5.5rem);
  line-height: 0.95;
  letter-spacing: 0.015em;
  margin: 0;
  color: var(--ink);
  text-shadow: 4px 4px 0 var(--yellow);
}

.section-heading {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.6vw, 3.1rem);
  line-height: 1.02;
  letter-spacing: 0.02em;
  margin: 0;
  color: var(--ink);
}

.card-heading {
  font-family: var(--font-display);
  font-size: 1.5rem;
  letter-spacing: 0.03em;
  margin: 0;
  color: var(--ink);
}

/*
 * The eyebrow is a caption box, not a line of small caps. Ink on yellow is
 * 12.40:1 and it gives every section a comic caption in the corner, which is
 * where a comic puts its narration.
 */
.eyebrow {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--yellow);
  border: var(--outline) solid var(--ink);
  padding: 0.25rem 0.7rem;
  margin: 0;
  transform: rotate(-1.5deg);
}

/* The accent caption inverts: white on red, 6.11:1. */
.eyebrow-accent {
  background-color: var(--red);
  color: var(--paper);
}

.text-measure { max-width: 62ch; }

/* ---------- Speech bubbles ---------- */

/*
 * The prose of the site is speech. Every passage of running text is a bubble
 * with a tail, hand-lettered, because that is what the brief asks for and
 * because it is the only way a comic carries an argument.
 *
 * The tail is two triangles, not one: an ink triangle and a paper triangle a
 * little inside it, so the outline appears to continue around the tail. A
 * single triangle leaves the bubble looking punctured.
 */
.intro-copy,
.lead-copy,
.body-copy {
  position: relative;
  font-family: var(--font-letter);
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 22px;
  padding: 1rem 1.25rem;
  margin: 0;
  color: var(--ink);
}

.intro-copy {
  font-size: clamp(1.15rem, 2vw, 1.4rem);
  box-shadow: var(--shadow-hard);
}

.lead-copy { font-size: 1.1rem; }
.body-copy { font-size: 1.05rem; }

.intro-copy::before,
.lead-copy::before {
  content: "";
  position: absolute;
  left: 34px;
  bottom: calc(-1 * var(--outline) - 18px);
  border-left: 16px solid transparent;
  border-right: 10px solid transparent;
  border-top: 20px solid var(--ink);
}

.intro-copy::after,
.lead-copy::after {
  content: "";
  position: absolute;
  left: 39px;
  bottom: -13px;
  border-left: 11px solid transparent;
  border-right: 7px solid transparent;
  border-top: 15px solid var(--paper);
}

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

.site-header {
  background-color: var(--paper);
  border-bottom: var(--outline-heavy) solid var(--ink);
}

.header-bar {
  max-width: 1440px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 1.25rem;
}

@media (min-width: 768px) {
  .header-bar { padding: 0.85rem 2rem; }
}

.wordmark {
  font-family: var(--font-display);
  font-size: 1.7rem;
  letter-spacing: 0.03em;
  color: var(--ink);
  text-decoration: none;
}

.wordmark-dot { color: var(--red); }

.header-nav {
  display: none;
  gap: 1.5rem;
  align-items: center;
}

@media (min-width: 1024px) {
  .header-nav { display: flex; }
}

.nav-link {
  font-family: var(--font-letter);
  font-size: 1.1rem;
  color: var(--ink);
  text-decoration: none;
  position: relative;
}

.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  height: 3px;
  width: 0;
  background-color: var(--red);
  transition: width 0.15s ease;
}

.nav-link:hover::after { width: 100%; }

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

@media (min-width: 1024px) {
  .header-actions .button-compact { display: none; }
}

.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background-color: var(--paper);
  color: var(--ink);
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  cursor: pointer;
}

@media (min-width: 1024px) {
  .icon-button { display: none; }
}

.mobile-nav {
  display: grid;
  gap: 0.25rem;
  padding: 1rem 1.25rem 1.5rem;
  border-top: var(--outline) solid var(--ink);
  background-color: var(--paper);
}

.mobile-link {
  font-family: var(--font-letter);
  font-size: 1.15rem;
  color: var(--ink);
  text-decoration: none;
  padding: 0.5rem 0;
  border-bottom: 2px solid var(--ink);
}

/* ---------- Buttons ---------- */

/*
 * White on this red is 6.11:1, so the label is legal at any size. The shadow is
 * a hard offset block: nothing in a comic is blurred, and a soft shadow was the
 * single most editorial thing in the old system.
 */
.button-primary,
.button-outline {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-size: 1.15rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  padding: 0.7rem 1.3rem;
  cursor: pointer;
  box-shadow: var(--shadow-hard);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.button-primary {
  background-color: var(--red);
  color: var(--paper);
}

.button-outline {
  background-color: var(--paper);
  color: var(--ink);
}

/*
 * Hover keeps WHITE on the brighter red (4.83:1). Ink on --red-bright is
 * 3.89:1 — legal for a 24px-bold heading and illegal for a button label at this
 * size, which is an easy and invisible way to ship a hover that fails where the
 * resting state passed.
 */
.button-primary:hover {
  background-color: var(--red-bright);
  color: var(--paper);
}

.button-outline:hover { background-color: var(--yellow); }

.button-primary:hover,
.button-outline:hover {
  transform: translate(2px, 2px);
  box-shadow: 3px 3px 0 var(--ink);
}

.button-primary:active,
.button-outline:active {
  transform: translate(5px, 5px);
  box-shadow: 0 0 0 var(--ink);
}

.button-block { display: flex; width: 100%; }
.button-fit { align-self: flex-start; }
.button-compact { padding: 0.5rem 0.9rem; font-size: 1rem; }
.button-icon { width: 18px; height: 18px; flex-shrink: 0; }

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

.section-space {
  padding-top: 8vh;
  padding-bottom: 8vh;
}

.section-bordered { border-top: var(--outline-heavy) solid var(--ink); }

/*
 * A "muted" section is now a halftone one. There is no grey in this palette;
 * the tone that separated bands in the old system is a dot screen here.
 */
.section-muted {
  background-color: var(--paper);
  background-image: var(--halftone);
  background-size: var(--halftone-size);
}

.section-banded {
  border-top: var(--outline-heavy) solid var(--ink);
  border-bottom: var(--outline-heavy) solid var(--ink);
}

.preview-grid {
  padding-top: 3rem;
  padding-bottom: 3.5rem;
  row-gap: 2.5rem;
  align-items: center;
}

.hero-section {
  min-height: 620px;
  row-gap: 2.5rem;
  padding-top: 3.5rem;
  padding-bottom: 3.5rem;
  align-items: center;
}

/*
 * The closing band is the one ink panel on the site. White on it is 18.75:1 and
 * the amber mark is 8.67:1 — it is the only place a warm accent is legible, and
 * so the only place it appears.
 */
.dark-band {
  background-color: var(--ink);
  background-image: radial-gradient(rgba(255, 255, 255, 0.16) 1.1px, transparent 1.2px);
  background-size: var(--halftone-size);
  color: var(--paper);
  padding-top: 6vh;
  padding-bottom: 6vh;
}

.dark-band .section-heading { color: var(--paper); }

.dark-band .lead-copy,
.dark-band .body-copy {
  background-color: var(--ink);
  color: var(--paper);
  border-color: var(--paper);
}

.dark-band .lead-copy::before { border-top-color: var(--paper); }
.dark-band .lead-copy::after { border-top-color: var(--ink); }

.band-mark { color: var(--yellow); }
.band-copy { color: var(--paper); }

.dark-band .button-primary {
  border-color: var(--paper);
  box-shadow: 5px 5px 0 var(--paper);
}

/* ---------- Panels and cards ---------- */

.card-grid {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/*
 * Every card is a comic panel: hard outline, hard shadow, no radius to speak
 * of. The slight rotation alternates so a row of three does not read as a
 * spreadsheet.
 */
.paper-card {
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 1.35rem;
  box-shadow: var(--shadow-hard);
}

.card-grid > .paper-card:nth-child(odd) { transform: rotate(-0.7deg); }
.card-grid > .paper-card:nth-child(even) { transform: rotate(0.6deg); }

.card-icon {
  color: var(--red);
  width: 30px;
  height: 30px;
}

/* ---------- Process steps ---------- */

.process-grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .process-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .process-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.process-step {
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 1.15rem;
  box-shadow: var(--shadow-hard);
}

/*
 * The step number is a panel number, in the corner, the way a comic numbers
 * its panels. Ink on yellow, 12.40:1.
 */
.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.1rem;
  height: 2.1rem;
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--ink);
  background-color: var(--yellow);
  border: var(--outline) solid var(--ink);
  border-radius: 50%;
}

/* ---------- Checklist ---------- */

.checklist-row {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.7rem 0;
  border-bottom: 2px solid var(--ink);
}

/*
 * White tick on red, 6.11:1. Ink on the same red is 3.07:1 — legal for an icon
 * and visibly murky, which is the trap this palette sets twice.
 */
.checklist-mark,
.confirmation-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  border-radius: 50%;
  background-color: var(--red);
  color: var(--paper);
  border: var(--outline) solid var(--ink);
}

.checklist-mark svg,
.confirmation-mark svg {
  width: 16px;
  height: 16px;
}

/* ---------- Project fit ---------- */

.fit-card {
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 1.2rem;
  box-shadow: var(--shadow-hard);
}

.fit-label {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--cyan);
  border: 2px solid var(--ink);
  padding: 0.15rem 0.6rem;
  margin-bottom: 0.6rem;
}

/* ---------- Questions accordion ---------- */

.question {
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  background-color: var(--paper);
  box-shadow: var(--shadow-hard);
  margin-bottom: 1rem;
}

.question-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  text-align: left;
  font-family: var(--font-display);
  font-size: 1.25rem;
  letter-spacing: 0.02em;
  color: var(--ink);
  background: none;
  border: 0;
  padding: 1rem 1.15rem;
  cursor: pointer;
}

.question-chevron {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--red);
  transition: transform 0.25s ease;
}

.question-trigger[aria-expanded="true"] .question-chevron {
  transform: rotate(180deg);
}

/*
 * The answer is speech: hand-lettered, on a halftone ground so it reads as the
 * inside of a panel rather than as more page.
 */
.question-answer {
  font-family: var(--font-letter);
  font-size: 1.05rem;
  padding: 0 1.15rem 1.15rem;
  border-top: 2px dashed var(--ink);
  padding-top: 0.9rem;
  margin: 0;
}

/* ---------- Concept preview ---------- */

.preview-window {
  background-color: var(--paper);
  border: var(--outline-heavy) solid var(--ink);
  border-radius: 4px;
  box-shadow: var(--shadow-hard-lg);
  overflow: hidden;
}

.preview-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1rem;
  border-bottom: var(--outline) solid var(--ink);
  background-color: var(--yellow);
}

.preview-name {
  font-family: var(--font-display);
  font-size: 1.1rem;
  letter-spacing: 0.04em;
  color: var(--ink);
}

.preview-type {
  font-family: var(--font-display);
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
}

.preview-body { padding: 1.35rem; }

.preview-kicker {
  font-family: var(--font-display);
  font-size: 0.9rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--red);
  margin: 0 0 0.5rem;
}

.preview-tagline {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3vw, 2.3rem);
  line-height: 1.05;
  letter-spacing: 0.02em;
  color: var(--ink);
  margin: 0;
}

/*
 * The preview's own accent is set per colour direction and applied here. It is
 * always a filled control carrying white, which is why every direction in
 * theme's place below is dark enough to hold a white label.
 */
.preview-action {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-size: 1.05rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--paper);
  background-color: hsl(var(--preview-accent));
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  padding: 0.55rem 1rem;
  margin-top: 1.2rem;
  box-shadow: 4px 4px 0 var(--ink);
}

.preview-features {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: 1fr;
  margin-top: 1.35rem;
  padding-top: 1rem;
  border-top: 2px dashed var(--ink);
}

@media (min-width: 768px) {
  .preview-features { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.preview-feature {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
}

.preview-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  background-color: hsl(var(--preview-accent));
  border: 2px solid var(--ink);
}

.preview-idea {
  width: 100%;
  min-height: 7rem;
  font-family: var(--font-letter);
  font-size: 1.05rem;
  color: var(--ink);
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 0.8rem;
  resize: vertical;
}

.theme-controls {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}

.theme-controls-label {
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
}

.theme-swatch {
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  color: var(--ink);
  background-color: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 3px;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}

.theme-swatch[aria-pressed="true"] {
  background-color: var(--ink);
  color: var(--paper);
}

/*
 * The three colour directions the preview offers a visitor. These are NOT the
 * site's palette — they are a demonstration of what a client's own brand could
 * be, which is why they are three unrelated hues rather than three tints of the
 * comic red. They live here because theme.css, which used to define them, no
 * longer exists.
 *
 * Every one is dark enough to carry the white label on the mock button: white
 * measures 6.30:1 on forest, 9.99:1 on indigo and 5.21:1 on clay. A colour
 * demonstration that shipped unreadable text would be a poor advertisement for
 * the company making it.
 */
:root,
.preview-forest {
  --preview-accent: 152 52% 28%;
}

.preview-indigo { --preview-accent: 244 58% 42%; }
.preview-clay { --preview-accent: 16 44% 44%; }

/* ---------- Forms ---------- */

.form-stack { display: grid; gap: 1.15rem; }

.field-group { display: grid; gap: 0.4rem; }

.field-group-grid {
  display: grid;
  gap: 1.15rem;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .field-group-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.field-group-legend,
.field-label {
  font-family: var(--font-display);
  font-size: 1.05rem;
  letter-spacing: 0.05em;
  color: var(--ink);
}

.field-group-hint {
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
  margin: 0 0 0.5rem;
}

.field-input,
.field-select,
.field-textarea {
  width: 100%;
  font-family: var(--font-letter);
  font-size: 1.05rem;
  color: var(--ink);
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 0.65rem 0.75rem;
}

.field-textarea { min-height: 7rem; resize: vertical; }

/*
 * Errors are red on white at 6.11:1 and are never colour alone — the message is
 * words, and the field it belongs to is named in them.
 */
.form-error {
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--red);
  margin: 0.35rem 0 0;
}

.confirmation {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 1rem;
  box-shadow: var(--shadow-hard);
}

/* ---------- Aside ---------- */

.aside-panel {
  background-color: var(--paper);
  background-image: var(--halftone);
  background-size: var(--halftone-size);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 1.2rem;
  box-shadow: var(--shadow-hard);
}

.aside-item {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  font-family: var(--font-letter);
  font-size: 1.02rem;
  color: var(--ink);
}

/*
 * These icons are rendered from a shared fragment with no class of their own,
 * so nothing else here sizes them. An SVG with no width and no class inherits
 * only `max-width: 100%` and expands to fill its container — which turned the
 * tick in this panel into a black diagonal the width of the page. Any icon
 * without its own class needs an explicit size somewhere, and this is it.
 */
.aside-item svg,
.fit-arrow {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--red);
}

.icon-button svg {
  width: 22px;
  height: 22px;
}

.back-label {
  font-family: var(--font-letter);
  font-size: 1.05rem;
  color: var(--ink);
  text-decoration: none;
}

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

.site-footer {
  background-color: var(--paper);
  border-top: var(--outline-heavy) solid var(--ink);
  padding: 2.5rem 0;
}

.footer-grid { row-gap: 1.75rem; }

.footer-blurb {
  font-family: var(--font-letter);
  font-size: 1.05rem;
  color: var(--ink);
  margin: 0;
}

.footer-link {
  display: flex;
  align-items: center;
  min-height: 2.4rem;
  font-family: var(--font-letter);
  font-size: 1.05rem;
  color: var(--ink);
  text-decoration: none;
}

.footer-link:hover { color: var(--red); }

.footer-legal {
  font-family: var(--font-letter);
  font-size: 0.95rem;
  color: var(--ink);
}

/* ---------- Chip, the PC narrator ---------- */

/*
 * Chip is decoration; the words in the bubble beside him are real content. The
 * SVG is inline and aria-hidden, the speech is server-rendered markup, so the
 * character costs nothing to a screen reader and the narration costs nothing to
 * a crawler.
 */
.chip {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  margin-top: 1.5rem;
}

.chip-art {
  width: 92px;
  height: 92px;
  flex-shrink: 0;
  color: var(--ink);
}

@media (min-width: 768px) {
  .chip-art { width: 116px; height: 116px; }
}

.chip-says {
  position: relative;
  font-family: var(--font-letter);
  font-size: 1.05rem;
  color: var(--ink);
  background-color: var(--yellow);
  border: var(--outline) solid var(--ink);
  border-radius: 20px;
  padding: 0.85rem 1.1rem;
  margin: 0 0 0.75rem;
  box-shadow: var(--shadow-hard);
}

/* Tail pointing back at Chip, drawn as two triangles so the outline continues. */
.chip-says::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--outline) - 17px);
  bottom: 14px;
  border-top: 11px solid transparent;
  border-bottom: 11px solid transparent;
  border-right: 20px solid var(--ink);
}

.chip-says::after {
  content: "";
  position: absolute;
  left: -12px;
  bottom: 17px;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  border-right: 15px solid var(--yellow);
}

.dark-band .chip-says {
  background-color: var(--yellow);
  color: var(--ink);
}

/* ---------- The desk (R04) ---------- */

/*
 * Everything about the desk, the book and the throw is scoped to .comic-rig,
 * which comic.js adds only once it has decided it can run. No JavaScript,
 * blocked GSAP and reduced motion therefore land on the plain vertical document
 * by default rather than by correction.
 *
 * The desk is drawn in LAYERS, and that is the whole fix in this phase. The
 * first attempt was one flat tan fill with a halftone over it, and it did not
 * read as a table at all — it read as a yellow background, which is exactly
 * what it was. A surface needs a horizon: something behind it, an edge where
 * the two meet, grain running across the near side, and objects sitting on it
 * at a believable scale. Four elements, all of them static paint, none of them
 * costing anything per frame while a page turns.
 *
 * Colour, measured against the real ground rather than assumed:
 *
 *   ink #16110D on desk #DCBE8E = 10.44:1  — carries the hint and the controls.
 *   ink #16110D on wall #F4EADA = 15.59:1
 *   desk against wall           =  1.49:1  — FAILS as a boundary on its own.
 *   paper #FFFFFF on desk       =  1.62:1  — FAILS as a boundary on its own.
 *
 * The last two are the ones to keep in mind. Both boundaries — the far edge of
 * the desk, and the edge of the book against it — are non-text marks that need
 * 3:1, and neither difference in fill comes close. Both are carried by ink
 * rules instead: a 6px edge on the desk and the book's own 5px outline, at
 * 10.44:1 and better. A later pass that tidies either rule away deletes the
 * only thing holding the picture apart.
 *
 * White is never a word on the desk.
 */

.comic-scene {
  position: relative;
}

.comic-rig .comic-scene {
  height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background-color: var(--wall);

  /*
   * The book's dimensions live here, not on the book, because four things have
   * to agree on them: the object itself, the shadow it casts, the hint under it
   * and the zoom that scales it to fit the viewport.
   *
   * 66svh, and the number is measured rather than chosen. The scene is a full
   * viewport tall but begins below the site header — 89px at 1440x900 — so its
   * own bottom 89px are already past the fold at scroll 0. The book and the
   * hint under it both have to clear that fold, or the throw lands with the
   * book sliced off at the exact moment it is meant to be looked at.
   *
   * 0.68 of the height is a comic's proportion. It is what makes the object
   * read as a comic rather than as a screen: a page is TALLER THAN IT IS WIDE,
   * and every panel grid on it is laid out for that shape.
   */
  /*
   * ONE PAGE is 0.68 as wide as it is tall, which is a comic's proportion. The
   * BOOK is open, so it is two of those side by side: 1.36 of the page height.
   * Opening the book is also what makes it bigger, which is the other half of
   * "zoom in the whole book a bit" - the object went from 481px across to 857.
   */
  --page-h: min(70svh, 760px);
  --book-h: var(--page-h);
  --book-w: calc(var(--page-h) * 1.36);

  /*
   * The gutter is the page ground showing between panels, which is what a
   * gutter is in a comic. It scales with the book so the page keeps its
   * proportions when the book is small.
   */
  --gutter: max(5px, 0.85svh);

  perspective: 1600px;
  perspective-origin: 50% 42%;
}

/* The wall behind the desk, and the far edge where the two meet. */
.comic-rig .desk-wall {
  position: absolute;
  inset: 0 0 auto 0;
  height: 30%;
  background-color: var(--wall);
  background-image: var(--halftone);
  background-size: var(--halftone-size);
  border-bottom: 6px solid var(--ink);
}

/*
 * The near surface. The grain is a repeating gradient rather than an image:
 * painted once, and the lines get further apart towards the bottom of the
 * element, which is the cheapest honest way to suggest a surface receding.
 */
.comic-rig .desk-top {
  position: absolute;
  inset: 30% 0 0 0;
  background-color: var(--desk);
  /*
   * Grain runs ONE way. The first attempt crossed a horizontal repeat with a
   * vertical one and the desk came out as graph paper - the two together read
   * as a grid, which is a notebook, not a surface. Two horizontal repeats at
   * different periods instead: they drift in and out of phase down the desk,
   * which is what stops evenly spaced lines looking evenly spaced.
   */
  background-image:
    repeating-linear-gradient(
      180deg,
      rgba(22, 17, 13, 0.09) 0 2px,
      transparent 2px 29px
    ),
    repeating-linear-gradient(
      180deg,
      rgba(22, 17, 13, 0.05) 0 1px,
      transparent 1px 47px
    ),
    radial-gradient(ellipse 80% 60% at 50% 0%, rgba(255, 255, 255, 0.34), transparent 70%);
}

/*
 * Objects on the desk, for scale. Without something else sitting on the
 * surface the book has nothing to be next to, and a lone rectangle on a
 * gradient is a rectangle on a gradient.
 */
.comic-rig .desk-props {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/*
 * The throw wrapper owns the arrival transform and the zoom, and carries no
 * paint of its own. It is preserve-3d so the book's lean happens in the desk's
 * perspective rather than being flattened into a squash.
 */
.comic-rig .comic-throw {
  position: relative;
  width: var(--book-w);
  height: var(--book-h);
  transform-style: preserve-3d;
}

.comic-rig .comic-book {
  position: relative;
  width: 100%;
  height: 100%;
  /*
   * The lean tips the object away from the reader about its bottom edge, the
   * way a book lies on a desk. It is NOT preserve-3d: the page block inside is
   * flat within this plane, and a 3D-transformed page block inside a preserve-3d
   * book is sorted behind the book's own opaque background at z=0 and silently
   * stops being painted.
   */
  transform-origin: 50% 100%;
}

/*
 * THE BOARDS. Half the book each, and each one paints the paper ground, the ink
 * outline and the page-edge stack down its own OUTER side. Their inner edges
 * meet at the spine with no border between them, so an open book reads as one
 * rectangle rather than as two boxes pushed together.
 *
 * This is a split rather than one bordered book element because of the shut
 * state: while the book is closed the left board is not there at all, and an
 * element cannot have half a background. The fold (.book-gutter) is drawn above
 * both of them and covers the join.
 */
.book-board { display: none; }

.comic-rig .book-board {
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  width: 50%;
  background-color: var(--paper);
  border: var(--outline-heavy) solid var(--ink);
  z-index: 0;
}

.comic-rig .book-board-left {
  left: 0;
  border-right: 0;
  border-radius: 3px 0 0 3px;
}

.comic-rig .book-board-right {
  left: 50%;
  border-left: 0;
  border-radius: 0 3px 3px 0;
}

/*
 * The page edges. A shut book shows one stack down its open side; an open one
 * shows a stack down BOTH outer sides, because both of them are outer. Stepped
 * offsets of a box-shadow cannot do that - a shadow goes one way - so these are
 * striped bars sitting just outside each board.
 */
.comic-rig .book-board::before {
  content: "";
  position: absolute;
  top: 7px;
  bottom: 7px;
  width: 12px;
  background: repeating-linear-gradient(90deg, var(--paper) 0 3px, var(--ink) 3px 4px);
  border: 2px solid var(--ink);
}

.comic-rig .book-board-left::before {
  left: -14px;
  border-right: 0;
  border-radius: 3px 0 0 3px;
}

.comic-rig .book-board-right::before {
  right: -14px;
  border-left: 0;
  border-radius: 0 3px 3px 0;
}

/*
 * The shadow on the desk is a sibling element and not a fourth entry in that
 * box-shadow, because the landing animates it. Animating a box-shadow repaints
 * the whole rectangle every frame; a sibling moves on transform and opacity,
 * which is all the performance rules allow.
 */
.comic-shadow {
  display: none;
}

.comic-rig .comic-shadow {
  display: block;
  position: absolute;
  inset: 0;
  margin: auto;
  width: var(--book-w);
  height: var(--book-h);
  translate: 26px 30px;
  pointer-events: none;
}

/*
 * The paint sits on an inner element so that two animations can own one shadow
 * without fighting over the same matrix: the throw squashes the outer element
 * uniformly as the book lands, and the opening widens this one from half the
 * book to all of it. Written as one element, the intro's uniform `scale` would
 * overwrite the opening's `scaleX` sixty times a second for the first second of
 * the page's life.
 */
.comic-rig .shadow-body {
  display: block;
  position: absolute;
  inset: 0;
  border-radius: 3px;
  background-color: rgba(22, 17, 13, 0.24);
}

/* ---------- The page block ---------- */

/*
 * No background of its own: the boards under it are the paper, and a page block
 * painting the full spread would put a white left half behind a shut book. Every
 * page paints its own ground, so nothing shows through between panels.
 */
.comic-rig .comic-pages {
  position: absolute;
  inset: 0;
  perspective: 2000px;
  perspective-origin: 50% 50%;
  overflow: hidden;
  cursor: zoom-in;
}

.comic-zoomed .comic-pages { cursor: default; }

/*
 * A LEAF is one sheet with two sides. It occupies the right half of the spread
 * and turns about its left edge, which on the right half is the spine - so a
 * page lifts and falls to the left the way a page does, rather than the whole
 * book flipping like a card.
 */
.comic-rig .leaf {
  position: absolute;
  left: 50%;
  top: 0;
  width: 50%;
  height: 100%;
  transform-origin: left center;
  transform-style: preserve-3d;
}

.comic-rig .leaf-face {
  position: absolute;
  inset: 0;
  margin: 0;
  /*
   * Each face is drawn on one side of the sheet only. Without this both sides
   * paint at once and the front shows through the back, mirrored.
   */
  backface-visibility: hidden;
  /*
   * hidden, and never auto. A page inside a book does not scroll: it is a fixed
   * grid of panels sized to its half of the spread, and a page too dense to
   * read at rest is read by ZOOMING INTO IT (R08). An earlier build let each
   * leaf scroll internally, which put a scrollbar inside a comic book and asked
   * the reader to use two different scroll gestures on two different axes of
   * the same page.
   */
  overflow: hidden;
}

.comic-rig .leaf-back { transform: rotateY(180deg); }

/*
 * The one page that never turns: the back page, which is what the last turn
 * reveals. It sits under every leaf from the start, so the right half of the
 * spread is never a hole.
 *
 * There is no left-hand equivalent any more. The left half of the first spread
 * is the inside of the front cover - the back of leaf 0 - and once that leaf has
 * been turned it stays there for the rest of the book, underneath everything
 * that lands on top of it.
 */
.comic-rig .page-base {
  position: absolute;
  top: 0;
  width: 50%;
  height: 100%;
  margin: 0;
  z-index: 0;
  overflow: hidden;
}

.comic-rig .page-base-right { left: 50%; }

/*
 * The fold. Above every leaf, so the shading reads across the whole spread
 * rather than under whichever page happens to be on top. Leaf z-indexes run to
 * 8; this sits at 40.
 */
.book-gutter { display: none; }

.comic-rig .book-gutter {
  display: block;
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 46px;
  translate: -50% 0;
  z-index: 40;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    rgba(22, 17, 13, 0) 0%,
    rgba(22, 17, 13, 0.09) 34%,
    rgba(22, 17, 13, 0.26) 49%,
    rgba(22, 17, 13, 0.26) 51%,
    rgba(22, 17, 13, 0.09) 66%,
    rgba(22, 17, 13, 0) 100%
  );
}

/* ---------- The page, and its panels (R07) ---------- */

/*
 * A page is a grid of panels separated by gutters. The gutter is the page
 * ground showing through, so panels are bordered boxes on paper rather than
 * boxes with margins.
 *
 * The row templates use `fr` and are NOT scoped to the rig. In the pinned book
 * the page has a fixed height and the fractions divide it; in the plain
 * vertical document the page has no fixed height and `fr` rows fall back to
 * their content size, which is the layout that fallback wants anyway. One set
 * of templates, both modes.
 */
.comic-page {
  display: grid;
  gap: var(--gutter, 8px);
  padding: var(--gutter, 8px);
  background-color: var(--paper);
  border-bottom: var(--outline-heavy) solid var(--ink);
}

/*
 * Every page is ONE column. A comic page is normally a grid, but the panels on
 * this one carry sentences rather than drawings, and half of a portrait page is
 * about eleven characters wide - which sets every line of dialogue one word at
 * a time. Full-width tiers, stacked, is the shape that reads.
 *
 * Rows are `auto` for anything sized by its own type and one `minmax(0, 1fr)`
 * to absorb what is left. Fixed fractions were what pushed the form panel 34px
 * past its own box: a fraction does not know how tall a textarea is.
 */
.page-inside { grid-template-rows: auto minmax(0, 1fr) minmax(0, 0.55fr); }
.page-3 { grid-template-rows: auto minmax(0, 1fr) minmax(0, 1fr); }
.page-4 { grid-template-rows: auto minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.15fr); }
.page-preview { grid-template-rows: auto auto auto minmax(0, 1fr); }
.page-list { grid-template-rows: auto auto minmax(0, 1fr); }
.page-fit { grid-template-rows: auto auto auto minmax(0, 1fr); }
.page-questions { grid-template-rows: auto auto minmax(0, 1fr); }

.page-cast {
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto repeat(3, minmax(0, 1fr));
}

.panel-cast {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25em;
  text-align: center;
}

.panel-cast .cast { align-self: center; }

.cast-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(0.6rem, 1.5svh, 1rem);
  letter-spacing: 0.05em;
  line-height: 1.1;
  color: var(--ink);
}

.cast-name span {
  display: block;
  font-family: var(--font-letter);
  font-size: 0.78em;
  letter-spacing: 0;
}
.page-end { grid-template-rows: auto minmax(0, 0.85fr) minmax(0, 1fr) auto; }

.panel {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.5em;
  min-width: 0;
  min-height: 0;
  padding: clamp(0.35rem, 1.3svh, 0.85rem);
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 2px;
  overflow: hidden;
}

/*
 * A title panel spans the page and stacks. Every page opens with one, which is
 * what gives the book a rhythm when the leaves are flicked past quickly.
 */
.page-cast .panel-title { grid-column: 1 / -1; }

.panel-title,
.panel-wide,
.panel-form,
.panel-preview,
.panel-checks,
.panel-questions,
.panel-rows,
.panel-lineup {
  grid-column: 1 / -1;
}

.panel-title {
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 0.25em;
}

.panel-tight { padding: clamp(0.3rem, 1svh, 0.6rem); }

/* Panel fills. Every pair measured: see the palette note at the top of the file. */
.panel-red { background-color: var(--red-bright); }
.panel-yellow { background-color: var(--yellow); }
.panel-cyan { background-color: var(--cyan); }
.panel-ink { background-color: var(--ink); }

/* Paper on bright red is 4.83:1 and on ink 18.75:1 — display sizes only. */
.panel-red .panel-shout,
.panel-red .panel-kicker,
.panel-ink .panel-shout,
.panel-ink .panel-kicker {
  color: var(--paper);
}

.panel-kicker {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(0.5rem, 1.3svh, 0.85rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
}

.panel-shout {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1rem, 3.6svh, 2.5rem);
  line-height: 0.98;
  letter-spacing: 0.01em;
  color: var(--ink);
}

.panel-shout-sm { font-size: clamp(0.85rem, 2.7svh, 1.9rem); }

/*
 * The panel number is a caption box in the corner, the way a comic numbers its
 * beats. Absolutely positioned so it never takes a row from the dialogue.
 */
.panel-number {
  position: absolute;
  top: 0;
  left: 0;
  background-color: var(--yellow);
  color: var(--ink);
  border-right: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
  font-family: var(--font-display);
  font-size: clamp(0.5rem, 1.4svh, 0.9rem);
  letter-spacing: 0.06em;
  padding: 0.1em 0.45em;
}

/* ---------- The cast, and what they say ---------- */

.cast {
  flex: none;
  width: clamp(34px, 11svh, 100px);
  height: auto;
  align-self: flex-end;
}

.cast-lg { width: clamp(44px, 14svh, 124px); }
.cast-sm { width: clamp(28px, 8.5svh, 76px); }

/*
 * A speech bubble, with a tail pointing back at whoever is speaking. The tail
 * is two borders rather than an image, and it sits on the left because every
 * panel puts its character on the left.
 */
.bubble {
  position: relative;
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  background-color: var(--paper);
  color: var(--ink);
  border: 2px solid var(--ink);
  border-radius: 14px;
  padding: 0.5em 0.75em;
  font-family: var(--font-letter);
  font-size: clamp(0.6rem, 1.62svh, 1.1rem);
  line-height: 1.3;
}

.bubble-sm { font-size: clamp(0.55rem, 1.42svh, 0.95rem); }

.bubble::before,
.bubble::after {
  content: "";
  position: absolute;
  left: -11px;
  bottom: 14px;
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-right: 11px solid var(--ink);
}

.bubble::after {
  left: -8px;
  border-right-color: var(--paper);
}

.bubble-cyan { background-color: var(--cyan); }
.bubble-cyan::after { border-right-color: var(--cyan); }

.bubble b {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0.03em;
}

/* ---------- Leaf 0: the cover ---------- */

.page-cover {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 0.6em;
  padding: clamp(0.9rem, 3.4svh, 2.4rem);
  background-color: var(--red-bright);
  background-image: var(--halftone);
  background-size: var(--halftone-size);
}

.cover-burst {
  position: absolute;
  top: clamp(0.6rem, 2vh, 1.4rem);
  right: clamp(0.6rem, 2vh, 1.4rem);
  background-color: var(--yellow);
  color: var(--ink);
  border: var(--outline) solid var(--ink);
  font-family: var(--font-display);
  font-size: clamp(0.6rem, 1.6svh, 1.05rem);
  letter-spacing: 0.06em;
  padding: 0.4em 0.7em;
  rotate: 9deg;
}

.cover-kicker {
  margin: 0;
  display: inline-block;
  font-family: var(--font-display);
  font-size: clamp(0.6rem, 1.7svh, 1.15rem);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--yellow);
  border: var(--outline) solid var(--ink);
  padding: 0.2em 0.6em;
  rotate: -1.5deg;
}

.cover-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(2rem, 8.4svh, 5.5rem);
  line-height: 0.9;
  letter-spacing: 0.02em;
  color: var(--paper);
  text-shadow:
    3px 3px 0 var(--ink),
    -2px -2px 0 var(--ink),
    2px -2px 0 var(--ink),
    -2px 2px 0 var(--ink);
}

.cover-title .stop { color: var(--yellow); }

.cover-tagline {
  margin: 0;
  max-width: 26ch;
  font-family: var(--font-letter);
  font-size: clamp(0.62rem, 1.7svh, 1.15rem);
  line-height: 1.3;
  color: var(--ink);
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 16px;
  padding: 0.5em 0.8em;
}

/* The cast lined up along the foot of the cover, the way a team book does it. */
.cover-cast {
  display: flex;
  align-items: flex-end;
  gap: 0.4em;
  margin: 0;
  width: 100%;
}

.cover-face {
  width: clamp(28px, 8svh, 68px);
  height: auto;
  flex: 0 1 auto;
}

.cover-open {
  font-family: var(--font-display);
  font-size: clamp(0.7rem, 2svh, 1.3rem);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--yellow);
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  padding: 0.45em 1em;
  cursor: pointer;
  box-shadow: var(--shadow-hard);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

.cover-open:hover {
  transform: translate(2px, 2px);
  box-shadow: 3px 3px 0 var(--ink);
}

/* ---------- The pages that carry a working control ---------- */

/*
 * Two pages do something rather than say something: the concept preview and the
 * questions. Both are the reason the zoom exists — at rest they are legible as
 * a picture of a form, and zoomed they are a form.
 */
.panel-form,
.panel-preview,
.panel-questions,
.panel-checks,
.panel-rows {
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: 0.4em;
}

.panel-form form { display: contents; }

.panel-form .preview-idea {
  width: 100%;
  resize: none;
  font-family: var(--font-letter);
  font-size: clamp(0.55rem, 1.5svh, 1rem);
  padding: 0.4em 0.6em;
  border: 2px solid var(--ink);
  border-radius: 3px;
  background-color: var(--paper);
  color: var(--ink);
}

.panel .button-primary,
.panel .button-outline {
  font-family: var(--font-display);
  font-size: clamp(0.6rem, 1.6svh, 1.05rem);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 0.4em 0.9em;
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  box-shadow: 4px 4px 0 var(--ink);
  text-decoration: none;
  display: block;
  width: 100%;
  text-align: center;
  cursor: pointer;
}

.panel .button-primary {
  background-color: var(--red);
  color: var(--paper);
}

.panel .button-outline {
  background-color: var(--yellow);
  color: var(--ink);
}

.panel .form-error {
  margin: 0;
  font-size: clamp(0.5rem, 1.3svh, 0.85rem);
  color: var(--red);
}

/* The mock product window inside a panel. */
.panel-preview .preview-window {
  flex: 1 1 auto;
  min-height: 0;
  border: 2px solid var(--ink);
  border-radius: 3px;
  box-shadow: none;
  overflow: hidden;
}

/*
 * Restated, not inherited. `.preview-tagline` is `clamp(1.6rem, 3vw, 2.3rem)`
 * where it was designed to live, which inside a panel is a line of type taller
 * than the panel: the first build of this page put one word of the tagline
 * across the whole window and pushed the call to action off the bottom edge.
 */
.panel-preview .preview-name { font-size: clamp(0.55rem, 1.5svh, 1rem); }
.panel-preview .preview-type { font-size: clamp(0.45rem, 1.15svh, 0.75rem); }
.panel-preview .preview-kicker { font-size: clamp(0.5rem, 1.25svh, 0.82rem); }

.panel-preview .preview-tagline {
  font-size: clamp(0.72rem, 2svh, 1.35rem);
  line-height: 1.1;
}

.panel-preview .preview-action { font-size: clamp(0.5rem, 1.3svh, 0.88rem); }
.panel-preview .preview-feature { font-size: clamp(0.48rem, 1.2svh, 0.8rem); }

.panel-preview .preview-bar {
  display: flex;
  justify-content: space-between;
  gap: 0.5em;
  padding: 0.3em 0.6em;
  border-bottom: 2px solid var(--ink);
  font-family: var(--font-display);
  letter-spacing: 0.06em;
}

.panel-preview .preview-body { padding: 0.5em 0.6em; }

.panel-preview .preview-kicker {
  margin: 0;
  font-family: var(--font-display);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.panel-preview .preview-tagline {
  margin: 0.25em 0 0;
  font-family: var(--font-letter);
  line-height: 1.3;
}

.panel-preview .preview-action {
  display: inline-block;
  margin-top: 0.4em;
  font-family: var(--font-display);
  letter-spacing: 0.06em;
  border: 2px solid var(--ink);
  padding: 0.15em 0.6em;
}

.panel-preview .preview-features {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15em 0.7em;
  margin-top: 0.45em;
  padding-top: 0.35em;
  border-top: 2px dashed var(--ink);
  font-family: var(--font-letter);
}

.panel-preview .theme-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em;
}

.panel-preview .theme-swatch {
  font-family: var(--font-letter);
  font-size: clamp(0.5rem, 1.3svh, 0.85rem);
  padding: 0.15em 0.5em;
  border: 2px solid var(--ink);
  border-radius: 3px;
  background-color: var(--paper);
  color: var(--ink);
  cursor: pointer;
}

.panel-preview .theme-swatch[aria-pressed="true"] { background-color: var(--yellow); }

/* The checklist and the project-fit rows. */
.panel-checks ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35em;
}

.check-row {
  display: flex;
  align-items: center;
  gap: 0.5em;
  font-family: var(--font-letter);
  font-size: clamp(0.58rem, 1.55svh, 1.05rem);
  color: var(--ink);
}

.check-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.5em;
  height: 1.5em;
  background-color: var(--yellow);
  border: 2px solid var(--ink);
  border-radius: 50%;
}

.check-mark svg { width: 0.9em; height: 0.9em; }

.fit-row {
  display: flex;
  align-items: center;
  gap: 0.6em;
  margin: 0;
  padding: 0.3em 0.4em 0.3em 0;
  border-bottom: 2px solid var(--ink);
  font-family: var(--font-letter);
  font-size: clamp(0.56rem, 1.5svh, 1rem);
  line-height: 1.2;
}

/* Inside a row the number is in the flow, not pinned to the panel corner. */
.fit-row .panel-number {
  position: static;
  border: 2px solid var(--ink);
}

/* The questions, unchanged in behaviour and re-set for a panel. */
.panel-questions .question { border-bottom: 2px solid var(--ink); }

.panel-questions .question-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5em;
  width: 100%;
  padding: 0.4em 0.2em;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
  font-family: var(--font-display);
  font-size: clamp(0.58rem, 1.5svh, 1rem);
  letter-spacing: 0.03em;
  color: var(--ink);
}

.panel-questions .question-chevron { width: 1em; height: 1em; flex: none; }

.panel-questions .question-answer {
  padding: 0 0.2em 0.5em;
  font-family: var(--font-letter);
  font-size: clamp(0.52rem, 1.38svh, 0.92rem);
  line-height: 1.32;
  color: var(--ink);
}

.panel-lineup {
  flex-direction: row;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.2em;
}

/*
 * Six characters shoulder to shoulder. They share the panel width rather than
 * each taking their fixed one, which six of at full size is wider than the page.
 */
.panel-lineup .cast {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
  max-width: 5.5em;
}

/* ---------- Decoration on the desk ---------- */

/*
 * Both are display:none by default rather than merely transparent. In the plain
 * vertical document there is no desk for them to sit on, and a stray "Fwap!" in
 * the middle of the copy is worse than no impact mark at all.
 *
 * The burst is a filled yellow panel with ink lettering, not yellow words:
 * yellow is a fill in this system and never a word (1.51:1 on white). Ink on
 * yellow is 12.40:1.
 */
.comic-impact,
.comic-hint {
  display: none;
}

.comic-rig .comic-impact {
  display: block;
  position: absolute;
  top: 16%;
  right: clamp(1rem, 12vw, 12rem);
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 4vw, 2.9rem);
  letter-spacing: 0.06em;
  color: var(--ink);
  background-color: var(--yellow);
  border: var(--outline) solid var(--ink);
  padding: 0.35rem 0.95rem;
  rotate: -8deg;
  opacity: 0;
  pointer-events: none;
}

/*
 * Anchored to the book, not to the foot of the scene. The scene is a full
 * viewport tall but begins below the site header, so its bottom edge is off
 * screen until the pin engages — a hint pinned there is invisible at exactly
 * the moment it is meant to be read.
 */
/*
 * Beside the book, not under it, and that is what lets the book be as tall as
 * it is. The scene is a full viewport but begins below the site header, so its
 * bottom edge is off screen until the pin engages: anything anchored there is
 * invisible at exactly the moment it is meant to be read, and everything above
 * it has to shrink to make room. There is a whole desk to the left instead.
 */
.comic-rig .comic-hint {
  display: block;
  position: absolute;
  top: 50%;
  right: calc(50% + var(--book-w) / 2 + 2.5vw);
  width: min(15ch, 20vw);
  margin: 0;
  translate: 0 -50%;
  rotate: -4deg;
  font-family: var(--font-letter);
  font-size: clamp(0.85rem, 1.2vw, 1.1rem);
  line-height: 1.35;
  text-align: right;
  color: var(--ink);
  pointer-events: none;
}

/* ---------- The controls on the desk ---------- */

.comic-controls {
  margin: 0;
  padding: 0.75rem 1.25rem;
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
}

/*
 * Lifted onto the desk inside the rig. They stay first in the scene's tab
 * order, which is the entire point of them: a pinned, scrubbed book is hostile
 * to tabbing, and these are the ways out of it — one to the plain document, one
 * into the page itself.
 */
.comic-rig .comic-controls {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 6;
  padding: 0.6rem 0.9rem;
}

.comic-plain-toggle,
.comic-zoom-toggle {
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
  background-color: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 3px;
  padding: 0.35rem 0.8rem;
  cursor: pointer;
}

.comic-plain-toggle[aria-pressed="true"],
.comic-zoom-toggle[aria-pressed="true"] { background-color: var(--yellow); }

/* The zoom control is meaningless without a book to zoom. */
.comic-zoom-toggle { display: none; }
.comic-rig .comic-zoom-toggle { display: inline-block; }

/* ---------- Zoomed in (R08) ---------- */

/*
 * Zooming is a mode, not an effect. While it is on, the page underneath must
 * not move: scroll is frozen rather than the ScrollTrigger disabled, because
 * disabling a pinned trigger releases the pin and drops the book out of the
 * viewport. `scrollbar-gutter: stable` on <html> is what keeps freezing the
 * scroll from shifting the layout sideways by the width of a scrollbar.
 */
html { scrollbar-gutter: stable; }

/*
 * ---------- Shut (R10) ----------
 *
 * What the class does NOT do is hide the left board or move the book. Both of
 * those are scrubbed by the opening turn, so GSAP owns them outright and writes
 * them inline; a class rule for the same properties would be overridden on the
 * first frame and would then be a lie sitting in the stylesheet.
 *
 * What is left is the things no tween touches: which half of the hint is shown,
 * and the fact that a shut book is not something you can zoom into.
 */
body.comic-shut .comic-pages { cursor: default; }

/*
 * The zoom control is not there while the book is, because zooming into a shut
 * book is zooming into a cover. It arrives with the first spread.
 */
.comic-rig.comic-shut .comic-zoom-toggle { display: none; }

.comic-hint .hint-shut,
.comic-hint .hint-open { display: none; }

.comic-rig.comic-shut .comic-hint .hint-shut { display: inline; }
.comic-rig:not(.comic-shut) .comic-hint .hint-open { display: inline; }

/*
 * Scroll stays LIVE while zoomed. This rule was `overflow: hidden`, and a zoom
 * that froze the book was a dead end: the page after the one being read could
 * only be reached by coming back out and going in again. What the freeze was
 * protecting - the scrub writing the book's pose underneath the zoom - is held
 * in comic.js now, by the zoom re-asserting its own pose on the one turn that
 * contests it and letting go where the book stops being open.
 *
 * `scrollbar-gutter: stable` on <html> stays: it is no longer compensating for
 * a scrollbar that appears and disappears, but it still keeps the desk from
 * shifting sideways when the document grows past the viewport.
 */

.comic-zoomed .comic-hint { opacity: 0; }
.comic-zoomed .desk-props { opacity: 0.3; }

/* ---------- The mock site (/preview, DOMAIN.md 2.2) ---------- */

/*
 * A drawn browser window standing on the desk.
 *
 * It reuses the concept panel's colour system rather than introducing one:
 * --preview-accent is the same variable the three direction classes set, so
 * .preview-forest / .preview-indigo / .preview-clay work on this window without
 * a line of new theming. Everything below reads that variable, which is why
 * switching a direction here costs two custom properties and repaints no copy.
 *
 * The window is a PANEL in the comic's own vocabulary: heavy ink border, hard
 * offset shadow, no soft edges anywhere. It has to read as a DRAWING of a
 * browser rather than as a real one embedded in the page, because a sketch that
 * passes for a live site is a promise this page is not entitled to make.
 */

.mock-stage {
  margin: 0;
  /* The desk under the window, so the sketch sits on a surface the way the
     book does rather than floating on the page ground. */
  background-color: var(--wall);
  background-image: var(--halftone);
  background-size: var(--halftone-size);
  border: var(--outline) solid var(--ink);
  border-radius: 6px;
  padding: clamp(1rem, 3vw, 2.5rem);
  box-shadow: var(--shadow-hard);
}

.mock-window {
  background-color: var(--paper);
  border: var(--outline-heavy) solid var(--ink);
  border-radius: 5px;
  box-shadow: var(--shadow-hard-lg);
  overflow: hidden;
}

.mock-chrome {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.9rem;
  border-bottom: var(--outline) solid var(--ink);
  background-color: var(--yellow);
}

.mock-dot {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  border: 2px solid var(--ink);
  background-color: var(--paper);
  flex-shrink: 0;
}

/*
 * The address bar. A span, never an anchor: there is nothing at this domain,
 * and a link that goes nowhere is the one detail that would turn a sketch into
 * a small lie.
 */
.mock-address {
  flex: 1 1 auto;
  min-width: 0;
  margin-left: 0.4rem;
  padding: 0.25rem 0.7rem;
  font-family: var(--font-letter);
  font-size: 0.95rem;
  color: var(--ink);
  background-color: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 999px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mock-badge {
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 3px;
  padding: 0.15rem 0.5rem;
  flex-shrink: 0;
}

/*
 * The one thing on this site that scrolls inside itself, and it is not an
 * exception to the book's rule - it IS the point. A browser window scrolls, so
 * the drawing of one scrolls, and the height is capped so the window reads as a
 * window rather than as the page it is drawn on.
 */
.mock-viewport {
  max-height: min(70vh, 40rem);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.mock-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 0.9rem 1.2rem;
  border-bottom: var(--outline) solid var(--ink);
  background-color: hsl(var(--preview-accent));
}

.mock-brand {
  font-family: var(--font-display);
  font-size: 1.35rem;
  letter-spacing: 0.04em;
  color: var(--paper);
}

.mock-tabs {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.mock-tab {
  font-family: var(--font-display);
  font-size: 0.9rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 3px;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
}

.mock-tab[aria-selected="true"] {
  background-color: var(--yellow);
  box-shadow: 3px 3px 0 var(--ink);
}

.mock-panel {
  padding: clamp(1.2rem, 3vw, 2.2rem);
}

/*
 * The panel is focused when the hero's call to action opens it, and a focus
 * ring drawn around a whole section is noise. It is somewhere to be put, not
 * something to point at.
 */
.mock-panel:focus { outline: none; }

.mock-kicker {
  font-family: var(--font-display);
  font-size: 0.85rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: hsl(var(--preview-accent));
  margin: 0;
}

.mock-headline {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3.4vw, 2.6rem);
  line-height: 1.05;
  letter-spacing: 0.02em;
  color: var(--ink);
  margin: 0.5rem 0 0;
  max-width: 22ch;
}

.mock-headline-sm { font-size: clamp(1.3rem, 2.6vw, 1.9rem); }

.mock-lede {
  font-family: var(--font-letter);
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--ink);
  margin: 0.8rem 0 0;
  max-width: 46ch;
}

.mock-cta {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 1rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  background-color: hsl(var(--preview-accent));
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  padding: 0.55rem 1.2rem;
  margin-top: 1.3rem;
  box-shadow: 4px 4px 0 var(--ink);
  cursor: pointer;
}

/*
 * The press. Two pixels down and the shadow shortens by two, so the button
 * moves towards the paper rather than changing colour - the same gesture every
 * other control on this site makes.
 */
.mock-cta:active {
  transform: translate(2px, 2px);
  box-shadow: 2px 2px 0 var(--ink);
}

.mock-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 1.6rem;
}

.mock-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
  background-color: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
}

.mock-mark {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  border: 2px solid var(--ink);
  background-color: hsl(var(--preview-accent));
  flex-shrink: 0;
}

.mock-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1rem;
  margin-top: 1.5rem;
}

.mock-card {
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 1rem;
  box-shadow: var(--shadow-hard);
}

.mock-card-index {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: hsl(var(--preview-accent));
}

.mock-card-title {
  font-family: var(--font-display);
  font-size: 1.1rem;
  letter-spacing: 0.03em;
  color: var(--ink);
  margin: 0.3rem 0 0;
}

.mock-card-copy {
  font-family: var(--font-letter);
  font-size: 0.98rem;
  line-height: 1.45;
  color: var(--ink);
  margin: 0.4rem 0 0;
}

.mock-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem;
  margin-top: 1.4rem;
}

.mock-input {
  flex: 1 1 16rem;
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  padding: 0.6rem 0.8rem;
}

.mock-form .mock-cta { margin-top: 0; }

.mock-note {
  font-family: var(--font-letter);
  font-size: 0.95rem;
  color: var(--ink);
  margin: 0.8rem 0 0;
}

/*
 * After a submit that refused. The sketch has to be able to say no loudly
 * enough that nobody walks away thinking they signed up for something.
 */
.mock-note-loud {
  background-color: var(--yellow);
  border: 2px solid var(--ink);
  border-radius: 3px;
  padding: 0.5rem 0.7rem;
  display: inline-block;
}

.mock-real {
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
  margin: 1.2rem 0 0;
}

.mock-real-link { color: var(--ink); }

.mock-caption {
  font-family: var(--font-letter);
  font-size: 1rem;
  color: var(--ink);
  background-color: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 3px;
  padding: 0.5rem 0.8rem;
  margin-top: 1rem;
  display: inline-block;
  box-shadow: 3px 3px 0 var(--ink);
}

@media (max-width: 767px) {
  /*
   * The window keeps its chrome on a phone - it is what makes the thing read as
   * a browser at all - but the address bar is the first thing to go when there
   * is no room for it beside the badge, and the viewport stops being a window
   * with its own scrollbar inside a page that already scrolls.
   */
  .mock-address { display: none; }
  .mock-viewport { max-height: none; }
  .mock-nav { justify-content: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  .mock-cta:active { transform: none; }
}

/* ---------- The preview link and the note on it (DOMAIN.md 2.3) ---------- */

/*
 * A message written to one visitor by the person who made their version. It is
 * given the speech-bubble treatment the rest of the site uses for anything said
 * rather than printed, because that is exactly what it is: somebody at infoenc
 * talking to them.
 */
.mock-message {
  font-family: var(--font-letter);
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--ink);
  background-color: var(--yellow);
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 0.8rem 1rem;
  box-shadow: var(--shadow-hard);
}

/* The link itself, set in mono so a visitor can read it character by character
   when they copy it by hand off a screen. */
.mock-permalink {
  font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  font-size: 0.95rem;
  word-break: break-all;
  background-color: var(--paper);
  border: 2px dashed var(--ink);
  border-radius: 4px;
  padding: 0.6rem 0.8rem;
  margin: 0;
}

/* ---------- The preview queue (/admin, DOMAIN.md 2.3) ---------- */

/*
 * An admin screen, and it does not pretend otherwise. No halftone, no hand
 * lettering in the body, no comic panels: the queue is a tool for one person
 * reading other people's ideas quickly, and the display face that makes a
 * headline shout makes a table of prose hard to scan.
 *
 * It shares the stylesheet only for the tokens - ink, paper, the outline
 * weights, the focus ring - so a change to the palette does not leave this
 * screen behind.
 */
.admin-body {
  background-color: var(--wall);
}

.admin-shell {
  max-width: 68rem;
  margin: 0 auto;
  padding: clamp(1.5rem, 4vw, 3rem) clamp(1rem, 4vw, 2rem) 5rem;
}

.admin-head { margin-bottom: 2rem; }

.admin-title {
  font-family: var(--font-display);
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  letter-spacing: 0.02em;
  color: var(--ink);
  margin: 0.3rem 0 0;
}

.admin-lede,
.admin-empty {
  font-family: var(--font-letter);
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--ink);
  max-width: 54ch;
  margin: 0.6rem 0 0;
}

.admin-heading {
  font-family: var(--font-display);
  font-size: 1.15rem;
  letter-spacing: 0.04em;
  color: var(--ink);
  margin: 0 0 0.6rem;
}

.admin-card {
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 5px;
  box-shadow: var(--shadow-hard);
  padding: clamp(1rem, 3vw, 1.6rem);
  margin-bottom: 1.5rem;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 5px;
  overflow: hidden;
  box-shadow: var(--shadow-hard);
}

.admin-table th,
.admin-table td {
  text-align: left;
  padding: 0.7rem 0.9rem;
  border-bottom: 2px solid var(--ink);
  vertical-align: top;
  font-size: 0.98rem;
}

.admin-table th {
  font-family: var(--font-display);
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background-color: var(--yellow);
}

.admin-table tbody tr:last-child td { border-bottom: none; }

/*
 * The idea column. Clamped to three lines rather than truncated on the server:
 * the half of a sentence that says what the business actually does is usually
 * the second half, and a queue that hides it makes every row need opening.
 */
.admin-table td a.admin-link {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.admin-idea {
  font-family: var(--font-letter);
  font-size: 1.1rem;
  line-height: 1.5;
  color: var(--ink);
  white-space: pre-wrap;
  margin: 0;
}

.admin-pairs {
  display: grid;
  grid-template-columns: minmax(6rem, max-content) 1fr;
  gap: 0.4rem 1rem;
  margin: 0.8rem 0;
  font-size: 0.98rem;
}

.admin-pairs dt {
  font-family: var(--font-display);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.admin-pairs dd { margin: 0; }

.admin-link { color: var(--ink); }

.admin-dim { opacity: 0.7; }

.admin-mono {
  font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  font-size: 0.9rem;
}

.admin-badge {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  padding: 0.15rem 0.5rem;
  border: 2px solid var(--ink);
  border-radius: 3px;
  background-color: var(--paper);
}

/*
 * Status colour is a fill behind ink, never coloured text: the three fills here
 * are 10:1 or better under the ink they carry, where the same three as
 * foreground colours would not clear 4.5 on paper.
 */
.admin-badge-new { background-color: var(--yellow); }
.admin-badge-in_progress { background-color: var(--paper); }
.admin-badge-published { background-color: hsl(152 52% 82%); }

.admin-form { display: grid; gap: 0.5rem; }
.admin-form .field-label { margin-top: 0.6rem; }

.admin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  margin-top: 1.4rem;
}

/*
 * The second-rank button, which the marketing pages never needed: they only
 * ever have one thing to do per screen. Here there are three, and only one of
 * them is the one that changes what a visitor sees.
 */
.button-secondary {
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--paper);
  border: var(--outline) solid var(--ink);
  border-radius: 3px;
  padding: 0.55rem 1.1rem;
  box-shadow: 4px 4px 0 var(--ink);
  cursor: pointer;
}

.button-secondary:active {
  transform: translate(2px, 2px);
  box-shadow: 2px 2px 0 var(--ink);
}

.admin-flash,
.admin-error {
  font-family: var(--font-letter);
  font-size: 1rem;
  border: var(--outline) solid var(--ink);
  border-radius: 4px;
  padding: 0.6rem 0.9rem;
  margin: 0 0 1.2rem;
}

.admin-flash { background-color: hsl(152 52% 85%); }
.admin-error { background-color: var(--yellow); }

/*
 * The admin's own navigation. Two screens, so it is two words and a separator
 * rather than anything that deserves the name "navigation" - and the page you
 * are on is not a link to itself.
 */
.admin-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.2rem;
  align-items: baseline;
  font-family: var(--font-display);
  font-size: 0.9rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding-bottom: 0.8rem;
  margin-bottom: 1.6rem;
  border-bottom: 2px solid var(--ink);
}

.admin-nav-here {
  color: var(--ink);
  background-color: var(--yellow);
  border: 2px solid var(--ink);
  border-radius: 3px;
  padding: 0.1rem 0.5rem;
}

.admin-pager {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.2rem;
  font-family: var(--font-letter);
}

@media (prefers-reduced-motion: reduce) {
  .button-secondary:active { transform: none; }
}

/* ---------- Mobile downgrade ---------- */

@media (max-width: 767px) {
  /*
   * A shorter book, because the proportion is fixed and a 66svh page on a phone
   * is wider than the phone once it is rotated three degrees — the scene clips
   * the corners of the cover. perspective and preserve-3d are dropped outright
   * rather than softened: a rotateY across a phone reads as a squash and still
   * costs a 3D layer on the devices least able to pay for one. comic.js
   * switches to xPercent at this same breakpoint; the two files have to agree
   * on the number.
   */
  /*
   * A spread on a 390px phone is two 175px columns, which is not a book, it is
   * a receipt. Below this breakpoint the leaf stops being a sheet: `display:
   * contents` removes its box entirely, both of its faces become full-width
   * pages positioned against the page block, and the eleven pages are read one
   * at a time in the same order the DOM already has them in.
   */
  .comic-rig .comic-scene {
    --page-h: min(58svh, 560px);
    --book-w: calc(var(--page-h) * 0.68);
    perspective: none;
  }

  .comic-rig .comic-throw { transform-style: flat; }

  .comic-rig .leaf { display: contents; }

  .comic-rig .leaf-face,
  .comic-rig .page-base {
    left: 0;
    width: 100%;
    transform: none;
    transform-origin: center center;
    backface-visibility: visible;
  }

  .comic-rig .book-gutter { display: none; }

  /*
   * One page wide is one board. The left board would otherwise cut the single
   * page down the middle, and the right one would start halfway across it.
   */
  .comic-rig .book-board-left { display: none; }

  .comic-rig .book-board-right {
    left: 0;
    width: 100%;
    border: var(--outline-heavy) solid var(--ink);
    border-radius: 3px;
  }

  .comic-rig .book-board::before { width: 8px; }

  .page-cast { grid-template-columns: 1fr 1fr; }

  .comic-rig .comic-book {
    box-shadow:
      3px 4px 0 0 var(--paper), 3px 4px 0 2px var(--ink),
      7px 8px 0 0 var(--paper), 7px 8px 0 2px var(--ink);
  }

  .comic-rig .comic-shadow { translate: 14px 16px; }
  .comic-rig .comic-pages { perspective: none; }

  /*
   * The title panel wraps to two or three lines on a phone and takes the space
   * the tiers below it were going to use, so the two densest panels - the
   * project-fit rows and the questions - are tightened rather than left to
   * spill past their own borders.
   */
  .panel-shout-sm { font-size: clamp(0.8rem, 2.3svh, 1.6rem); }

  .fit-row {
    padding: 0.15em 0.3em 0.15em 0;
    font-size: clamp(0.6rem, 1.3svh, 0.9rem);
  }

  .panel-rows .button-outline { padding: 0.3em 0.7em; }

  .panel-questions .question-trigger {
    padding: 0.3em 0.15em;
    font-size: clamp(0.6rem, 1.35svh, 0.95rem);
  }

  .panel-questions .question-answer {
    padding-bottom: 0.35em;
    font-size: clamp(0.55rem, 1.25svh, 0.85rem);
  }

  .comic-rig .comic-impact { top: 11%; font-size: 1.4rem; }

  /*
   * There is no room BESIDE the book on a phone - the book is the width of the
   * screen - so the hint that sits to its left on a desk was hanging off the
   * left edge of the viewport, half a sentence visible. It goes above the book
   * instead, in the wall band, which is the only space a phone has spare.
   *
   * Not below it: the scene starts under the site header, so its bottom edge is
   * off screen until the pin engages, and a hint anchored there is invisible at
   * exactly the moment it is meant to be read.
   */
  .comic-rig .comic-hint {
    top: 4%;
    left: 5%;
    right: auto;
    width: min(22ch, 52vw);
    translate: none;
    text-align: left;
    font-size: 0.9rem;
  }
  .comic-rig .comic-controls { padding: 0.4rem 0.5rem; }

  .comic-plain-toggle,
  .comic-zoom-toggle { font-size: 0.85rem; padding: 0.25rem 0.55rem; }

  .display-heading { text-shadow: 3px 3px 0 var(--yellow); }
  .chip { gap: 0.6rem; }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .cover-open,
  .button-primary,
  .button-outline,
  .nav-link::after,
  .question-chevron {
    transition: none;
  }

  .cover-open:hover,
  .button-primary:hover,
  .button-outline:hover,
  .button-primary:active,
  .button-outline:active {
    transform: none;
    box-shadow: var(--shadow-hard);
  }

  /*
   * Belt and braces on the whole scene. comic.js already declines to build the
   * rig when motion is reduced, so .comic-rig should never be present here —
   * but if it ever is, through a bug or a setting changed after load, these
   * rules put the desk away and the leaves back into normal flow rather than
   * leaving eight of the nine stacked invisibly on top of one another.
   */
  .comic-rig .comic-scene {
    height: auto;
    display: block;
    overflow: visible;
    perspective: none;
    background-color: var(--paper);
  }

  .comic-rig .desk-wall,
  .comic-rig .desk-top,
  .comic-rig .desk-props,
  .comic-rig .comic-shadow,
  .comic-rig .comic-impact,
  .comic-rig .comic-hint,
  .comic-rig .comic-zoom-toggle {
    display: none;
  }

  .comic-rig .comic-throw,
  .comic-rig .comic-book {
    width: auto;
    height: auto;
    border: 0;
    box-shadow: none;
    transform: none !important;
    transform-style: flat;
  }

  .comic-rig .comic-controls { position: static; }

  .comic-rig .comic-pages {
    position: static;
    perspective: none;
    overflow: visible;
    cursor: auto;
  }

  .comic-rig .leaf { display: contents; }

  .comic-rig .book-gutter { display: none; }

  .comic-rig .leaf-face,
  .comic-rig .page-base {
    position: static;
    width: auto;
    transform: none !important;
    backface-visibility: visible;
    overflow: visible;
  }
}

/*
 * The plain vertical document, with or without the rig. A page that is not a
 * leaf has no fixed height, so its panels need a floor to stand on or a `1fr`
 * row collapses to the height of one line of type.
 */
body:not(.comic-rig) .comic-page,
.comic-plain .comic-page {
  min-height: 60svh;
  padding: 1.25rem;
  gap: 0.75rem;
}

body:not(.comic-rig) .panel,
.comic-plain .panel {
  padding: 1rem;
  gap: 0.75rem;
}

body:not(.comic-rig) .bubble,
body:not(.comic-rig) .bubble-sm,
.comic-plain .bubble,
.comic-plain .bubble-sm {
  font-size: 1.0625rem;
}

body:not(.comic-rig) .cast,
.comic-plain .cast { width: 84px; }

body:not(.comic-rig) .cast-sm,
.comic-plain .cast-sm { width: 64px; }

body:not(.comic-rig) .panel-shout,
.comic-plain .panel-shout { font-size: clamp(1.6rem, 4.5vw, 3rem); }

body:not(.comic-rig) .panel-shout-sm,
.comic-plain .panel-shout-sm { font-size: clamp(1.3rem, 3.4vw, 2.1rem); }

body:not(.comic-rig) .cover-title,
.comic-plain .cover-title { font-size: clamp(3rem, 12vw, 6rem); }
