/* ============================================
   v4 — Refined
   Best of v1 (bento, char hero, mouseover),
   v2 (multi-color heads, left/right intros, game cards),
   v3 (floating pill nav — morphed in from a top bar).
   No diamond mark. CTA in every section.
   ============================================ */

/* ============================================
   MORPHING NAV
   - Starts as full-width bar (v1 feel)
   - On scroll past threshold, contracts into a
     centered floating pill (v3 feel)
   - Same element. Animates max-width / padding /
     border-radius / margin-top / shadow / bg.
   ============================================ */
.smartnav {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;

  /* The geometry is ALWAYS the pill's: same width, padding and item
     positions at the top as when floating. Nothing moves on scroll — the
     pill's surface just fades in around the items (#2). */
  width: 100%;
  max-width: min(920px, calc(100vw - 32px));
  margin-top: 12px;

  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 22px;
  padding: 10px 14px 10px 22px;

  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  box-shadow: 0 0 0 transparent;

  transition:
    background-color 400ms var(--ease-inout),
    border-color 400ms var(--ease-inout),
    box-shadow 500ms var(--ease-out);
}

.smartnav.is-floating {
  /* Only the surface appears — the items are already exactly here. */
  background: color-mix(in srgb, var(--bg-card) 92%, transparent);
  border-color: var(--hairline);
  box-shadow: 0 12px 34px color-mix(in srgb, var(--primary) 9%, transparent),
              0 2px 8px color-mix(in srgb, #000 5%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

/* --- Brand (no diamond, just wordmark) --- */
.smartnav__brand {
  display: inline-flex;
  align-items: baseline;
  gap: 0;
  text-decoration: none;
  color: var(--text-main);
  position: relative;
}
.smartnav__wordmark {
  font-family: var(--font-logo);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.01em;
  transition: font-size 500ms var(--ease-out);
}
/* is-floating no longer resizes the wordmark — it stays put (#2). */

.smartnav__domain {
  font-family: var(--font-logo);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.01em;
  color: var(--primary-text);
  transition: color var(--d-slow), font-size 500ms var(--ease-out);
}
/* is-floating no longer resizes the domain suffix (#2). */

/* --- Center links --- */
.smartnav__links {
  display: flex;
  justify-content: center;
  gap: 6px;
  transition: gap 400ms var(--ease-out);
}
.smartnav__links a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  text-decoration: none;
  border-radius: 999px;
  transition: background-color var(--d-base), color var(--d-base), padding 400ms var(--ease-out), font-size 500ms var(--ease-out);
  position: relative;
}
.smartnav__links a:hover {
  background: var(--base-elev);
  color: var(--text-main);
}
.smartnav__links a.is-active {
  color: var(--text-main);
  background: color-mix(in srgb, var(--primary) 8%, var(--bg-card));
}
.smartnav__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--link-color, var(--text-subtle));
}
.smartnav__links a[data-pillar="ai"]    { --link-color: var(--ai-primary); }
.smartnav__links a[data-pillar="games"] { --link-color: var(--games-primary); }
.smartnav__links a[data-pillar="labs"]  { --link-color: var(--labs-primary); }

/* --- Right cluster --- */
.smartnav__right {
  display: flex;
  align-items: center;
  gap: 10px;
}
.smartnav__theme {
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 1px solid var(--hairline);
  background: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: border-color var(--d-base), transform var(--d-fast) var(--spring);
}
.smartnav__theme:hover { border-color: var(--primary); transform: rotate(20deg); }
.smartnav__theme-icon {
  width: 13px; height: 13px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px var(--text-main);
  position: relative;
  overflow: hidden;
}
.smartnav__theme-icon::after {
  content: '';
  position: absolute;
  inset: 0 0 0 50%;
  background: var(--text-main);
}
.smartnav .btn--nav { padding: 8px 16px; font-size: 12px; }
/* is-floating keeps the contact button + theme toggle at their bar size (#2). */

/* --- Accent follows the scroll (#8): the ".ai" suffix and the Contact
   button shift colour continuously with the background wash. landscape.js
   sets --nav-accent from scroll progress; sub-pages fall back to --primary. --- */
.smartnav__domain { color: var(--nav-accent, var(--primary)); transition: color 80ms linear; }
.smartnav .btn--nav.btn--primary {
  background: var(--nav-accent, var(--primary));
  border-color: var(--nav-accent, var(--primary));
  transition: background-color 80ms linear, border-color 80ms linear,
              transform var(--d-fast) var(--spring);
}

/* --- Language switcher (JP/EN) — placeholder until the JA build lands --- */
.smartnav__lang {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
}
.smartnav__lang-cur { color: var(--text-main); font-weight: 500; }
.smartnav__lang-sep { color: var(--text-subtle); }
/* A real link again as of 2026-08-26, in both directions, because /ja/ ships.
   NOT dimmed. It carried opacity 0.55 until 2026-08-26, on top of
   --text-subtle, which tokens.css sets at exactly 4.52:1. The composite
   measured 2.08:1 against a 4.5:1 requirement at 11px: WCAG 1.4.3 Contrast
   (Minimum), Level AA. Do not reintroduce opacity here. */
.smartnav__lang-alt {
  color: var(--text-subtle);
  text-decoration: none;
  cursor: pointer;
  transition: color var(--d-base);
}
.smartnav__lang-alt:hover,
.smartnav__lang-alt:focus-visible { color: var(--text-main); }
/* The Japanese label is set in the Japanese UI face at both ends of the
   switcher. In the monospace Latin face of .smartnav__lang it would fall back
   to whatever the OS picks and sit at a different optical size to the EN. */
.smartnav__lang-alt[lang="ja"],
.smartnav__lang-cur[lang="ja"] {
  font-family: var(--font-jp-sans);
  letter-spacing: 0.02em;
}

@media (max-width: 760px) {
  .smartnav { padding: 14px 18px; gap: 12px; }
  .smartnav__links { display: none; }
  /* The language switch survives on mobile as just the OTHER language — one
     compact link: 日本語 on English pages, EN on Japanese ones. The current
     label and separator are decoration a phone header cannot afford. The
     bilingual legal pages render only current-labels (no sibling to offer),
     so their group hides entirely, as before. Chosen by Joe, 2026-08-28,
     launch-day walk: previously the whole switcher was display:none here
     and mobile had no language switch at all. */
  .smartnav__lang-cur,
  .smartnav__lang-sep { display: none; }
  .smartnav__lang:not(:has(.smartnav__lang-alt)) { display: none; }
  .smartnav__lang { font-size: 14px; }
  /* Bigger tap target without moving the layout. */
  .smartnav__lang-alt { padding: 10px; margin: -10px; }
}

/* ============================================
   HERO (v1's char-in animation)
   ============================================ */
.hero4 {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 140px 24px 80px;
  overflow: hidden;
}
.hero4::before {
  content: '';
  position: absolute;
  inset: -10%;
  background:
    radial-gradient(ellipse 60% 50% at 50% 35%,
      color-mix(in srgb, var(--primary) 7%, transparent) 0%,
      transparent 60%);
  z-index: 0;
  pointer-events: none;
  filter: blur(28px);
}
.hero4__inner {
  position: relative;
  z-index: 2;
  text-align: center;
  max-width: 980px;
}

.hero4__meta {
  display: inline-flex;
  margin: 0 auto 36px;
  align-items: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 8px 16px;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: color-mix(in srgb, var(--bg-card) 70%, transparent);
}
.hero4__meta-dot {
  width: 7px; height: 7px;
  background: var(--primary);
  border-radius: 50%;
  position: relative;
}
.hero4__meta-dot::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 35%, transparent);
  animation: hero4-pulse 2.6s ease-out infinite;
}
@keyframes hero4-pulse {
  0%   { transform: scale(0.6); opacity: 0.9; }
  100% { transform: scale(1.9); opacity: 0; }
}
[data-motion="off"] .hero4__meta-dot::after { animation: none; }

/* Char-by-char animated wordmark (from v1) */
/* The h1 now wraps both the wordmark and the sentence, so it needs to be a
   plain container: no inherited h1 size, margin or weight. */
.hero4__h1 {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
}
.hero4__h1 > .hero4__wordmark,
.hero4__h1 > .hero4__lede { display: block; }

