/* Site styles, on tokens.css semantic and component tokens only (Plumb's rule).
   A ruled sheet: cells sit on 1px gaps over the grid-line colour. */

/* Self-hosted and subset (tokens.css, Font loading). Licences: /fonts/OFL.txt, /fonts/OFL-space-grotesk.txt */
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-mono-700.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Space Grotesk";
  src: url("/fonts/space-grotesk-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

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

html {
  font-family: var(--type-ui-family);
  font-size: 100%;
  line-height: var(--type-body-leading);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  padding: var(--space-page-gutter);
  background: var(--color-bg-page);
  color: var(--color-text);
  display: flex;
  flex-direction: column;
  gap: var(--space-page-gutter);
  transition: background-color var(--duration-hover), color var(--duration-hover);
}

h1 {
  font-family: var(--type-display-family);
  font-size: var(--type-display-size);
  font-weight: var(--type-display-weight);
  line-height: var(--type-display-leading);
  letter-spacing: var(--type-display-tracking);
}
h2 {
  font-family: var(--type-heading-family);
  font-size: var(--type-heading-size);
  font-weight: var(--type-heading-weight);
  line-height: var(--type-heading-leading);
  letter-spacing: var(--type-heading-tracking);
}
h1, h2 { margin: 0 0 var(--space-stack-tight); }
p { margin: 0 0 var(--space-stack-tight); }

a {
  color: var(--color-text-accent);
  text-decoration-thickness: var(--link-underline);
  text-underline-offset: var(--link-underline-offset);
  transition: text-decoration-thickness var(--duration-hover);
}
a:hover { text-decoration-thickness: var(--link-underline-hover); }

:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* ── Sheet and cells ─────────────────────────────────────────── */

.sheet {
  display: grid;
  gap: var(--line-hair);
  background: var(--color-line-grid);
  border: var(--border-grid);
  max-width: var(--layout-max);
  width: 100%;
  margin-inline: auto;
}

.cell {
  position: relative;
  background: var(--color-bg-cell);
  padding: var(--space-cell);
  margin: 0;
  min-width: 0;
  transition: background-color var(--duration-hover);
}
/* Cells are still: they never react to hover or focus (Plumb, P1). */

.sheet--bar {
  grid-template-columns: 1fr auto;
}
.sheet--bar .cell { padding-block: var(--space-cell-bar); }
.sheet--bar:has(> :only-child) { grid-template-columns: 1fr; }

main.sheet {
  flex: 1;
  grid-template-columns: 1fr;
}

/* Phones stack About before Selected work, and Contact last (Zoe, #35).
   The numbers and the desktop keep source order. */
@media (max-width: 47.99rem) {
  .cell--about   { order: 1; }
  .cell--work    { order: 2; }
  .cell--clients { order: 3; }
  .cell--contact { order: 4; }
}

/* Asymmetric on wide screens: uneven columns, uneven rows. */
@media (min-width: 48rem) {
  main.sheet {
    grid-template-columns: 5fr 3fr;
    grid-auto-rows: minmax(10rem, auto);
  }
  .cell--hero     { grid-row: span 2; min-height: 22rem; }
  .cell--clients  { grid-column: 1 / -1; }
}

.cell--hero {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.cell--specimen {
  display: flex;
  flex-direction: column;
  min-height: 12rem;
}
.cell--specimen .corner + * { margin: auto; }
/* His reply under reduced motion: the caption's voice, one step quieter (#36).
   It clears his 90px sprite, so the line never runs behind him. */
.beepus-reply {
  margin: 0 0 90px;
  color: var(--color-text-muted);
  font-size: var(--type-small-size);
}

/* Orange punctuation: two squares on the whole page (hero, specimen). No more. */
.punct {
  position: absolute;
  top: var(--space-cell);
  right: var(--space-cell);
  width: var(--mark-size);
  height: var(--mark-size);
  background: var(--color-accent-graphic);
}

/* ── Text roles ──────────────────────────────────────────────── */

.label {
  font-family: var(--type-label-family);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  line-height: var(--type-label-leading);
  letter-spacing: var(--type-label-tracking);
  text-transform: var(--type-label-case);
  color: var(--color-text-muted);
}
.corner { margin-bottom: var(--space-corner); }
/* Section and figure numbers come from CSS, with empty alt text, so they're not read out (Plumb, P1). */
.corner[data-num]::before { content: attr(data-num) " — " / ""; }
.corner--dot[data-num]::before { content: attr(data-num) " · " / ""; }
.title {
  font-size: var(--type-title-size);
  font-weight: var(--type-title-weight);
  line-height: var(--type-title-leading);
}
.lede   { max-width: var(--measure-lede); color: var(--color-text-muted); }
.cell--about p { max-width: var(--measure-prose); }

/* Running text opts into the body face; everything else stays on the UI face. */
.lede,
.cell--about p {
  font-family: var(--type-body-family);
  font-synthesis: var(--type-body-synthesis);
}
.lede strong,
.cell--about p strong {
  font-family: var(--type-body-strong-family);
  font-weight: var(--type-body-strong-weight);
}
.muted  { color: var(--color-text-muted); }

.wordmarks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-inline-tight) var(--space-stack-loose);
  font-weight: var(--type-title-weight);
}