.hero4__wordmark {
  font-family: var(--font-logo);
  font-weight: 500;
  font-size: clamp(96px, 18vw, 240px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  color: var(--text-main);
  margin: 0 auto 24px;
  display: flex;
  justify-content: center;
  flex-wrap: nowrap;
  white-space: nowrap;
  width: fit-content;
  user-select: none;
}
.hero4__char { display: inline-block; }
.js-reveal .hero4__char {
  animation: hero4-char-in 900ms var(--ease-out) backwards;
  animation-delay: calc(140ms + var(--i) * 60ms);
}
@keyframes hero4-char-in {
  from { opacity: 0; transform: translateY(48px) rotate(-2deg); }
  to   { opacity: 1; transform: translateY(0) rotate(0); }
}
[data-motion="off"] .hero4__char { animation: none; }

.hero4__lede {
  font-family: var(--font-display);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(20px, 2.2vw, 28px);
  line-height: 1.4;
  color: var(--text-main);
  max-width: 680px;
  margin: 0 auto 12px;
  text-wrap: balance;
}
.hero4__lede em {
  font-style: italic;
  color: var(--primary-text);
  font-weight: 400;
}

.hero4__cta {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 0;
}

.hero4__verticals {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  max-width: 760px;
  margin: 0 auto;
}
.hero4__chip {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: baseline;
  gap: 10px 12px;
  padding: 14px 16px;
  background: color-mix(in srgb, var(--bg-card) 85%, transparent);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--text-main);
  text-align: left;
  transition: transform var(--d-base) var(--ease-out),
              border-color var(--d-base),
              background-color var(--d-base);
}
.hero4__chip:hover {
  transform: translateY(-3px);
  border-color: var(--chip-color);
  background: color-mix(in srgb, var(--chip-color) 4%, var(--bg-card));
}
[data-motion="off"] .hero4__chip:hover { transform: none; }
.hero4__chip-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--chip-color);
  align-self: center;
}
.hero4__chip-num {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--text-subtle);
}
.hero4__chip-name {
  font-family: var(--font-logo);
  font-weight: 500;
  font-size: 16px;
}
.hero4__chip-domain { color: var(--chip-color); }
.hero4__chip-desc {
  grid-column: 1 / -1;
  padding-left: 20px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 0.04em;
}
.hero4__chip[data-pillar="ai"]    { --chip-color: var(--ai-primary); }
.hero4__chip[data-pillar="games"] { --chip-color: var(--games-primary); }
.hero4__chip[data-pillar="labs"]  { --chip-color: var(--labs-primary); }
@media (max-width: 640px) {
  .hero4__verticals { grid-template-columns: 1fr; }
}

/* ============================================
   SECTION SCAFFOLDING
   Multi-color heads (each section's primary)
   Left intro / right lede + CTA
   ============================================ */
.s4 {
  position: relative;
  z-index: 1;
  padding: var(--section-pad) 0;
}
.s4__inner {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 32px;
}

/* Head: left intro, right lede + CTA */
.s4__head {
  display: grid;
  /* Was 1.15fr. The h2 is written to break on its own <br> into two lines;
     at 1.15fr the left column was 578px and every heading rewrapped to four
     lines with a one-word orphan on the last. */
  grid-template-columns: 1.4fr 1fr;
  gap: 56px;
  /* Was `end`. Bottom-alignment only ever looked deliberate because the
     left column happened to be taller than the right; the moment that
     flipped, the two columns started at different heights and the top of
     the section went ragged. Align the tops so it holds whatever the
     heading and lede lengths are. */
  align-items: start;
  margin-bottom: 72px;
}
/* Keep the CTA anchored to the foot of the right column so it does not
   float directly under a short lede. */
.s4__head-right { min-height: 100%; }
.s4__head-cta { margin-top: auto; }
.s4__head-left { display: flex; flex-direction: column; gap: 14px; }
.s4__head-right {
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding-bottom: 4px;
}
@media (max-width: 880px) {
  .s4__head { grid-template-columns: 1fr; gap: 28px; }
  .s4__head-right { gap: 20px; }
}

/* Chapter mark — adopts section --primary automatically */
/* Sizing (Joe 2026-07-26, asked for 2x). A flat 22px of 0.18em-tracked
   uppercase mono ran about 330px wide and sat directly above a 58px
   heading, so the page had two headlines and no hierarchy. The numeral
   takes the full 2x, where the extra weight reads as an editorial chapter
   number; the word goes to 15px, which is a 1.4x bump and clears the
   14px floor for meaningful mono. Tracking comes down because tracked-out
   type gets harder to read as it gets bigger, not easier. */
.s4__chapter {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  font-family: var(--font-mono);
  font-size: clamp(13px, 1.05vw, 15px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.s4__chapter-num {
  color: var(--primary-text);
  font-weight: 600;
  font-size: 1.5em;         /* 22.5px at desktop: the 2x, on the numeral */
  letter-spacing: 0.06em;
  line-height: 1;
}
.s4__chapter-line {
  width: 44px;
  height: 1px;
  background: var(--primary);
  transition: background var(--d-slow);
}
/* This label is what tells a reader which section they are in, so it gets
   full-strength ink rather than the muted tier. */
.s4__chapter-name { color: var(--text-main); font-weight: 500; }

.s4__h {
  font-family: var(--font-display);
  font-weight: 300;
  /* Was clamp(40px, 5.4vw, 70px). 70px overran the column and forced a
     four-line rag; 58px lets the authored two-line break stand. */
  font-size: clamp(38px, 4.2vw, 58px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--text-main);
  text-wrap: balance;
}
.s4__h em {
  font-style: italic;
  color: var(--primary-text);
  font-weight: 400;
}
.s4__lede {
  font-family: var(--font-body);
  font-size: clamp(15px, 1.15vw, 17px);
  line-height: 1.75;
  /* Darker than --text-muted: these ledes sit on the tinted landscape
     background and were hard to read (Joe 2026-07-20). */
  color: color-mix(in srgb, var(--text-main) 78%, var(--text-muted));
  max-width: 50ch;
  text-wrap: pretty;
}

.s4__head-cta {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

/* Section foot CTA (in addition to head CTA) */
.s4__foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 48px;
  padding-top: 32px;
  border-top: 1px solid var(--hairline);
  gap: 24px;
  flex-wrap: wrap;
}
.s4__foot-note {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

/* ============================================
   BENTO — three distinct shapes
   .bento4 is just the shared cell scaffold.
   Each variant defines its own grid silhouette:
     --pillars  pococo.ai · tall featured left  + 2 stacked + wide bottom band
     --games    games     · full-width banner top + 2 equal squares below
     --labs     labs      · big 2×2 featured + wide top-right + 2 small bottom-right
   ============================================ */
.bento4 {
  display: grid;
  gap: 20px;
}

/* === SHAPE 1: pococo.ai — Pillars + footer band === */
.bento4--pillars {
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto;
}
/* uses .bento4__cell--lg (1×2 tall) and .bento4__cell--wide (2×1) modifiers */

/* === SHAPE 2: games — Cinema strip === */
.bento4--games {
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: minmax(360px, auto) auto;
}
.bento4--games > :nth-child(1) { grid-column: 1 / -1; grid-row: 1; }
.bento4--games > :nth-child(2) { grid-column: 1;       grid-row: 2; }
.bento4--games > :nth-child(3) { grid-column: 2;       grid-row: 2; }

/* === SHAPE 3: labs — Plaza mosaic === */
.bento4--labs {
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: minmax(220px, auto) minmax(220px, auto);
}
.bento4--labs > :nth-child(1) { grid-column: 1 / 3; grid-row: 1 / 3; }  /* big featured 2×2 */
.bento4--labs > :nth-child(2) { grid-column: 3 / 5; grid-row: 1; }      /* wide top-right */
.bento4--labs > :nth-child(3) { grid-column: 3;     grid-row: 2; }      /* small bottom L */
.bento4--labs > :nth-child(4) { grid-column: 4;     grid-row: 2; }      /* small bottom R */

.bento4__cell {
  background: var(--bg-card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: var(--sp-8);
  display: flex;
  flex-direction: column;
  gap: 8px;
  transition: transform var(--d-slow) var(--ease-out),
              box-shadow var(--d-slow) var(--ease-out),
              border-color var(--d-base);
  position: relative;
  overflow: hidden;
}
.bento4__cell::after {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--primary);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 500ms var(--ease-out); /* fallback bars keep pace with .edge-hl (500ms) */
}
.bento4__cell:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--primary) 22%, var(--hairline));
  box-shadow: 0 14px 40px color-mix(in srgb, var(--primary) 8%, transparent);
}
[data-theme="dark"] .bento4__cell:hover {
  border-color: color-mix(in srgb, var(--primary) 38%, var(--hairline));
  box-shadow:
    0 14px 40px color-mix(in srgb, var(--primary) 22%, transparent),
    0 0 0 1px color-mix(in srgb, var(--primary) 20%, transparent);
}
.bento4__cell:hover::after { transform: scaleX(1); }
[data-motion="off"] .bento4__cell:hover { transform: none; }
/* Accent highlight, all bento cells (Joe 2026-07-20): a bar resting on
   the left edge that travels around the top-left corner on hover. The
   real motion is the JS-injected SVG below (.edge-hl), whose path traces
   the card's actual outline — these two pseudo bars are only the no-JS
   fallback and stand down once .has-edge-hl lands. --bar-h sets the
   resting length per cell. */
.bento4__cell::before {
  content: '';
  position: absolute;
  left: 0;
  top: 18px;
  height: var(--bar-h, 38%);
  width: 2px;
  border-radius: 0 2px 2px 0;
  background: var(--primary);
  transform-origin: top;
  transition: transform 500ms var(--ease-out); /* matches ::after — one bar, one speed */
  z-index: 2;
}
.bento4__cell:hover::before {
  transform: translateY(-18px) scaleY(0);
}
/* Media cards (games/labs) put art at the top; both accent strips must
   paint above it. Harmless on text-only cells. */
.bento4__cell::after { z-index: 2; }

/* SVG edge highlight (JS-injected; see initBentoEdges in site.js): the
   dash rides a path drawn along the card's real outline, so the turn
   happens on the actual rounded corner. Dasharray + dashoffset move
   together — the dash slides up the side, bends through the arc, and
   stretches across the top; mouse-out runs the same path in reverse. */
.edge-hl {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}
.edge-hl path {
  fill: none;
  stroke: var(--primary);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: var(--ehl-rest, 0 9999);
  stroke-dashoffset: 0;
  transition: stroke-dasharray 500ms var(--ease-out),  /* was 750 — +50% speed (Joe 2026-07-20) */
              stroke-dashoffset 500ms var(--ease-out);
}
.bento4__cell:hover .edge-hl path {
  stroke-dasharray: var(--ehl-hover, var(--ehl-rest, 0 9999));
  stroke-dashoffset: var(--ehl-shift, 0);
}
[data-motion="off"] .edge-hl path { transition: none; }
/* JS present → pseudo fallback bars retire. */
.bento4__cell.has-edge-hl::before,
.bento4__cell.has-edge-hl::after { content: none; }

/* Pillar cells: slightly tighter than the 32px default so the trio sits
   a bit shorter (Joe 2026-07-20). */
.bento4--pillars .bento4__cell { padding: 26px 28px; }

/* Bar lengths. Pillars: 01 keeps the tall default (38%); training +
   tooling get equal fixed bars that run beside the title and the first
   paragraph. Media cards (games/labs): a shorter fixed accent along the
   art edge. */
.bento4--pillars-3 > .bento4__cell:nth-child(2),
.bento4--pillars-3 > .bento4__cell:nth-child(3) { --bar-h: 150px; }
.bento4--games .bento4__cell,
.bento4--labs .bento4__cell { --bar-h: 110px; }

/* Concrete everyday examples per pillar card: a tiny label, then one or
   more before → after lines. Examples show what the VISITOR could do
   (famous tools welcome), not what we've done (Joe 2026-07-20).

   These were `margin-top: auto`, which pinned them to the card floor and
   left 139px of void under the body copy in card 01. They now sit directly
   under the paragraph they illustrate. They were also 11.5px mono at ~69
   characters a line, which made the most persuasive block on the page the
   hardest to read; body font, body size now. */
.bento4__eg {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.65;
  letter-spacing: 0;
  color: var(--text-muted);
  text-wrap: pretty;
}
.bento4__eg-line { display: block; }
.bento4__eg-line + .bento4__eg-line { margin-top: 13px; }
/* Keep the arrow welded to the words on either side so a wrap never
   strands the outcome on a line of its own. */
.bento4__eg-line .bento4__eg-to {
  color: var(--primary-text);
  padding: 0 2px;
  white-space: nowrap;
}
.bento4__eg-label {
  display: block;
  margin-bottom: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-subtle);
}
.bento4__cell--lg {
  grid-column: span 1;
  grid-row: span 2;
  padding: 40px 36px;
}
.bento4__cell--wide { grid-column: span 2; }

.bento4__index {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: lowercase;
  color: var(--primary-text);
  margin-bottom: 12px;
}
.bento4__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 1.7vw, 26px);
  line-height: 1.25;
  color: var(--text-main);
  text-wrap: balance;
}
.bento4__body {
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-muted);
}
.bento4__bullets {
  list-style: none;
  margin-top: auto;
  padding-top: 24px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.bento4__bullets li {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
  padding-left: 16px;
  position: relative;
}
/* Bullet marker: a small accent diamond, not a dash (#7). */
.bento4__bullets li { padding-left: 18px; }
.bento4__bullets li::before {
  content: '';
  position: absolute;
  left: 1px; top: 6px;
  width: 5px; height: 5px;
  background: var(--primary);
  transform: rotate(45deg);
}

@media (max-width: 960px) {
  .bento4--pillars { grid-template-columns: repeat(2, 1fr); }
  .bento4--pillars .bento4__cell--lg { grid-row: auto; grid-column: span 2; }
  .bento4--pillars .bento4__cell--wide { grid-column: span 2; }

  .bento4--labs { grid-template-columns: repeat(2, 1fr); grid-template-rows: auto; }
  .bento4--labs > :nth-child(n) { grid-column: auto; grid-row: auto; }
  .bento4--labs > :nth-child(1) { grid-column: span 2; }
}
@media (max-width: 560px) {
  .bento4--pillars { grid-template-columns: 1fr; }
  .bento4--pillars .bento4__cell--lg,
  .bento4--pillars .bento4__cell--wide { grid-column: span 1; }

  .bento4--games { grid-template-columns: 1fr; grid-template-rows: auto; }
  .bento4--games > :nth-child(n) { grid-column: 1; grid-row: auto; }

  .bento4--labs { grid-template-columns: 1fr; }
  .bento4--labs > :nth-child(n) { grid-column: 1; }
}

/* ============================================
   GAMES — bento variant
   Same scaffold as .bento4, but each cell is a
   game card with a prominent screenshot/key-art
   slot. Accent: --games-primary.
   ============================================ */
.bento4--games .bento4__cell {
  padding: 0;
  gap: 0;
  overflow: hidden;
}
.bento4--games .bento4__cell::after { background: var(--games-primary); }
.bento4--games .bento4__cell:hover {
  border-color: color-mix(in srgb, var(--games-primary) 25%, var(--hairline));
  box-shadow: 0 18px 44px color-mix(in srgb, var(--games-primary) 10%, transparent);
}
[data-theme="dark"] .bento4--games .bento4__cell:hover {
  border-color: color-mix(in srgb, var(--games-primary) 42%, var(--hairline));
  box-shadow:
    0 18px 44px color-mix(in srgb, var(--games-primary) 24%, transparent),
    0 0 0 1px color-mix(in srgb, var(--games-primary) 22%, transparent);
}

.gameb {
  display: flex;
  flex-direction: column;
}
.gameb--horizontal {
  flex-direction: row;
  align-items: stretch;
}

/* Banner = the cinema-strip hero card. Horizontal but image-dominant. */
.gameb--banner {
  flex-direction: row;
  align-items: stretch;
}
.gameb--banner .gameb__shot--side {
  flex: 0 0 58%;
  min-height: 100%;
  border-right: 1px solid var(--hairline);
  border-bottom: none;
}
.gameb--banner .gameb__body {
  padding: 40px 44px;
  justify-content: center;
}
.gameb--banner .gameb__name {
  font-size: clamp(28px, 2.6vw, 38px);
}
.gameb--banner .gameb__desc {
  font-size: 15px;
  max-width: 44ch;
}
@media (max-width: 960px) {
  .gameb--banner { flex-direction: column; }
  .gameb--banner .gameb__shot--side {
    flex: 0 0 auto;
    min-width: 0;
    min-height: 280px;
    border-right: none;
    border-bottom: 1px solid var(--hairline);
  }
}

/* The screenshot / key-art slot — striped neutral placeholder.
   Status pill anchored to the slot so it reads like a state badge
   on the image, not floating text. */
.gameb__shot {
  position: relative;
  background:
    repeating-linear-gradient(
      135deg,
      color-mix(in srgb, var(--games-primary) 7%, var(--base-elev)) 0px,
      color-mix(in srgb, var(--games-primary) 7%, var(--base-elev)) 12px,
      color-mix(in srgb, var(--games-primary) 3%, var(--base-elev)) 12px,
      color-mix(in srgb, var(--games-primary) 3%, var(--base-elev)) 24px
    );
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 14px;
  gap: 8px;
}
.gameb__shot--lg {
  min-height: 320px;
  border-bottom: 1px solid var(--hairline);
}
.gameb__shot--side {
  min-width: 280px;
  flex: 0 0 38%;
  border-right: 1px solid var(--hairline);
}

.gameb__shot-caption {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: color-mix(in srgb, var(--bg-card) 88%, transparent);
  padding: 5px 9px;
  border: 1px dashed color-mix(in srgb, var(--text-muted) 35%, transparent);
  border-radius: 4px;
  align-self: flex-start;
}
.gameb__shot-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  background: color-mix(in srgb, var(--bg-card) 88%, transparent);
  padding: 5px 9px;
  border-radius: 4px;
  align-self: flex-end;
}
.gameb__shot-meta strong { color: var(--text-main); font-weight: 500; }

/* Status pill on the screenshot (top-right) */
.gameb__status {
  position: absolute;
  top: 14px;
  right: 14px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-main);
  background: color-mix(in srgb, var(--bg-card) 92%, transparent);
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.gameb__status-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--status-color, var(--text-subtle));
}
.gameb__status[data-state="dev"]     { --status-color: var(--games-primary); }
.gameb__status[data-state="proto"]   { --status-color: var(--gold); }
.gameb__status[data-state="concept"] { --status-color: var(--text-subtle); }