/* Footer credits: small mono, sentence case, under the © line (Cairn, Folio). */
.footer-credits p { margin: 0; }
.credit {
  font-family: var(--type-label-family);
  font-size: var(--type-label-size);
  line-height: var(--type-label-leading);
  color: var(--color-text-muted);
}
.footer-credits .label { margin-bottom: var(--space-half); }

/* ── Controls ────────────────────────────────────────────────── */

.button {
  display: inline-block;
  padding: var(--button-pad-block) var(--button-pad-inline);
  background: var(--button-bg-pressed);
  color: var(--button-text-pressed);
  border: var(--line-hair) solid var(--button-border-pressed);
  font-weight: var(--type-title-weight);
  text-decoration: none;
}

/* Quiet buttons ("Let Beepus nap", "Reset"): text only, underlined like a link (Plumb, P4). */
.quiet-button {
  justify-self: start;
  min-height: var(--target-min);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--quiet-button-color);
}
.quiet-button,
.beepus-home:is([data-state="awake"], [data-state="away"]) .beepus-text {
  font-family: var(--type-label-family);
  font-size: var(--type-label-size);
  letter-spacing: var(--type-label-tracking);
  text-transform: var(--type-label-case);
  text-decoration: underline;
  text-decoration-thickness: var(--quiet-button-underline);
  text-underline-offset: var(--link-underline-offset);
  transition: text-decoration-thickness var(--duration-hover);
}
.quiet-button:hover,
.beepus-home:hover .beepus-text { text-decoration-thickness: var(--quiet-button-underline-hover); }
.quiet-button[hidden] { display: none; }

.theme-switch {
  border: 0;
  display: flex;
  align-items: center;
  gap: var(--space-stack);
  font-size: var(--type-label-size);
}
.theme-switch legend { float: left; margin-right: var(--space-inline-tight); padding: 0; }
.theme-switch label  { display: flex; align-items: center; gap: var(--space-half); min-height: var(--target-min); cursor: pointer; }
.theme-switch input  { accent-color: var(--color-control-accent); margin: 0; }

.nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-inline-tight) var(--space-inline);
  font-size: var(--type-small-size);
}
.nav .label { margin-left: var(--space-half); }

@media (max-width: 30rem) {
  .sheet--bar { grid-template-columns: 1fr; }
}

/* ── Beepus ──────────────────────────────────────────────────
   Keeper's cat (claude/world-spec.md, Beepus). Asleep, he is this
   button and breathes in CSS only. js/beepus.js loads on the first
   click and walks him around the page as .beepus-actor.
   Sprite cells are 120 × 90 CSS px; his floor point is (67, 87). */

.beepus-home {
  position: absolute;
  left: calc(46% - 67px);   /* floor point near the middle of his bed */
  bottom: -3px;             /* floor point on the cell's bottom edge */
  z-index: var(--layer-pet-home);
  width: 120px;
  height: 90px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--quiet-button-color);
  font: inherit;
  cursor: pointer;
}
.beepus-sprite { position: absolute; inset: 0; }
.beepus-sprite,
.beepus-actor.is-asleep {
  background: url("/beepus/beepus-asleep.webp") 0 0 / 240px 90px no-repeat;
  animation: beepus-breathe 3.2s steps(2) infinite;
}
@keyframes beepus-breathe { to { background-position: -240px 0; } }

/* A dark edge in light mode, so he holds 3:1 on the pale ground (Plumb, P8). None in dark. */
.beepus-sprite,
.beepus-actor { filter: var(--beepus-edge); }

/* "Wake Beepus" is his name while he sleeps. Awake or away, "Let Beepus nap" shows as a quiet button. */
.beepus-text {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.beepus-home:is([data-state="awake"], [data-state="away"]) .beepus-sprite { display: none; }
.beepus-home:is([data-state="awake"], [data-state="away"]) .beepus-text {
  inset: auto 0 var(--space-stack);
  width: auto;
  height: auto;
  min-height: var(--target-min);
  display: flex;
  align-items: center;
  justify-content: center;
  clip: auto;
}

.beepus-actor {
  position: absolute;
  left: 0;
  top: 0;
  z-index: var(--layer-pet);
  width: 120px;
  height: 90px;
  background-repeat: no-repeat;
  pointer-events: none;     /* awake, clicks pass straight through him */
}
.beepus-actor.is-asleep { pointer-events: auto; cursor: pointer; }

/* A paw is a nudge, never a press (Plumb, P2, P3): out in his facing direction
   and back. No colour, weight or underline change. Under reduced motion the
   shift, lift and tilt are 0, so nothing moves. */
[data-beepus="bat"] { display: inline-block; }
.is-batted { animation: beepus-batted var(--beepus-bat-duration) var(--ease-standard); }
@keyframes beepus-batted {
  30% {
    transform:
      translate(calc(var(--beepus-push, 1) * var(--beepus-bat-shift)), calc(-1 * var(--beepus-bat-lift)))
      rotate(calc(var(--beepus-push, 1) * var(--beepus-bat-tilt)));
  }
}

/* ── Utilities ───────────────────────────────────────────────── */

.skip {
  position: absolute;
  z-index: var(--layer-skip);
  left: var(--space-page-gutter);
  top: -10rem;
  padding: var(--space-inline-tight) var(--space-stack);
  background: var(--color-bg-inverse);
  color: var(--color-text-inverse);
}
.skip:focus { top: var(--space-page-gutter); }

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

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .beepus-sprite, .beepus-actor { animation: none; }
}