/* "Playable now" was also data-state="dev", so the one game a visitor can
   actually play looked identical to the two they can't. It now reads as a
   solid, filled pill in the live green rather than an outlined grey one. */
.gameb__status[data-state="live"] {
  --status-color: var(--base-card);
  color: var(--base-bg);
  background: var(--live);
  border-color: var(--live);
  font-weight: 500;
}
[data-theme="dark"] .gameb__status[data-state="live"] {
  color: #10201A;
  background: var(--live);
  border-color: var(--live);
}

.gameb__body {
  padding: 26px 28px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
  min-width: 0;
}
.gameb__genre {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--games-primary);
  margin-bottom: 8px;
}
.gameb__name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(22px, 1.9vw, 28px);
  line-height: 1.2;
  color: var(--text-main);
  text-wrap: balance;
}
.gameb__desc {
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-muted);
  max-width: 52ch;
}
.gameb__cta {
  margin-top: auto;
  padding-top: 18px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--games-primary);
  text-decoration: none;
  transition: gap var(--d-base);
}
.gameb__cta:hover { gap: 12px; }

@media (max-width: 960px) {
  .gameb--horizontal { flex-direction: column; }
  .gameb__shot--side {
    flex: 0 0 auto;
    min-width: 0;
    min-height: 200px;
    border-right: none;
    border-bottom: 1px solid var(--hairline);
  }
}

/* ============================================
   LABS — bento variant
   Same scaffold as .bento4, but each cell is a
   lab card with a "live demo" frame (chrome +
   prominent status pill). No numbering, no time.
   Accent: --labs-primary.
   ============================================ */
.bento4--labs .bento4__cell {
  padding: 0;
  gap: 0;
  overflow: hidden;
}
.bento4--labs .bento4__cell::after { background: var(--labs-primary); }
.bento4--labs .bento4__cell:hover {
  border-color: color-mix(in srgb, var(--labs-primary) 28%, var(--hairline));
  box-shadow: 0 18px 44px color-mix(in srgb, var(--labs-primary) 10%, transparent);
}
[data-theme="dark"] .bento4--labs .bento4__cell:hover {
  border-color: color-mix(in srgb, var(--labs-primary) 44%, var(--hairline));
  box-shadow:
    0 18px 44px color-mix(in srgb, var(--labs-primary) 24%, transparent),
    0 0 0 1px color-mix(in srgb, var(--labs-primary) 22%, transparent);
}

.labb {
  display: flex;
  flex-direction: column;
}
.labb--horizontal {
  flex-direction: row;
  align-items: stretch;
}
/* Compact = the small 1×1 tiles at the bottom of the labs mosaic */
.labb--compact .labb__preview { min-height: 130px; }
.labb--compact .labb__chrome { padding: 8px 12px; font-size: 10px; }
.labb--compact .labb__body { padding: 18px 20px 20px; gap: 6px; }
.labb--compact .labb__title { font-size: 18px; }
.labb--compact .labb__desc { font-size: 13px; line-height: 1.6; }
.labb--compact .labb__cta { padding-top: 10px; }

/* "Live / prototype" framed preview */
.labb__preview {
  position: relative;
  background:
    repeating-linear-gradient(
      135deg,
      color-mix(in srgb, var(--labs-primary) 6%, var(--base-elev)) 0px,
      color-mix(in srgb, var(--labs-primary) 6%, var(--base-elev)) 12px,
      color-mix(in srgb, var(--labs-primary) 2%, var(--base-elev)) 12px,
      color-mix(in srgb, var(--labs-primary) 2%, var(--base-elev)) 24px
    );
  display: flex;
  flex-direction: column;
  min-height: 200px;
  border-bottom: 1px solid var(--hairline);
}
.labb__preview--lg { min-height: 320px; }
.labb__preview--side {
  flex: 0 0 42%;
  min-width: 300px;
  border-bottom: none;
  border-right: 1px solid var(--hairline);
}

/* Browser-style chrome with dots + url + status */
.labb__chrome {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: color-mix(in srgb, var(--bg-card) 88%, transparent);
  border-bottom: 1px solid var(--hairline);
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.labb__chrome-dots {
  display: inline-flex;
  gap: 5px;
  align-items: center;
}
.labb__chrome-dots i {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--text-muted) 25%, transparent);
}
.labb__chrome-url {
  flex: 1;
  text-align: center;
  letter-spacing: 0.04em;
  /* The chrome bar is the darkest light surface on the page; --text-subtle
     measured 2.4:1 here. */
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.labb__status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 500;
  padding: 4px 10px 4px 8px;
  border-radius: 999px;
  margin-left: auto;
  white-space: nowrap;
}
/* "live" reads the same everywhere on the page: the same filled green pill
   the games section uses for "Playable now". The tinted gold version sat on
   the chrome bar at 3.9:1. */
.labb__status--live {
  background: var(--live);
  color: var(--base-bg);
}
[data-theme="dark"] .labb__status--live { color: #10201A; }
.labb__status--proto {
  background: color-mix(in srgb, var(--text-muted) 14%, transparent);
  color: var(--text-main);
}

/* Simul preview mock: the original page and its translation, side by side. */
.labb__preview--mock { display: flex; flex-direction: column; cursor: default; }
.simul-mock {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--hairline);
  min-height: 180px;
}
.simul-mock__pane {
  background: var(--bg-card);
  padding: 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.simul-mock__pane--yours {
  background: color-mix(in srgb, var(--labs-primary) 6%, var(--bg-card));
}
.simul-mock__label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.simul-mock__line {
  height: 8px;
  flex: 0 0 auto;
  border-radius: 4px;
  background: color-mix(in srgb, var(--text-muted) 18%, transparent);
}
.simul-mock__pane--yours .simul-mock__line {
  background: color-mix(in srgb, var(--labs-primary) 30%, transparent);
}
/* "Image" regions in the page samples; they flex to absorb leftover
   height so the portrait panes never show dead space. */
.simul-mock__block {
  flex: 1 1 40px;
  min-height: 28px;
  max-height: 140px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--text-muted) 10%, transparent);
}
.simul-mock__pane--yours .simul-mock__block {
  background: color-mix(in srgb, var(--labs-primary) 16%, transparent);
}
.labb__status-pulse {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  position: relative;
}
.labb__status--live .labb__status-pulse::after {
  content: '';
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.4;
  animation: labb-pulse 2.2s ease-out infinite;
}
@keyframes labb-pulse {
  0%   { transform: scale(0.6); opacity: 0.5; }
  100% { transform: scale(2.4); opacity: 0; }
}
[data-motion="off"] .labb__status--live .labb__status-pulse::after { animation: none; }

.labb__preview-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 14px;
  gap: 6px;
}
.labb__preview-caption {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: color-mix(in srgb, var(--bg-card) 88%, transparent);
  padding: 5px 9px;
  border: 1px dashed color-mix(in srgb, var(--text-muted) 35%, transparent);
  border-radius: 4px;
  align-self: flex-start;
}
.labb__preview-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
  align-self: flex-end;
  background: color-mix(in srgb, var(--bg-card) 88%, transparent);
  padding: 5px 9px;
  border-radius: 4px;
}

.labb__body {
  padding: 24px 26px 26px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
  min-width: 0;
}
.labb__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 2px;
}
.labb__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(20px, 1.7vw, 26px);
  line-height: 1.2;
  color: var(--text-main);
}
.labb__desc {
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-muted);
  max-width: 56ch;
}
.labb__cta {
  margin-top: auto;
  padding-top: 14px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--labs-primary);
  text-decoration: none;
  transition: gap var(--d-base);
}
.labb__cta:hover { gap: 12px; }

/* Whole-card click without a third duplicate anchor.
   The Guten Glance card previously held three separate links to the same
   URL (the preview, this CTA, and a .card-stretch overlay), so keyboard
   users hit the same destination three times and the overlay sat on top of
   the preview, making that one unreachable by mouse. One link now covers
   the card via ::after, and the secondary CTA is lifted above it. */
.labb__cta--stretch::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
}
.labb__cta--ghost {
  position: relative;
  z-index: 2;
  color: var(--text-muted);
}
.labb__cta--stretch:focus-visible { outline-offset: 4px; }

@media (max-width: 960px) {
  .labb--horizontal { flex-direction: column; }
  .labb__preview--side {
    flex: 0 0 auto;
    min-width: 0;
    min-height: 200px;
    border-right: none;
    border-bottom: 1px solid var(--hairline);
  }
}

/* ---- Dark-mode contrast pass for labs/games chrome ----
   --text-subtle (#72706C) drops below AA on dark card surfaces.
   Bump URL + caption to --text-muted, and brighten the chrome
   dots so the browser-frame metaphor still reads. */
[data-theme="dark"] .labb__chrome-url {
  color: var(--text-muted);
}
[data-theme="dark"] .labb__chrome-dots i {
  background: color-mix(in srgb, var(--text-muted) 45%, transparent);
}
[data-theme="dark"] .labb__preview-meta {
  color: var(--text-muted);
}
[data-theme="dark"] .labb__preview-caption,
[data-theme="dark"] .gameb__shot-caption {
  color: var(--text-muted);
  border-color: color-mix(in srgb, var(--text-muted) 50%, transparent);
  background: color-mix(in srgb, var(--bg-card) 70%, transparent);
}
[data-theme="dark"] .gameb__shot-meta {
  color: var(--text-muted);
  background: color-mix(in srgb, var(--bg-card) 70%, transparent);
}
[data-theme="dark"] .gameb__shot-meta strong {
  color: var(--text-main);
}
[data-theme="dark"] .gameb__status,
[data-theme="dark"] .labb__chrome {
  background: color-mix(in srgb, var(--bg-card) 78%, transparent);
}

/* ============================================
   MEDIA SLOTS — real art / screenshots (2026-07-18)
   The shot + preview slots were striped placeholders only.
   These modifiers let an actual image fill the slot while
   keeping the status pill and browser chrome on top.
   ============================================ */

/* Games: key art (or the "coming soon" placeholder) fills the slot.
   Drop the slot's own diagonal stripes — the placeholder SVG carries its
   own texture, and two stripe fields crossing renders as plaid. */
.gameb__shot--media {
  padding: 0;
  background: var(--base-elev);
}
.gameb__shot--media .gameb__shot-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Two in-progress titles side by side — overrides the cinema-strip
   layout (1 full-width banner + 2 halves) which assumed 3 cards. */
.bento4--games.bento4--games-pair {
  grid-template-columns: 1.5fr 1fr;   /* bento, not equal halves (#6) */
  grid-template-rows: auto;
}
.bento4--games.bento4--games-pair > :nth-child(1),
.bento4--games.bento4--games-pair > :nth-child(2) {
  grid-column: auto;
  grid-row: auto;
}
@media (max-width: 760px) {
  .bento4--games.bento4--games-pair { grid-template-columns: 1fr; }
}

/* Labs: the whole preview can be the link out to the live site. */
a.labb__preview { text-decoration: none; color: inherit; }

/* One lab live — the 4-column plaza mosaic leaves half the row empty,
   so a single entry runs full width as a horizontal card instead. */
.bento4--labs.bento4--labs-single {
  grid-template-columns: 1fr;
  grid-template-rows: auto;
}

/* Two labs side by side, both vertical (Joe 2026-07-20): equal halves —
   Guten Glance is one lab of two, not the hero of the page. Image above
   text on the left; text above the two page samples on the right. */
.bento4--labs.bento4--labs-duo {
  grid-template-columns: 1fr 1fr;
  grid-template-rows: auto;
}
.bento4--labs.bento4--labs-duo > :nth-child(n) {
  grid-column: auto;
  grid-row: auto;
}
/* Previews absorb leftover height so both cards stand level, but are
   CAPPED (Joe 2026-07-20): the GG screenshot was dwarfing the row. A
   280px ceiling on both previews keeps the section short; the screenshot
   crops (object-fit) instead of growing. Simul panes just fill the same
   capped frame — no forced portrait ratio anymore. */
.bento4--labs-duo .labb__preview { flex: 1 1 auto; min-height: 170px; max-height: 280px; }
/* The mock now sits above the body (matching the Guten Glance card), so it
   has to absorb the spare height and clip, the way the screenshot does.
   Without min-height:0 it overflowed and painted over the card title. */
.bento4--labs-duo .labb__preview--mock {
  flex: 1 1 auto;
  min-height: 260px;
  overflow: hidden;
}
.bento4--labs-duo .labb__preview--mock .simul-mock { min-height: 0; }
.bento4--labs-duo .labb__body { padding: 20px 24px 22px; flex: 0 0 auto; }
@media (max-width: 960px) {
  .bento4--labs.bento4--labs-duo { grid-template-columns: 1fr; }
  .simul-mock { min-height: 200px; }
}
.bento4--labs.bento4--labs-single > :nth-child(1) {
  grid-column: 1 / -1;
  grid-row: auto;
}

/* Labs: live screenshot sits under the browser chrome. */
.labb__shot {
  flex: 1;
  width: 100%;
  min-height: 0;
  object-fit: cover;
  object-position: top center;
  display: block;
}

/* ============================================
   CONTACT — keeps emphasis + dual CTA
   ============================================ */
.contact4 {
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: center;
}
.contact4__chapter { margin-bottom: 4px; }
.contact4__h {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(36px, 5vw, 60px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--text-main);
  text-wrap: balance;
}
.contact4__h em {
  font-style: italic;
  color: var(--primary-text);
  font-weight: 400;
}
.contact4__lede {
  font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.7;
  color: color-mix(in srgb, var(--text-main) 78%, var(--text-muted));
  max-width: 56ch;
  text-wrap: pretty;
}
.contact4__cta {
  display: flex;
  gap: 14px;
  margin-top: 24px;
  flex-wrap: wrap;
  justify-content: center;
}
/* Email-only contact (Joe 2026-08-22). The form, the Worker endpoint and
   Turnstile were all removed, so this is the whole contact block: a lede,
   the address set large enough to read as the call to action, and the
   reply-time note. The .contact4__form* rules below are kept for now
   because nothing else uses them and restoring a form should be cheap. */
.contact4__direct {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  margin-top: 8px;
}
.contact4__mail { margin: 0; }
.contact4__mail-link {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(28px, 3.4vw, 44px);
  letter-spacing: -0.01em;
  color: var(--primary-text);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--primary) 35%, transparent);
  padding-bottom: 2px;
  transition: border-color 160ms ease, color 160ms ease;
  /* An email address must not break mid-word on a narrow screen. */
  white-space: nowrap;
}
.contact4__mail-link:hover,
.contact4__mail-link:focus-visible {
  border-bottom-color: var(--primary);
}
.contact4__mail-link:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 4px;
  border-radius: 2px;
}
@media (max-width: 420px) {
  /* Below this the address stops fitting on one line at the display size. */
  .contact4__mail-link { font-size: 24px; }
}

/* Contact form (Joe 2026-07-20): replaces the lede + big mailto button.
   A small box that collects exactly what we need; Send composes the
   message in the visitor's mail app (static site, no backend yet). */
/* Type scale raised across the whole form (Joe 2026-07-26). The inputs
   were the load-bearing change: anything under 16px makes iOS Safari zoom
   the page on focus, which is why the form felt broken on a phone. */
.contact4__form {
  width: 100%;
  max-width: 680px;
  margin-top: 8px;
  text-align: left;
  background: var(--bg-card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: clamp(20px, 2.4vw, 30px);
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.contact4__form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
/* Visible labels. The fields previously carried a placeholder and an
   aria-label only, so the label vanished the moment anyone started typing
   and the placeholder itself measured 2.4:1. */
.contact4__field { display: flex; flex-direction: column; gap: 7px; }
.contact4__field--wide { grid-column: 1 / -1; }
.contact4__label {
  font-family: var(--font-mono);
  font-size: 12.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.contact4__label .contact4__req {
  color: var(--primary-text);
  margin-left: 2px;
}
.contact4__hint {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-muted);
  margin: -2px 0 2px;
}
.contact4__input {
  font-family: var(--font-body);
  /* 16px is the iOS zoom-on-focus threshold. Do not go below it. */
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-main);
  background: var(--base-bg);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  padding: 13px 16px;
  min-height: 50px;
  width: 100%;
  transition: border-color var(--d-base), box-shadow var(--d-base);
}
.contact4__input::placeholder { color: var(--text-subtle); }
[data-theme="dark"] .contact4__input::placeholder { color: var(--text-muted); }
.contact4__input:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--primary) 55%, var(--hairline));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 14%, transparent);
}
.contact4__input--area {
  resize: vertical;
  min-height: 130px;
}
.contact4__form-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
/* Send matches the fields it sits under rather than the 14px nav buttons. */
.contact4__form-foot .btn {
  font-size: 16px;
  padding: 14px 24px;
  min-height: 50px;
}
/* Reply time and the free call are the most reassuring lines on the page.
   They were 11px tracked mono, the hardest small-text setting there is. */
.contact4__form-note {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.6;
  letter-spacing: 0;
  color: var(--text-muted);
  max-width: 46ch;
  /* Line up with the text inside the inputs (16px padding + 1px border),
     not with the boxes' outer edge (Joe 2026-07-20). */
  padding-left: 17px;
}
.contact4__form-note a { color: var(--text-main); }
/* Honeypot: invisible to people, tempting to bots. */
.contact4__hp { display: none !important; }
@media (max-width: 560px) {
  .contact4__form-grid { grid-template-columns: 1fr; }
  .contact4__form-foot { flex-direction: column; align-items: flex-start; }
}

.contact4__addr {
  display: flex;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
  margin-top: 36px;
  flex-wrap: wrap;
  justify-content: center;
}
.contact4__sep { opacity: 0.5; }
.btn--lg { padding: 14px 22px; font-size: 14px; }

/* ============================================
   FOOTER — no diamond, just wordmark
   ============================================ */
.footer4 {
  position: relative;
  z-index: 1;
  border-top: 1px solid var(--hairline);
  padding: 32px;
  background: color-mix(in srgb, var(--base-bg) 80%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
[data-theme="dark"] .footer4 {
  background: color-mix(in srgb, var(--base-card) 72%, transparent);
}
/* Top block: brand + categorized link columns (#5). */
.footer4__inner {
  max-width: var(--container);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.8fr 1fr 1fr 1fr;
  gap: 40px;
  align-items: start;
}
.footer4__brand-block { display: flex; flex-direction: column; gap: 12px; max-width: 34ch; }
.footer4__brand {
  color: var(--text-main);
  font-family: var(--font-logo);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.01em;
  text-decoration: none;
}
.footer4__brand-dot { color: var(--primary-text); }
.footer4__tag {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-muted);
}
.footer4__col { display: flex; flex-direction: column; gap: 10px; }
.footer4__h {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-subtle);
  font-weight: 500;
  margin-bottom: 4px;
}
.footer4__col a {
  color: var(--text-muted);
  text-decoration: none;
  font-size: 14px;
  transition: color var(--d-base);
}
.footer4__col a:hover { color: var(--text-main); }

/* Base row: copyright + compact legal strip (#5, #6). */
.footer4__base {
  max-width: var(--container);
  margin: 32px auto 0;
  padding-top: 20px;
  border-top: 1px solid var(--hairline);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 14px 24px;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
}

@media (max-width: 720px) {
  .footer4__inner { grid-template-columns: 1fr 1fr; gap: 28px 24px; }
  .footer4__brand-block { grid-column: 1 / -1; }
}
@media (max-width: 480px) {
  .footer4__base { flex-direction: column; align-items: flex-start; }
}

/* ============================================
   ADDITIONS 2026-07-19 (Joe review pass)
   ============================================ */

/* pococo.ai pillars: a bento, not a row of equals (#6) — consulting is the
   tall feature, training + tooling stack beside it. */
.bento4--pillars-3 { grid-template-columns: 1fr 1fr; grid-template-rows: auto auto; }  /* consulting = exactly 50% (#7) */
/* Card 01 spans both rows, so it is as tall as cards 02 + 03 stacked.
   Centring its content left a 162px hole above the card's own label, which
   read as a bug. Content starts at the top like every other card; the
   spare height falls at the foot, where it is unremarkable. */
.bento4--pillars-3 > :nth-child(1) { grid-column: 1; grid-row: 1 / 3; justify-content: flex-start; }
.bento4--pillars-3 > :nth-child(2) { grid-column: 2; grid-row: 1; }
.bento4--pillars-3 > :nth-child(3) { grid-column: 2; grid-row: 2; }
@media (max-width: 760px) {
  .bento4--pillars-3 { grid-template-columns: 1fr; }
  .bento4--pillars-3 > :nth-child(1),
  .bento4--pillars-3 > :nth-child(2),
  .bento4--pillars-3 > :nth-child(3) { grid-column: 1; grid-row: auto; }
}

/* Card click targets (#5): a stretched link over the whole card. Secondary
   links inside the card are raised above it so they stay clickable. */
.card-stretch { position: absolute; inset: 0; z-index: 1; }
.gameb .card-stretch:focus-visible { outline-offset: -3px; }
.labb__cta-row { position: relative; z-index: 2; }

/* Red marker for genuine TBD placeholders only (#14). */
.gameb__tbd {
  position: absolute;
  left: 14px;
  bottom: 14px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #d90000;
  background: color-mix(in srgb, #d90000 8%, var(--bg-card));
  border: 1px solid color-mix(in srgb, #d90000 45%, transparent);
  border-radius: 4px;
  padding: 4px 8px;
}
[data-theme="dark"] .gameb__tbd {
  color: #ff8a8a;
  background: color-mix(in srgb, #ff5c5c 14%, transparent);
  border-color: #ff5c5c;
}

/* Guten Glance card: primary link + secondary "how we built it" (#12). */
.labb__cta-row {
  margin-top: auto;
  padding-top: 16px;
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
  align-items: center;
}
.labb__cta-row .labb__cta { margin-top: 0; padding-top: 0; }
.labb__cta--ghost { color: var(--text-muted); }
.labb__cta--ghost:hover { color: var(--labs-primary); }

/* ── Home games: two play links on one card (matchomino v1 / v2) ──────
   The single .gameb__cta uses margin-top:auto to pin to the card foot;
   inside a row we clear that so both links sit inline. */
.gameb__cta-row {
  margin-top: auto;
  padding-top: 18px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  align-items: center;
}
.gameb__cta-row .gameb__cta {
  margin-top: 0;
  padding-top: 0;
}
/* Rank the two Matchomino links. Side by side and identical, "Play v1" and
   "Play v2" asked a stranger to choose between two versions of a game they
   have never heard of. */
.gameb__cta--go {
  font-family: var(--font-body);
  /* 13px in a 9x16 pill read as a footnote next to the card's 28px name
     (Joe 2026-08-28: "much too small"). It is the primary action on the
     whole games section, so it is now sized like a real button — and at
     46px tall it clears the 44px touch minimum at every width, not just
     under the 900px media query.
     NO vertical padding, deliberately: inside .gameb__cta-row the
     higher-specificity `.gameb__cta-row .gameb__cta` rule zeroes
     padding-top, so any vertical padding here survives only on the
     bottom and shoves the label off-centre (Joe 2026-08-28, again).
     min-height + flex centering can't be half-overridden. */
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--base-bg);
  background: var(--games-primary);
  padding: 0 24px;
  min-height: 46px;
  align-items: center;
  border-radius: var(--radius-sm);
  /* The label sat high in the pill (Joe 2026-08-23). The inherited body
     line-height made the line box taller than the glyphs, and align-items
     centred the BOX, not the text. line-height:1 collapses the box onto the
     text so the padding is what centres it. */
  line-height: 1;
  justify-content: center;
}
/* The arrow is a different glyph height, so it needs its own baseline reset
   or it drags the line box back out of centre. */
.gameb__cta--go span { line-height: 1; display: inline-block; }
[data-theme="dark"] .gameb__cta--go { color: #1A1210; }
.gameb__cta--alt {
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--text-muted) 40%, transparent);
}
.gameb__cta--alt:hover { color: var(--games-primary); }

/* ── Home games: cinema strip, second try (Joe 2026-08-28). The first
   pass gave Matchomino a tall featured cell and Joe's verdict was "much
   too large" — a 700px board dominated the section. It now runs as the
   base .bento4--games silhouette: Matchomino as a full-width banner with
   the portrait shot BESIDE the copy (36% wide, so the board stays modest),
   and Hachi Hachi + Chako as the two cells below. No grid override needed;
   only the phone stack. */
@media (max-width: 620px) {
  .bento4--games { grid-template-columns: 1fr; }
  .bento4--games > :nth-child(1),
  .bento4--games > :nth-child(2),
  .bento4--games > :nth-child(3) {
    grid-column: auto;
    grid-row: auto;
  }
}

/* The featured shot keeps the screenshot's own portrait proportions —
   aspect-ratio, not a fixed min-height, so the score panel, the board,
   the multiplier bar and the piece tray all stay in frame. Wherever the
   slot and the image disagree, `contain` over the screenshot's own
   backdrop color (#191A2E at every edge) absorbs the difference as
   seamless letterbox instead of cropping the board. */
.gameb__shot--tall {
  aspect-ratio: 1008 / 1220;
  background: #191A2E;
  border-bottom: 1px solid var(--hairline);
}
.gameb__shot--tall .gameb__shot-media { object-fit: contain; }
/* In the banner the shot sits beside the body, so the divider is the
   right edge, and 36% width keeps the board around 450-500px wide. */
.gameb--banner .gameb__shot--side.gameb__shot--tall {
  flex-basis: 36%;
  min-width: 0;
  border-right: 1px solid var(--hairline);
  border-bottom: none;
}
@media (max-width: 960px) {
  /* Stacked banner: a full-width portrait image would run ~850px tall,
     so the slot goes landscape and `contain` centers the board on its
     own navy. */
  .gameb--banner .gameb__shot--side.gameb__shot--tall {
    aspect-ratio: auto;
    height: 440px;
    border-right: none;
    border-bottom: 1px solid var(--hairline);
  }
}

/* Hachi Hachi's home cell (Joe 2026-08-28: 320px showed only a thin
   band, "cropped too much"). The home page uses hachihachi-home.jpg —
   the hive cluster without the score/item UI, which showed up as a
   stray fragment when the full composite was positioned in a short
   slot — and a 5/4 slot shows about 70% of it. `center 22%` anchors
   just above the top bee so the crop gives way at the bottom, not at
   a face. The games page shows the full UI composite instead. */
.gameb__shot--deep {
  aspect-ratio: 5 / 4;
  border-bottom: 1px solid var(--hairline);
}
.gameb__shot-media--bees { object-position: center 22%; }

/* Chako's mark is landscape in a cell that stretches to match Hachi
   Hachi's; `contain` over the art's own cream (#FBEDD2 at every edge)
   soaks up the height difference without cropping the girl or the
   lettering. */
.gameb__shot--fit {
  flex: 1 0 auto;
  min-height: 260px;
  background: #FBEDD2;
  border-bottom: 1px solid var(--hairline);
}
.gameb__shot--fit .gameb__shot-media { object-fit: contain; }
/* The base .gameb__body flex:1 would split the cell's surplus height
   between art and text and leave the short copy floating over a void;
   give it all to the art instead. */
.gameb__shot--fit ~ .gameb__body { flex-grow: 0; }

/* The banner body is justify-content:center, but the CTA row's
   margin-top:auto (right for the stacked cards, where it pins the
   button to the card foot) tears the button away from its copy here.
   A fixed gap keeps name, description and button as one centred group. */
.gameb--banner .gameb__cta-row { margin-top: 32px; }

/* ============================================
   RESOLUTION PASS 2026-07-26 (Joe #9)
   Checked at 390 / 768 / 1024 / 1280 / 1920 /
   2560 / 3840, light and dark.
   ============================================ */

/* 1 · Gutters scale with the viewport. A flat 32px each side took 16% of a
   390px phone away from the text and looked mean at 2560. */
.s4__inner { padding: 0 clamp(20px, 3.2vw, 40px); }

/* 2 · Anchor landings. The nav is fixed and nothing carried a scroll
   margin, so following consulting / games / labs from the header dropped
   the chapter mark and the first line of the heading behind it. */
.s4,
.hero4,
.bento4__cell[id] { scroll-margin-top: 104px; }

/* 3 · Games at tablet width. The banner stacks its image above the copy
   at 960px (rule with .gameb--banner above), the Hachi/Chako pair holds
   two columns to 621px, and phones stack everything. The old trio needed
   a special case here; the strip does not. */

/* 4 · Touch targets. The games and labs CTAs are 11px mono links with no
   padding, about 16px tall against a 24px minimum. */
@media (max-width: 900px) {
  .gameb__cta,
  .labb__cta {
    min-height: 44px;
    align-items: center;
  }
  .gameb__cta--alt { min-height: 44px; display: inline-flex; align-items: center; }
}

/* 5 · Phones in landscape and short laptop windows. A 100vh hero on a
   375px-tall viewport pushed every word of the page below the fold. */
@media (max-height: 600px) and (orientation: landscape) {
  .hero4 { min-height: auto; padding: 104px 24px 56px; }
  .hero4__wordmark { font-size: clamp(72px, 14vh, 132px); margin-bottom: 14px; }
}

/* 6 · Section rhythm on phones. 96px above and below each section is a lot
   of empty paper when the screen is 390px wide. */
@media (max-width: 640px) {
  :root { --section-pad: 64px; }
  .s4__head { margin-bottom: 44px; }
  .bento4 { gap: 16px; }
  .contact4__form-foot .btn { width: 100%; justify-content: center; }
}

/* 7 · Wide desktops. --container was hard-capped at 1200px and every type
   clamp topped out around its 1280px value, so a big display showed a
   laptop-sized page marooned in the middle of the background.

   THE THRESHOLD IS DELIBERATE (Joe 2026-07-26): nothing below 2000px
   changes. 1440, 1512, 1600 and 1920 are the widths this design was drawn
   at and tested at, and they render exactly as before. A first pass started
   this tier at 1600 and grew the section heading and lede on a 1920
   monitor, which is not a resolution problem, it is just a bigger page.
   Only raise a value here, never lower the 2000px floor. */
@media (min-width: 2000px) {
  :root {
    --container: clamp(1200px, 64vw, 1520px);
    --density: 1.1;
  }
  .s4__h { font-size: clamp(58px, 2.9vw, 72px); }
  .s4__lede { font-size: 18px; max-width: 52ch; }
  .bento4__title { font-size: clamp(26px, 1.3vw, 30px); }
  .bento4__body { font-size: 15px; max-width: 64ch; }
  .bento4__eg { font-size: 16px; }
  .gameb__desc,
  .labb__desc { font-size: 15px; max-width: 64ch; }
  .gameb__name { font-size: clamp(28px, 1.5vw, 30px); }
  .labb__title { font-size: clamp(26px, 1.4vw, 28px); }
  .contact4 { max-width: 880px; }
  .contact4__h { font-size: clamp(60px, 3vw, 76px); }
  .contact4__form { max-width: 760px; }

  /* The furniture scales with the page. Growing body copy while the
     buttons, card CTAs and nav pill stay put leaves links looking like
     footnotes and shrinks every target proportionally. */
  .btn { font-size: 15px; padding: 15px 26px; }
  .gameb__cta,
  .labb__cta { font-size: 12.5px; }
  /* Same specificity as .gameb__cta and later in the file, so without
     this line the 12.5px above would shrink the primary button back down. */
  .gameb__cta--go { font-size: 16px; }
  .gameb__cta--alt { font-size: 14px; }
  .smartnav { max-width: min(1080px, calc(100vw - 32px)); }
  .smartnav__links a { font-size: 13px; padding: 9px 15px; }
  .smartnav .btn--nav { font-size: 13px; padding: 9px 18px; }
}

@media (min-width: 2600px) {
  :root { --container: 1620px; --density: 1.28; }
  .hero4__inner { max-width: 1400px; }
  .hero4__wordmark { font-size: clamp(240px, 11vw, 330px); }
  .hero4__lede { font-size: clamp(28px, 1.4vw, 34px); max-width: 860px; }
  .s4__lede { font-size: 19px; }
  .bento4 { gap: 26px; }
  .bento4--pillars .bento4__cell { padding: 34px 38px; }
  .bento4__body { font-size: 16px; }
  .bento4__eg { font-size: 17px; }
  .bento4__eg-label,
  .s4__foot-note { font-size: 13px; }
  .gameb__status,
  .labb__status { font-size: 12px; }
  .btn { font-size: 17px; padding: 17px 30px; }
  .gameb__cta,
  .labb__cta { font-size: 14px; }
  .gameb__cta--go { font-size: 17px; }
  .gameb__cta--alt { font-size: 15px; }
  .contact4__input { font-size: 17px; }
  .contact4__hint,
  .contact4__form-note { font-size: 16px; }
  .smartnav { max-width: min(1200px, calc(100vw - 32px)); margin-top: 18px; }
  .smartnav__links a { font-size: 15px; padding: 11px 18px; }
  .smartnav .btn--nav { font-size: 15px; padding: 11px 22px; }
  .smartnav__wordmark { font-size: 26px; }
  .smartnav__theme { width: 38px; height: 38px; }
  .smartnav__lang { font-size: 13px; }
}

/* 8 · True 4K, unscaled. Most "4K" monitors report 1920 or 2560 CSS pixels
   because the OS scales them, and the tiers above cover that. A 3840 CSS
   viewport is the 32-inch-at-100% case. Measure is held by max-width in ch,
   not by the container cap. */
@media (min-width: 3200px) {
  :root { --container: 1780px; --density: 1.45; }
  .hero4__inner { max-width: 1720px; }
  .hero4__wordmark { font-size: min(430px, 11vw); }
  .hero4__lede { font-size: 42px; max-width: 1100px; }
  .s4__h { font-size: 88px; }
  .s4__lede { font-size: 21px; max-width: 50ch; }
  .bento4__body,
  .bento4__eg { max-width: 66ch; }
  .bento4__body { font-size: 17px; }
  .bento4__eg { font-size: 18px; }
  /* At 96px the contact heading broke into three lines with "stuck on."
     stranded mid-line. Wider column, slightly smaller type, two lines. */
  .contact4 { max-width: 1280px; }
  .contact4__h { font-size: 86px; }
  .contact4__form { max-width: 940px; }
  .btn { font-size: 19px; padding: 19px 34px; }
  .smartnav { max-width: min(1360px, calc(100vw - 32px)); margin-top: 24px; }
  .smartnav__links a { font-size: 17px; padding: 13px 21px; }
  .smartnav .btn--nav { font-size: 17px; padding: 13px 26px; }
  .smartnav__wordmark { font-size: 31px; }
  .smartnav__theme { width: 44px; height: 44px; }
}

/* ============================================================================
   JAPANESE TREE · html[lang="ja"]
   ----------------------------------------------------------------------------
   Everything above styles a page whose type is Latin. These rules re-point the
   families and re-set the vertical rhythm for /ja/, and nothing else: the
   layout, the colour landscape, the reveals and the spacing scale are shared,
   which is what brand-design-v4.md §4 means by "the Japanese version of every
   page must have identical visual weight, hierarchy, and polish".

   Scoped to html[lang="ja"] rather than to a class, so it cannot be applied by
   accident and cannot be forgotten: build.mjs sets lang from the locale.

   Rules, all from brand-design-v4.md:
     · Display / editorial  -> Shippori Mincho B1  (--font-jp-serif), downloaded
     · Body / UI            -> the SYSTEM gothic   (--font-jp-sans), not downloaded

   The body face changed on 2026-08-27. It was Zen Kaku Gothic New, self-hosted
   at 4.04 MB across 363 slices. Every platform ships a competent gothic -
   Hiragino Sans on Apple, Yu Gothic on Windows, Noto Sans CJK on Android - so
   the system stack never lands on nothing, which is NOT true of Mincho and is
   why the display face is still downloaded. See tokens.css for the stack and
   tools/set-jp-body-face.mjs to switch back. Provisional, not a verdict on
   the typeface.
     · Line height: EN body 1.6-1.75, JP body 1.8-1.95. Japanese is denser per
       line, so a layout balanced in English feels cramped in Japanese.
     · JP body never below 13px in cards, 14px in running prose.
     · M Plus Rounded 1c is games-only and is not loaded here at all.
   ========================================================================= */
html[lang="ja"] body {
  font-family: var(--font-jp-sans);
  line-height: 1.85;
}

/* Display faces. Literata has no Japanese coverage, so without this every
   heading would fall back to whatever the OS offers and the page would be set
   in two unrelated serifs. */
html[lang="ja"] .hero4__lede,
html[lang="ja"] .s4__h,
html[lang="ja"] .contact4__h,
html[lang="ja"] .bento4__title,
html[lang="ja"] .gameb__name,
html[lang="ja"] .labb__title,
html[lang="ja"] .pg h1,
html[lang="ja"] .pg h2,
html[lang="ja"] .pg h3,
html[lang="ja"] .h-display,
html[lang="ja"] .h-section {
  font-family: var(--font-jp-serif);
}

/* NO SYNTHETIC ITALIC. The English hero lede and the accent line inside each
   section heading are italic, and italic is a Latin convention: Shippori
   Mincho B1 ships no italic, so the browser would shear the glyphs by ~12deg
   and produce something no Japanese reader has seen in print. The accent is
   carried by weight and colour instead, which is what the English italic is
   doing anyway. */
html[lang="ja"] .hero4__lede,
html[lang="ja"] .hero4__lede em,
html[lang="ja"] .s4__h em,
html[lang="ja"] .contact4__h em {
  font-style: normal;
}
html[lang="ja"] .s4__h em,
html[lang="ja"] .contact4__h em,
html[lang="ja"] .hero4__lede em {
  font-weight: 500;
}

/* Optical size and tracking. Japanese sets tighter than Latin at the same
   nominal size and needs no negative tracking; -0.02em on a 58px heading
   closes the counters on dense kanji. Line height opens up because Japanese
   headings wrap more often. */
html[lang="ja"] .s4__h {
  font-size: clamp(30px, 3.4vw, 46px);
  line-height: 1.45;
  letter-spacing: 0.01em;
  font-weight: 500;
}
html[lang="ja"] .hero4__lede {
  font-size: clamp(19px, 2vw, 26px);
  line-height: 1.7;
  letter-spacing: 0.01em;
  font-weight: 500;
}
html[lang="ja"] .contact4__h {
  line-height: 1.5;
  letter-spacing: 0.01em;
  font-weight: 500;
}
/* h3 was never given a weight. `.pg h1` and `.pg h2` in page.css both declare
   300; `.pg h3` has no rule at all - no family, no size, no weight - so it kept
   the browser default of bold and asked Shippori for 700. Shippori shipped
   400/500/600, so it landed on 600: 122 files and 5.01 MB serving a boldness
   nobody chose, on five pages. 500 is what every other Japanese heading here
   uses.

   Scoped to html[lang="ja"] on purpose. The English `.pg h3` is still bold, and
   should stay that way until someone decides otherwise - it is a separate
   question, and Sora 600 stays live regardless because <strong> resolves to it.

   Deleting the 600 weight is what this rule pays for, so the two have to move
   together. Guard 3 in build.mjs enforces that: put the weight back without
   restoring the files and the build fails, and vice versa. */
html[lang="ja"] .pg h3 {
  font-weight: 500;
}
html[lang="ja"] .s4__lede,
html[lang="ja"] .bento4__body,
html[lang="ja"] .gameb__desc,
html[lang="ja"] .labb__desc,
html[lang="ja"] .contact4__lede {
  line-height: 1.9;
  letter-spacing: 0.01em;
}
/* Floors from the brand doc: 13px in cards, 14px in running prose. The English
   card body sits at 14px, which is below the Japanese prose floor, so the card
   text is raised rather than inherited. */
html[lang="ja"] .bento4__body,
html[lang="ja"] .gameb__desc,
html[lang="ja"] .labb__desc { font-size: 15px; }
html[lang="ja"] .bento4__eg,
html[lang="ja"] .bento4__eg-line { font-size: 13px; line-height: 1.85; }

/* Line breaking. `strict` keeps small kana, the long vowel mark and closing
   brackets off the start of a line - the browser default (`auto`) is
   permissive and lets ー and っ begin a line, which reads as a typo. */
html[lang="ja"] {
  line-break: strict;
  overflow-wrap: normal;
  word-break: normal;
}
/* text-wrap: balance reflows by word, and Japanese has no spaces, so on a
   heading it produces arbitrary breaks mid-phrase. The authored <br> is the
   break instead. */
html[lang="ja"] .s4__h,
html[lang="ja"] .hero4__lede,
html[lang="ja"] .contact4__h { text-wrap: initial; }

/* The wordmark, the chapter numerals and the section labels stay Latin: they
   are the brand mark and the 01/02/03 spine, not translated content. */
html[lang="ja"] .smartnav__wordmark,
html[lang="ja"] .smartnav__domain,
html[lang="ja"] .footer4__brand,
html[lang="ja"] .s4__chapter-num,
html[lang="ja"] .s4__chapter-name,
html[lang="ja"] .bento4__index { font-family: var(--font-logo); }
html[lang="ja"] .s4__chapter-num { font-family: var(--font-mono); }

/* Nav and footer labels are UI, so they take the sans, not the serif. */
html[lang="ja"] .smartnav__links a,
html[lang="ja"] .footer4__col a,
html[lang="ja"] .footer4__h,
html[lang="ja"] .btn { font-family: var(--font-jp-sans); }

/* --- English INSIDE a Japanese document keeps the Latin faces -------------
   The eight bilingual legal and company pages declare lang="ja" - they lead
   in Japanese - and carry their English in lang="en" blocks. Every rule above
   is scoped to html[lang="ja"], so without this it would set roughly 140
   English passages in the system gothic and Shippori Mincho B1. Both carry
   Latin glyphs, so nothing would look broken; the English would just quietly
   stop being Sora and Literata, which is the kind of regression nobody
   reports and everybody half-notices. This matters MORE since the body face
   became a system stack, not less: system-ui resolves to the OS interface
   face, so unprotected English would render as Segoe UI or SF Pro - closer to
   plausible, and so harder to spot, than Zen Kaku ever was.

   The selectors are deliberately over-specific. They have to outrank
   `html[lang="ja"] .pg h2` and `.pg h2 .jp` above, which are (0,2,2) - hence
   repeating .pg and the heading element rather than relying on one short
   selector. Scoped by the element's own lang, never by a class: on these
   pages `.en` and `.jp` name the ROW, not the language, and both appear on
   spans of either language. */
html[lang="ja"] [lang="en"],
html[lang="ja"] .pg [lang="en"],
html[lang="ja"] .pg h1 [lang="en"],
html[lang="ja"] .pg h2 [lang="en"],
html[lang="ja"] .pg h3 [lang="en"],
html[lang="ja"] .co-table [lang="en"] {
  font-family: var(--font-body);
  line-height: 1.7;
  letter-spacing: normal;
}

/* ...and back again, for Japanese nested INSIDE one of those English blocks.
   The English legal text quotes Japanese it cannot translate away: the
   registered company name ｐｏｃｏｃｏ合同会社, the representative's title
   代表社員, the Civil Code term 定型約款. Each is wrapped in <span lang="ja">
   so a browser knows to pick a Japanese face - which is the whole defence
   against Han unification, where Japanese and Chinese share codepoints but
   not letterforms, and a machine guessing wrong shows a Japanese reader
   Chinese shapes.

   Marking the span is necessary but not sufficient. The rule above has just
   set the run in Sora, which carries no CJK glyphs at all, so the browser
   would fall through per-character to whatever it found next - exactly the
   guess we are trying to prevent. This puts the Japanese stack back, so the
   named Japanese families are tried before anything is left to chance.

   tools/lang-coverage-audit.mjs fails the build if a Japanese run is ever
   rendered outside lang="ja" again. */
html[lang="ja"] [lang="en"] [lang="ja"],
html[lang="ja"] .pg [lang="en"] [lang="ja"],
html[lang="ja"] .co-table [lang="en"] [lang="ja"] {
  font-family: var(--font-jp-sans);
}
/* An English heading takes the Latin DISPLAY face, not the body face. */
html[lang="ja"] .pg h1[lang="en"],
html[lang="ja"] .pg h2[lang="en"],
html[lang="ja"] .pg h3[lang="en"] {
  font-family: var(--font-display);
}
