/* =============================================================================
   ethingwillbefine.com — design tokens and styles
   =============================================================================

   * THE ONE RULE: no hard-coded colour, font size, spacing value, radius,
   * duration, or easing appears anywhere below the TOKENS block. Every value
   * in the stylesheet resolves through a custom property. This is what makes
   * the WordPress port a transcription rather than a reinterpretation.

   ---------------------------------------------------------------------------
   theme.json MAPPING
   ---------------------------------------------------------------------------
   Every value below resolves through a custom property named exactly as
   WordPress emits it, so the port is copy-across. The short authoring aliases
   (--c-*, --t-*, --s-*, …) that this file used to carry are gone; the only
   remaining non-preset properties are the three local implementation details
   listed at the end of the TOKENS block, which are not design tokens and do
   not belong in theme.json.

   * A custom property in this file is either a --wp--* token or one of those
   * three. There is no third category, and adding one reopens the drift the
   * alias removal closed.

   settings.color.palette[]           -> --wp--preset--color--<slug>
     bg surface surface-raised border border-strong
     ink ink-muted ink-faint
     accent accent-hover accent-subtle
     state-active state-live state-idle

   settings.typography.fontFamilies[] -> --wp--preset--font-family--<slug>
     display body mono

   settings.typography.fontSizes[]    -> --wp--preset--font-size--<slug>
     display heading-1 heading-2 heading-3 body small meta label

   settings.spacing.spacingSizes[]    -> --wp--preset--spacing--<slug>
     1 2 3 4 5 6 7 8 9 10

   settings.custom.*                  -> --wp--custom--<path-in-kebab>
     radius.sm|md|pill      motion.dur-fast|dur-base|dur-slow
     motion.ease-out|ease-spring     layout.width-content|width-prose|gutter
     border.hairline

   Colour uses CSS light-dark(). In theme.json each palette entry carries the
   same light-dark() string as its "color" value — WordPress passes it through
   untouched, so both modes survive the port as one declaration each.
   ========================================================================== */


/* =============================================================================
   FONTS — self-hosted, Latin subset, variable. No third-party requests.
   ========================================================================== */

@font-face {
  font-family: 'Fraunces Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('./fonts/fraunces-var.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter Variable';
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url('./fonts/inter-var.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* * optional, not swap. Mono carries only metadata — tags, dates, badges — so
 * it is never the LCP element and never worth critical-path bytes. But short
 * strings are where per-glyph width differences bite: swapping it in re-wrapped
 * the stack-tag row and cost 0.014 CLS on a phone. optional means it either
 * arrives before first paint or waits for the next visit, and never shifts. */
@font-face {
  font-family: 'JetBrains Mono Variable';
  font-style: normal;
  font-display: optional;
  font-weight: 100 800;
  src: url('./fonts/mono-var.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* * Metric-matched fallbacks. Without these, font swap re-wraps headings and
 * pushes the page around — an early guess at Fraunces/Georgia was 11 points
 * out and moved 93px of hero content on a phone.
 *
 * * These numbers are MEASURED, not estimated: advance width of a sample
 * string in the real face divided by the same string in the fallback, with the
 * ascent/descent overrides normalised by that ratio. Re-measure if a face
 * changes — do not hand-tune. */
@font-face {
  font-family: 'Fraunces Fallback';
  src: local('Georgia'), local('Times New Roman'), local('serif');
  size-adjust: 114.4%;
  ascent-override: 85.7%;
  descent-override: 22.7%;
  line-gap-override: 0%;
}

@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial'), local('Helvetica'), local('sans-serif');
  size-adjust: 107%;
  ascent-override: 90.7%;
  descent-override: 22.4%;
  line-gap-override: 0%;
}

@font-face {
  font-family: 'Mono Fallback';
  src: local('Courier New'), local('monospace');
  size-adjust: 100%;
  ascent-override: 102%;
  descent-override: 30%;
  line-gap-override: 0%;
}


/* =============================================================================
   TOKENS
   ========================================================================== */

:root {
  /* --- colour scheme ------------------------------------------------------
   * Bare :root follows the operating system. The pre-paint script stamps
   * data-theme on <html> when the visitor has made an explicit choice. */
  color-scheme: light dark;

  /* --- colour: light-dark(LIGHT, DARK) ------------------------------------
   * Dark is the primary design; light is independently tuned, not inverted.
   * Warm neutrals throughout — no pure black, no pure grey. */
  --wp--preset--color--bg:             light-dark(#FBF9F5, #12100E);
  --wp--preset--color--surface:        light-dark(#FFFFFF, #1C1917);
  --wp--preset--color--surface-raised: light-dark(#F5F1EA, #242019);
  --wp--preset--color--border:         light-dark(#E5E0D8, #2E2A26);
  --wp--preset--color--border-strong:  light-dark(#CFC8BC, #3D3831);
  --wp--preset--color--ink:            light-dark(#1A1714, #EDE8E0);
  --wp--preset--color--ink-muted:      light-dark(#5C534A, #A8A099);
  --wp--preset--color--ink-faint:      light-dark(#7A7066, #8B837C);
  --wp--preset--color--accent:         light-dark(#8A560C, #E5A24B);
  --wp--preset--color--accent-hover:   light-dark(#6B4209, #F2B565);
  --wp--preset--color--accent-subtle:  light-dark(rgba(138, 86, 12, .10), rgba(229, 162, 75, .12));
  --wp--preset--color--state-active:   light-dark(#2F63C7, #5B8DEF);
  --wp--preset--color--state-live:     light-dark(#1F6B42, #5FBF7F);
  --wp--preset--color--state-idle:     light-dark(#7A7066, #8B837C);

  /* --- typography --------------------------------------------------------- */
  --wp--preset--font-family--display: 'Fraunces Variable', 'Fraunces Fallback', Georgia, serif;
  --wp--preset--font-family--body:    'Inter Variable', 'Inter Fallback', system-ui, sans-serif;
  --wp--preset--font-family--mono:    'JetBrains Mono Variable', 'Mono Fallback', ui-monospace, monospace;

  --wp--preset--font-size--display: clamp(2.15rem, 1.5rem + 2.4vw, 3.5rem);
  --wp--preset--font-size--heading-1:      clamp(2.1rem, 1.4rem + 2.8vw, 3.4rem);
  --wp--preset--font-size--heading-2:      clamp(1.55rem, 1.2rem + 1.4vw, 2.3rem);
  --wp--preset--font-size--heading-3:      clamp(1.2rem, 1.08rem + 0.5vw, 1.5rem);
  --wp--preset--font-size--body:    1.0625rem;
  --wp--preset--font-size--small:   0.9375rem;
  --wp--preset--font-size--meta:    0.8125rem;
  --wp--preset--font-size--label:   0.75rem;

  /* --- spacing: 4px base --------------------------------------------------- */
  --wp--preset--spacing--1:  0.25rem;
  --wp--preset--spacing--2:  0.5rem;
  --wp--preset--spacing--3:  0.75rem;
  --wp--preset--spacing--4:  1rem;
  --wp--preset--spacing--5:  1.5rem;
  --wp--preset--spacing--6:  2rem;
  --wp--preset--spacing--7:  3rem;
  --wp--preset--spacing--8:  4rem;
  --wp--preset--spacing--9:  6rem;
  --wp--preset--spacing--10: 8rem;

  /* --- custom -------------------------------------------------------------- */
  --wp--custom--radius--sm:   4px;
  --wp--custom--radius--md:   8px;
  --wp--custom--radius--pill: 999px;

  --wp--custom--border--hairline: 1px;

  --wp--custom--motion--dur-fast:    120ms;
  --wp--custom--motion--dur-base:    200ms;
  --wp--custom--motion--dur-slow:    420ms;
  --wp--custom--motion--dur-pulse:   2400ms;
  --wp--custom--motion--ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --wp--custom--motion--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --wp--custom--motion--stagger:     60ms;

  --wp--custom--layout--width-content: 1180px;
  /* * 68ch measured at the body size, expressed in rem. ch resolves against
   * each element's OWN font size, so a ch-based measure gives the lede a
   * visibly wider column than the paragraphs beneath it — the text column has
   * to be one width regardless of what is set in it. */
  --wp--custom--layout--width-prose:   46.75rem;
  --wp--custom--layout--gutter:        var(--wp--preset--spacing--5);

  /* --- local implementation details — NOT theme.json tokens ---------------
   * Everything above this line is a design token and moves to theme.json.
   * The three below describe how one component is built, carry no design
   * meaning outside it, and would only be noise in an editor's global styles.
   * They stay in the stylesheet through the port.
   *
   *   --header-h        reserved flow space for the fixed header
   *   --header-scale    compression ratio of the header's inner element
   *   --reveal-delay    per-element stagger offset, set by app.js at runtime
   *
   * * The header is fixed and its box never changes size. An in-flow sticky
   * header shifts everything below it when it compresses; a fixed one still
   * moves its own children, and CLS counts that too. So compression is
   * expressed as a transform — compositor-only, zero layout impact. */
  /* * Taller on phones: the nav takes a second row below 48rem, and the
   * reserved flow space has to match the tallest state or the header clips. */
  --header-h: calc(var(--wp--preset--spacing--8) + var(--wp--preset--spacing--4));
  --header-scale: 0.94;
}

:root[data-theme='light'] { color-scheme: light; }
:root[data-theme='dark']  { color-scheme: dark; }

@media (min-width: 48rem) { :root { --wp--custom--layout--gutter: var(--wp--preset--spacing--6); --header-h: var(--wp--preset--spacing--8); } }
@media (min-width: 64rem) { :root { --wp--custom--layout--gutter: var(--wp--preset--spacing--7); } }


/* =============================================================================
   BASE
   ========================================================================== */

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

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

body {
  margin: 0;
  padding-top: var(--header-h);
  background: var(--wp--preset--color--bg);
  color: var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--body);
  line-height: 1.7;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { margin: 0; font-weight: 400; }

h1, h2 {
  font-family: var(--wp--preset--font-family--display);
  font-variation-settings: 'SOFT' 40;
  line-height: 1.06;
  letter-spacing: -0.02em;
}

h3 {
  font-family: var(--wp--preset--font-family--body);
  font-weight: 600;
  font-size: var(--wp--preset--font-size--heading-3);
  line-height: 1.3;
  letter-spacing: -0.01em;
}

p { margin: 0 0 var(--wp--preset--spacing--4); }
p:last-child { margin-bottom: 0; }

a {
  color: var(--wp--preset--color--accent);
  text-decoration-thickness: var(--wp--custom--border--hairline);
  text-underline-offset: 0.2em;
  transition: color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}
a:hover { color: var(--wp--preset--color--accent-hover); }

img, svg { max-width: 100%; height: auto; display: block; }

:focus-visible {
  outline: 2px solid var(--wp--preset--color--accent);
  outline-offset: 2px;
  border-radius: var(--wp--custom--radius--sm);
}

::selection { background: var(--wp--preset--color--accent-subtle); }

[id] { scroll-margin-top: calc(var(--header-h) + var(--wp--preset--spacing--4)); }

.skip-link {
  position: absolute;
  left: var(--wp--preset--spacing--4);
  top: var(--wp--preset--spacing--2);
  z-index: 100;
  padding: var(--wp--preset--spacing--2) var(--wp--preset--spacing--4);
  background: var(--wp--preset--color--surface);
  color: var(--wp--preset--color--ink);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border-strong);
  border-radius: var(--wp--custom--radius--sm);
  font-size: var(--wp--preset--font-size--small);
  text-decoration: none;
  transform: translateY(calc(-1 * var(--wp--preset--spacing--10)));
  transition: transform var(--wp--custom--motion--dur-base) var(--wp--custom--motion--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); }

.visually-hidden {
  position: absolute;
  width: var(--wp--custom--border--hairline);
  height: var(--wp--custom--border--hairline);
  margin: calc(-1 * var(--wp--custom--border--hairline));
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.shell {
  width: 100%;
  max-width: var(--wp--custom--layout--width-content);
  margin-inline: auto;
  padding-inline: var(--wp--custom--layout--gutter);
}

/* ! Core writes `margin-block-start: 24px` onto every child of a group block,
 * ! from a `:where()` rule printed into the head *after* this stylesheet. Zero
 * ! specificity, so a plain element selector cannot reach it and later source
 * ! order cannot either — one class does both. Left alone it added 24px above
 * ! the mode toggle inside a flex row, which grew the header past its own
 * ! height and pushed the wordmark off the top of the page on a phone.
 *
 * * Spacing in this design comes from the stylesheet, never from block
 * attributes, so there is nothing to preserve here. `.prose` re-establishes its
 * own rhythm further down, and wins on source order. */
.is-layout-flow > *,
.is-layout-constrained > * {
  margin-block-start: 0;
}


/* =============================================================================
   TYPOGRAPHIC UTILITIES
   ========================================================================== */

.mono {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--meta);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

.label {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-faint);
  margin: 0 0 var(--wp--preset--spacing--4);
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--3);
}
.label::after {
  content: '';
  flex: 1;
  height: var(--wp--custom--border--hairline);
  background: var(--wp--preset--color--border);
}

.nowrap { white-space: nowrap; }

.lede {
  font-size: var(--wp--preset--font-size--heading-3);
  line-height: 1.5;
  color: var(--wp--preset--color--ink-muted);
}


/* =============================================================================
   BADGES AND TAGS
   ========================================================================== */

/* * Status badges are deliberately neutral. The dot carries state, the text
 * carries nuance, and neither uses the amber accent — if badges were amber
 * they would compete with links and the palette would collapse. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--wp--preset--spacing--2);
  padding: var(--wp--preset--spacing--1) var(--wp--preset--spacing--3);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border-strong);
  border-radius: var(--wp--custom--radius--pill);
  background: var(--wp--preset--color--surface);
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-muted);
  white-space: nowrap;
}

.badge__dot {
  width: var(--wp--preset--spacing--2);
  height: var(--wp--preset--spacing--2);
  border-radius: var(--wp--custom--radius--pill);
  background: var(--wp--preset--color--state-idle);
  flex: none;
}

.badge[data-state='concept']  .badge__dot,
.badge[data-state='building'] .badge__dot,
.badge[data-state='pre-test'] .badge__dot,
.badge[data-state='beta']     .badge__dot { background: var(--wp--preset--color--state-active); }
.badge[data-state='live']     .badge__dot { background: var(--wp--preset--color--state-live); }
.badge[data-state='paused']   .badge__dot,
.badge[data-state='archived'] .badge__dot { background: var(--wp--preset--color--state-idle); }

.tags--spaced { margin-top: var(--wp--preset--spacing--5); }

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wp--preset--spacing--2);
  padding: 0;
  margin: 0;
  list-style: none;
}

/* * `.tags li` alongside `.tag`: the hero's stack row is a core list block now,
 * and core writes the classes on the <ul>, never on the items. The blocks that
 * render tags in PHP still put `.tag` on each one. */
.tag,
.tags li {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.03em;
  color: var(--wp--preset--color--ink-muted);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--sm);
  padding: var(--wp--preset--spacing--1) var(--wp--preset--spacing--2);
  white-space: nowrap;
  transition: border-color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out), color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}
a.tag:hover, .card:hover .tag { border-color: var(--wp--preset--color--border-strong); }


/* =============================================================================
   BUTTONS
   ========================================================================== */

/* * Two selectors, one button. `.btn` is what the PHP-rendered blocks write;
 * `.wp-block-button__link` is what core's button block writes, and core puts
 * any custom class on the wrapper <div> rather than on the anchor — so the
 * variant below reaches through the wrapper. */
.btn,
.wp-block-button__link {
  display: inline-flex;
  align-items: center;
  gap: var(--wp--preset--spacing--2);
  padding: var(--wp--preset--spacing--3) var(--wp--preset--spacing--5);
  border-radius: var(--wp--custom--radius--sm);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border-strong);
  background: transparent;
  color: var(--wp--preset--color--ink);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--small);
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition:
    transform var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out),
    border-color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out),
    background-color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out),
    color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}
.btn:hover,
.wp-block-button__link:hover { border-color: var(--wp--preset--color--ink); color: var(--wp--preset--color--ink); }
.btn:active,
.wp-block-button__link:active { transform: translateY(var(--wp--custom--border--hairline)); }

.btn--primary,
.btn--primary .wp-block-button__link {
  background: var(--wp--preset--color--ink);
  border-color: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--bg);
}
.btn--primary:hover,
.btn--primary .wp-block-button__link:hover { background: var(--wp--preset--color--accent); border-color: var(--wp--preset--color--accent); color: var(--wp--preset--color--bg); }

/* Core's buttons block is a flex row of its own; the design's spacing lives on
 * the container that wraps it. */
.wp-block-buttons { display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--3); }
.wp-block-button { display: inline-flex; }


/* =============================================================================
   HEADER
   ========================================================================== */

.site-header {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  height: var(--header-h);
  display: flex;
  align-items: center;
  background: color-mix(in srgb, var(--wp--preset--color--bg) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: var(--wp--custom--border--hairline) solid transparent;
  transition: border-color var(--wp--custom--motion--dur-base) var(--wp--custom--motion--ease-out);
}
.site-header[data-scrolled='true'] { border-bottom-color: var(--wp--preset--color--border); }

/* * Logged-in admin bar. Core offsets the document with html { margin-top },
 * but position:fixed chrome is viewport-relative and stays at top: 0 under the
 * toolbar. Heights match admin-bar.css: 32px desktop, 46px below 782px. */
.admin-bar .site-header { top: 32px; }
@media screen and (max-width: 782px) {
  .admin-bar .site-header { top: 46px; }
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--5);
  transform-origin: left center;
  transition: transform var(--wp--custom--motion--dur-base) var(--wp--custom--motion--ease-out);
}
.site-header[data-scrolled='true'] .site-header__inner {
  transform: scale(var(--header-scale));
}

.wordmark {
  font-family: var(--wp--preset--font-family--display);
  font-variation-settings: 'SOFT' 60;
  font-size: var(--wp--preset--font-size--heading-3);
  color: var(--wp--preset--color--ink);
  text-decoration: none;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.wordmark:hover { color: var(--wp--preset--color--ink); }
.wordmark em { font-style: normal; color: var(--wp--preset--color--accent); }

.nav {
  display: flex;
  gap: var(--wp--preset--spacing--5);
  margin-inline-start: var(--wp--preset--spacing--4);
  align-items: center;
}
/* * Each link is a paragraph block, so the styling hangs off the anchor rather
 * than off a class. Core's list and navigation blocks were both heavier than
 * three links deserve — the list adds markers to reset, the navigation block
 * adds the interactivity runtime. */
.nav p { margin: 0; }

.nav a {
  position: relative;
  color: var(--wp--preset--color--ink-muted);
  text-decoration: none;
  font-size: var(--wp--preset--font-size--small);
  padding-block: var(--wp--preset--spacing--1);
}
.nav a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: var(--wp--custom--border--hairline);
  background: var(--wp--preset--color--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}
.nav a:hover, .nav a[aria-current='page'] { color: var(--wp--preset--color--ink); }
.nav a:hover::after, .nav a[aria-current='page']::after { transform: scaleX(1); }

.header__actions {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--2);
}

/* * Header controls share one chip: same height, radius, type, border and
 * hover. Availability is a paragraph, CV is a core button, mode and search are
 * custom controls — the selectors equalise them so the strip reads as one system.
 *
 * * Mode and search are classed on the control itself (not only under
 * `.header__actions`) so a mis-nested insert in the site editor, or a
 * ServerSideRender preview without the actions parent, still gets the chip —
 * not a bare grey browser button. */
.header__actions .availability,
.site-header .header__cv .wp-block-button__link,
.mode-toggle,
.search-toggle {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--wp--preset--spacing--2);
  min-height: var(--wp--preset--spacing--6);
  height: var(--wp--preset--spacing--6);
  padding: 0 var(--wp--preset--spacing--3);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border-strong);
  border-radius: var(--wp--custom--radius--pill);
  background: transparent;
  color: var(--wp--preset--color--ink-muted);
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  margin: 0;
  cursor: default;
  transition:
    border-color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out),
    color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out),
    background-color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}

/* * Availability is status, not a control — no pointer, no hover lift. */
.header__actions .availability { cursor: default; }

/* * The dot is a pseudo-element, not a span. The pill is a paragraph block now,
 * and a paragraph's content is text — a decorative span inside it would be one
 * more thing an editor could delete by pressing backspace. */
.availability::before {
  content: '';
  width: var(--wp--preset--spacing--2);
  height: var(--wp--preset--spacing--2);
  border-radius: var(--wp--custom--radius--pill);
  background: var(--wp--preset--color--state-live);
  flex: none;
  animation: pulse var(--wp--custom--motion--dur-pulse) var(--wp--custom--motion--ease-out) infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.site-header .header__cv .wp-block-button__link,
.mode-toggle,
.search-toggle {
  cursor: pointer;
}
.site-header .header__cv .wp-block-button__link:hover,
.mode-toggle:hover,
.search-toggle:hover {
  border-color: var(--wp--preset--color--ink);
  color: var(--wp--preset--color--ink);
  background: transparent;
}
.site-header .header__cv .wp-block-button__link:active {
  transform: none;
}

/* * Square chips: same height as the text pills, equal width so the strip
 * stays even. Mode and search share this geometry. */
.mode-toggle,
.search-toggle {
  width: var(--wp--preset--spacing--6);
  min-width: var(--wp--preset--spacing--6);
  padding: 0;
}
.mode-toggle svg,
.search-toggle svg {
  width: var(--wp--preset--spacing--4);
  height: var(--wp--preset--spacing--4);
  /* * Stroke icons — keep fill off so a theme/editor button rule cannot
   * paint a solid square over the glyph. */
  fill: none;
  stroke: currentColor;
}
.mode-toggle__icon {
  transition: transform var(--wp--custom--motion--dur-base) var(--wp--custom--motion--ease-spring), opacity var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
  transform-origin: center;
}
.mode-toggle:hover .mode-toggle__icon { transform: rotate(35deg); }

/* * No-JS falls through to a same-styled link at `/?s=`. With JS the button is
 * the control and the link stays out of the tab order. */
.search-toggle--noscript { display: none; }
.no-js .search-toggle[data-search-open] { display: none; }
.no-js .search-toggle--noscript { display: inline-flex; }

/* * Wrapper only hosts the chip in the strip; the dialog is top-layer via
 * showModal and is display:none while closed, so it does not affect flex.
 * Transparent so a selected block outline is the only editor chrome. */
.ewbf-search,
.wp-block-ewbf-search {
  display: inline-flex;
  align-items: center;
  background: transparent;
  color: inherit;
}

/* ! Two classes deep on purpose. The CV button is a core buttons block, and
 * ! core prints `.wp-block-buttons-is-layout-flex { display: flex }` into the
 * ! head *after* this stylesheet — at equal specificity that wins, the button
 * ! stayed on phones, the header row overflowed and the wordmark was pushed
 * ! off the top of the header. */
.site-header .header__cv { display: none; }
@media (min-width: 64rem) { .site-header .header__cv { display: inline-flex; } }

/* * Below 48rem the wordmark, nav and controls cannot share one row at 360px
 * without overflowing, so the nav takes a row of its own. No hamburger: a
 * disclosure menu needs JS and a focus trap for three links. */
@media (max-width: 47.99rem) {
  /* * The column gap is tightened as well as the row gap: at 360px the
   * wordmark and the actions come to 292px inside a 312px row, and the desktop
   * gap alone was enough to wrap them onto three rows — which the fixed-height
   * header then clipped from the top. */
  .site-header__inner {
    flex-wrap: wrap;
    row-gap: var(--wp--preset--spacing--2);
    column-gap: var(--wp--preset--spacing--3);
  }
  .nav { order: 3; width: 100%; gap: var(--wp--preset--spacing--4); margin-inline-start: 0; }
  .wordmark { font-size: var(--wp--preset--font-size--body); }
  /* * The availability pill stays on phones. It is the highest-value element
   * on the site for a job-hunting owner, and hiding it on the viewport most
   * recruiters actually browse from defeats the point. The header CV button
   * does go — the hero repeats it two scrolls up.
   *
   * * At 360px, wordmark (~125px) + actions (availability + mode + search,
   * ~249px) cannot share a row inside the shell, so the strip becomes three
   * rows: wordmark, chips, nav. Reserved height has to match or flex centering
   * clips the wordmark above the fixed header box. Measured inner ~106px. */
  :root {
    --header-h: calc(var(--wp--preset--spacing--9) + var(--wp--preset--spacing--4));
  }
  .header__actions {
    gap: var(--wp--preset--spacing--2);
  }
}

/* =============================================================================
   SEARCH DIALOG — command-palette overlay
   * Interaction pattern from joshwcomeau.com (icon → panel → live results).
   * Idle: accent sheet + squared bottom edge; page stays visible underneath.
   ========================================================================== */

.search-dialog {
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  /* * Fully transparent — only the sheet + terrain paint; the rest of the
   * dialog is a hit-target so backdrop clicks still close. */
  background: transparent;
  color: var(--wp--preset--color--bg);
  overflow: hidden;
}
/* * Light dim so the page under the idle panel remains readable, not a blackout. */
.search-dialog::backdrop {
  background: color-mix(in srgb, var(--wp--preset--color--bg) 42%, transparent);
  backdrop-filter: blur(1.5px);
}

.search-dialog__sheet {
  position: relative;
  z-index: 1;
  /* * Same token as the former input underline — the panel *is* the accent. */
  background: var(--wp--preset--color--accent);
  color: var(--wp--preset--color--bg);
  padding:
    var(--wp--preset--spacing--6)
    var(--wp--custom--layout--gutter)
    var(--wp--preset--spacing--4);
  display: flex;
  flex-direction: column;
  align-items: center;
  /* * Idle stays short so most of the page remains in view. */
  max-height: min(42vh, 22rem);
  overflow: hidden;
  transition: max-height var(--wp--custom--motion--dur-base) var(--wp--custom--motion--ease-out);
}
.search-dialog[data-has-results='true'] .search-dialog__sheet {
  max-height: min(78vh, 40rem);
}
@media (min-width: 48rem) {
  .search-dialog__sheet {
    padding-block-start: var(--wp--preset--spacing--7);
  }
}

/* * Squared terrain — continuation of the sheet, cut as a stepped bottom edge.
 * Transparent SVG regions under the steps reveal the dimmed page. */
.search-dialog__terrain {
  display: block;
  width: 100%;
  height: clamp(2.75rem, 8vw, 5rem);
  margin-top: -1px; /* * seal against the sheet so no hairline gap shows */
  color: var(--wp--preset--color--accent);
  fill: currentColor;
  pointer-events: none;
  flex: none;
  position: relative;
  z-index: 1;
}

.search-dialog__form {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--2);
  width: min(100%, 36rem);
  padding-bottom: var(--wp--preset--spacing--2);
  /* * Accent-on-accent would vanish; bg tracks the sheet text colour and
   * keeps a hard edge on both light and dark accent values. */
  border-bottom: 2px solid color-mix(in srgb, var(--wp--preset--color--bg) 55%, transparent);
}
.search-dialog__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.search-dialog__glyph {
  flex: none;
  width: var(--wp--preset--spacing--5);
  height: var(--wp--preset--spacing--5);
  color: color-mix(in srgb, var(--wp--preset--color--bg) 72%, transparent);
}
.search-dialog__input {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: var(--wp--preset--spacing--2) 0;
  border: 0;
  background: transparent;
  color: var(--wp--preset--color--bg);
  font-family: var(--wp--preset--font-family--body);
  font-size: var(--wp--preset--font-size--heading-3);
  line-height: 1.3;
  outline: none;
  box-shadow: none;
  /* * Native search clear is replaced by our trash control. */
  -webkit-appearance: none;
  appearance: none;
  caret-color: var(--wp--preset--color--bg);
}
.search-dialog__input::-webkit-search-cancel-button,
.search-dialog__input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}
.search-dialog__input::placeholder {
  color: color-mix(in srgb, var(--wp--preset--color--bg) 58%, transparent);
}
.search-dialog__clear,
.search-dialog__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--wp--preset--spacing--6);
  height: var(--wp--preset--spacing--6);
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--wp--custom--radius--sm);
  background: transparent;
  color: color-mix(in srgb, var(--wp--preset--color--bg) 72%, transparent);
  cursor: pointer;
  transition: color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}
/* * `display: inline-flex` above beats the UA [hidden] rule — reassert hide. */
.search-dialog__clear[hidden] {
  display: none;
}
.search-dialog__clear:hover,
.search-dialog__close:hover {
  color: var(--wp--preset--color--bg);
}
.search-dialog__clear svg,
.search-dialog__close svg {
  width: var(--wp--preset--spacing--4);
  height: var(--wp--preset--spacing--4);
  fill: none;
  stroke: currentColor;
}
/* * Close sits top-right of the viewport on wide screens, matching the
 * reference layout, while remaining in the form row on narrow ones. */
@media (min-width: 48rem) {
  .search-dialog__close {
    position: fixed;
    top: var(--wp--preset--spacing--5);
    right: var(--wp--preset--spacing--5);
    z-index: 2;
    width: var(--wp--preset--spacing--7);
    height: var(--wp--preset--spacing--7);
    color: color-mix(in srgb, var(--wp--preset--color--bg) 80%, transparent);
  }
  .search-dialog__close svg {
    width: var(--wp--preset--spacing--5);
    height: var(--wp--preset--spacing--5);
  }
}

.search-dialog__idle {
  width: min(100%, 36rem);
  margin-top: var(--wp--preset--spacing--5);
  text-align: center;
  padding-bottom: var(--wp--preset--spacing--3);
}
.search-dialog__hint {
  margin: 0;
  color: color-mix(in srgb, var(--wp--preset--color--bg) 78%, transparent);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.55;
  max-width: 28rem;
  margin-inline: auto;
}

.search-dialog__results {
  width: min(100%, 36rem);
  margin-top: var(--wp--preset--spacing--4);
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: 1 1 auto;
  min-height: 0;
  padding-bottom: var(--wp--preset--spacing--3);
  display: flex;
  flex-direction: column;
  gap: 0;
}

.search-result {
  display: block;
  padding: var(--wp--preset--spacing--4) var(--wp--preset--spacing--2);
  border-bottom: var(--wp--custom--border--hairline) solid color-mix(in srgb, var(--wp--preset--color--bg) 28%, transparent);
  text-decoration: none;
  color: inherit;
  border-radius: var(--wp--custom--radius--sm);
  transition: background-color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}
.search-result:last-child { border-bottom: 0; }
.search-result:hover,
.search-result:focus-visible {
  background: color-mix(in srgb, var(--wp--preset--color--bg) 14%, transparent);
  outline: none;
}
.search-result__type {
  display: block;
  margin-bottom: var(--wp--preset--spacing--1);
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--wp--preset--color--bg) 65%, transparent);
}
.search-result__title {
  display: block;
  font-family: var(--wp--preset--font-family--display);
  font-variation-settings: 'SOFT' 50;
  font-size: var(--wp--preset--font-size--heading-3);
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--wp--preset--color--bg);
  margin-bottom: var(--wp--preset--spacing--1);
}
.search-result__excerpt {
  display: block;
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.55;
  color: color-mix(in srgb, var(--wp--preset--color--bg) 78%, transparent);
}

/* * Match highlight — wash of the sheet text colour on accent, not a second amber. */
mark.search-hit {
  background: color-mix(in srgb, var(--wp--preset--color--bg) 28%, transparent);
  color: inherit;
  border-radius: 2px;
  padding: 0 0.1em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.search-dialog__none {
  width: min(100%, 36rem);
  margin: var(--wp--preset--spacing--5) 0 var(--wp--preset--spacing--3);
  text-align: center;
  color: color-mix(in srgb, var(--wp--preset--color--bg) 78%, transparent);
  font-size: var(--wp--preset--font-size--small);
}


/* =============================================================================
   REVEAL — shared entrance mechanism
   ========================================================================== */

[data-reveal] {
  opacity: 0;
  transform: translateY(var(--wp--preset--spacing--3));
  transition:
    opacity var(--wp--custom--motion--dur-slow) var(--wp--custom--motion--ease-out),
    transform var(--wp--custom--motion--dur-slow) var(--wp--custom--motion--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal][data-revealed='true'] {
  opacity: 1;
  transform: none;
}
/* * No-JS and pre-paint safety: never leave content permanently invisible. */
.no-js [data-reveal] { opacity: 1; transform: none; }


/* =============================================================================
   SECTIONS
   ========================================================================== */

main { display: block; }

.section { padding-block: var(--wp--preset--spacing--8); }
@media (min-width: 64rem) { .section { padding-block: var(--wp--preset--spacing--9); } }

.section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--wp--preset--spacing--4);
  margin-bottom: var(--wp--preset--spacing--6);
  flex-wrap: wrap;
}
.section__head h2 { font-size: var(--wp--preset--font-size--heading-2); }

.section__more {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--meta);
  text-decoration: none;
}
.section__more:hover { text-decoration: underline; }


/* =============================================================================
   HERO
   ========================================================================== */

.hero {
  padding-block: var(--wp--preset--spacing--8) var(--wp--preset--spacing--8);
  border-bottom: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
}
@media (min-width: 64rem) {
  .hero { padding-block: var(--wp--preset--spacing--9) var(--wp--preset--spacing--9); }
}

.hero__grid {
  display: grid;
  gap: var(--wp--preset--spacing--7);
  align-items: start;
}
@media (min-width: 64rem) {
  .hero__grid { grid-template-columns: 1.35fr 1fr; gap: var(--wp--preset--spacing--8); }
}

.hero h1 {
  font-size: var(--wp--preset--font-size--display);
  font-variation-settings: 'SOFT' 30;
  margin-bottom: var(--wp--preset--spacing--5);
  text-wrap: balance;
  /* * Stops "end-to-end" splitting at its own hyphen. The headline is the
   * single highest-value string on the site; it does not get to break badly. */
  hyphens: none;
}

.hero__sub {
  font-size: var(--wp--preset--font-size--heading-3);
  line-height: 1.5;
  color: var(--wp--preset--color--ink-muted);
  max-width: 42ch;
  margin-bottom: var(--wp--preset--spacing--6);
}
.hero__sub .mono { font-size: inherit; color: var(--wp--preset--color--ink); }

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wp--preset--spacing--3);
  margin-bottom: var(--wp--preset--spacing--6);
}

.hero__stack {
  padding-top: var(--wp--preset--spacing--5);
  border-top: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
}
.hero__stack .label { margin-bottom: var(--wp--preset--spacing--3); }


/* =============================================================================
   NOW-BUILDING — the one signature moment
   ========================================================================== */

/* * Reads as a quiet status readout, not a dashboard widget and not a fake
 * terminal. Rows stagger in and the dots settle with a spring; nothing loops. */
.now {
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--md);
  background: var(--wp--preset--color--surface);
  overflow: hidden;
}

.now__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wp--preset--spacing--3);
  padding: var(--wp--preset--spacing--3) var(--wp--preset--spacing--4);
  border-bottom: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  background: var(--wp--preset--color--surface-raised);
}
.now__title {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-muted);
}
.now__pulse {
  width: var(--wp--preset--spacing--2);
  height: var(--wp--preset--spacing--2);
  border-radius: var(--wp--custom--radius--pill);
  background: var(--wp--preset--color--accent);
  animation: pulse var(--wp--custom--motion--dur-pulse) var(--wp--custom--motion--ease-out) infinite;
}

.now__list { margin: 0; padding: 0; list-style: none; }

.now__row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--wp--preset--spacing--1) var(--wp--preset--spacing--3);
  padding: var(--wp--preset--spacing--4);
  border-bottom: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  opacity: 0;
  transform: translateY(var(--wp--preset--spacing--2));
  transition:
    opacity var(--wp--custom--motion--dur-slow) var(--wp--custom--motion--ease-out),
    transform var(--wp--custom--motion--dur-slow) var(--wp--custom--motion--ease-spring);
  transition-delay: var(--reveal-delay, 0ms);
}
.now__row:last-child { border-bottom: 0; }
.now__row[data-revealed='true'] { opacity: 1; transform: none; }
.no-js .now__row { opacity: 1; transform: none; }

.now__dot {
  width: var(--wp--preset--spacing--2);
  height: var(--wp--preset--spacing--2);
  border-radius: var(--wp--custom--radius--pill);
  background: var(--wp--preset--color--state-idle);
  margin-top: 0.55em;
  flex: none;
}
.now__row[data-state='live'] .now__dot { background: var(--wp--preset--color--state-live); }
.now__row[data-state='beta'] .now__dot,
.now__row[data-state='building'] .now__dot,
.now__row[data-state='pre-test'] .now__dot { background: var(--wp--preset--color--state-active); }

.now__name {
  font-weight: 600;
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--ink);
  text-decoration: none;
}
.now__name:hover { color: var(--wp--preset--color--accent); }

.now__note {
  grid-column: 2;
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--ink-muted);
  line-height: 1.5;
}

.now__meta {
  grid-column: 2;
  display: flex;
  gap: var(--wp--preset--spacing--3);
  align-items: center;
  color: var(--wp--preset--color--ink-faint);
}


/* =============================================================================
   PROJECT CARDS
   ========================================================================== */

.grid {
  display: grid;
  gap: var(--wp--preset--spacing--5);
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card--feature { grid-column: 1 / -1; }
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--3);
  padding: var(--wp--preset--spacing--5);
  background: var(--wp--preset--color--surface);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--md);
  transition:
    transform var(--wp--custom--motion--dur-base) var(--wp--custom--motion--ease-out),
    border-color var(--wp--custom--motion--dur-base) var(--wp--custom--motion--ease-out);
}
.card:hover {
  transform: translateY(calc(-1 * var(--wp--preset--spacing--1) / 2));
  border-color: var(--wp--preset--color--border-strong);
}
.card:has(.card__link:focus-visible) {
  outline: 2px solid var(--wp--preset--color--accent);
  outline-offset: 2px;
}

.card__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wp--preset--spacing--3);
  flex-wrap: wrap;
}

.card__title { font-size: var(--wp--preset--font-size--heading-3); }
.card--feature .card__title { font-size: var(--wp--preset--font-size--heading-2); font-family: var(--wp--preset--font-family--display); font-weight: 400; }

.card__link { color: inherit; text-decoration: none; }
.card__link::after { content: ''; position: absolute; inset: 0; }
.card__link:hover { color: var(--wp--preset--color--accent); }

.card__summary { color: var(--wp--preset--color--ink-muted); font-size: var(--wp--preset--font-size--small); margin: 0; }
.card--feature .card__summary { font-size: var(--wp--preset--font-size--body); max-width: 62ch; }

.card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wp--preset--spacing--4);
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: var(--wp--preset--spacing--3);
  border-top: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  color: var(--wp--preset--color--ink-faint);
}


/* =============================================================================
   ABOUT TEASER
   ========================================================================== */

.about {
  display: grid;
  gap: var(--wp--preset--spacing--6);
  align-items: start;
}
@media (min-width: 48rem) {
  .about { grid-template-columns: minmax(0, 200px) minmax(0, 1fr); gap: var(--wp--preset--spacing--7); }
}

.portrait {
  aspect-ratio: 4 / 5;
  width: 100%;
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--md);
  background: var(--wp--preset--color--surface-raised);
  overflow: hidden;
}
/* * A core image block, so the site editor offers Replace and the real
 * photograph arrives through the media library. The aspect ratio is held by the
 * figure and the image is cropped into it, so a portrait of any dimensions
 * swaps in without moving a pixel of the page. */
.portrait { margin: 0; }
.portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }

.about__body { max-width: var(--wp--custom--layout--width-prose); }
.about__body p { color: var(--wp--preset--color--ink-muted); }
.about__body strong { color: var(--wp--preset--color--ink); font-weight: 600; }

.linkrow {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wp--preset--spacing--4);
  margin-top: var(--wp--preset--spacing--5);
  padding: 0;
  list-style: none;
}
.linkrow--flush { margin-top: 0; }
.linkrow a {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--meta);
  text-decoration: none;
}
.linkrow a:hover { text-decoration: underline; }


/* =============================================================================
   POST LIST
   ========================================================================== */

.posts { margin: 0; padding: 0; list-style: none; }

.post-item {
  padding-block: var(--wp--preset--spacing--5);
  border-top: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
}
.post-item:last-child { border-bottom: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border); }

.post-item__title {
  font-size: var(--wp--preset--font-size--heading-3);
  margin-bottom: var(--wp--preset--spacing--2);
}
.post-item__title a { color: var(--wp--preset--color--ink); text-decoration: none; }
.post-item__title a:hover { color: var(--wp--preset--color--accent); }

.post-item__excerpt { color: var(--wp--preset--color--ink-muted); font-size: var(--wp--preset--font-size--small); margin-bottom: var(--wp--preset--spacing--3); }

.post-item__meta {
  display: flex;
  align-items: center;
  gap: var(--wp--preset--spacing--3);
  flex-wrap: wrap;
  color: var(--wp--preset--color--ink-faint);
}

/* * Empty states render nothing at all — no placeholder, no "coming soon".
 * The site must never advertise what it lacks. */
[data-empty='true'] { display: none; }


/* =============================================================================
   FOOTER
   ========================================================================== */

.site-footer {
  border-top: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  padding-block: var(--wp--preset--spacing--7);
  margin-top: var(--wp--preset--spacing--8);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wp--preset--spacing--5);
  justify-content: space-between;
  align-items: center;
}

.site-footer a { font-family: var(--wp--preset--font-family--mono); font-size: var(--wp--preset--font-size--meta); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }

.site-footer__colophon {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  color: var(--wp--preset--color--ink-faint);
  line-height: 1.6;
  margin: 0;
}


/* =============================================================================
   ARTICLE — project detail and blog post
   ========================================================================== */

.progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left;
  background: var(--wp--preset--color--accent);
  z-index: 60;
}
.admin-bar .progress { top: 32px; }
@media screen and (max-width: 782px) {
  .admin-bar .progress { top: 46px; }
}

.article-head {
  padding-block: var(--wp--preset--spacing--8) var(--wp--preset--spacing--6);
  border-bottom: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
}

.article-head h1 {
  font-size: var(--wp--preset--font-size--heading-1);
  font-variation-settings: 'SOFT' 30;
  max-width: 22ch;
  text-wrap: balance;
  margin-bottom: var(--wp--preset--spacing--5);
}

.article-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--wp--preset--spacing--3) var(--wp--preset--spacing--5);
  color: var(--wp--preset--color--ink-faint);
}

.partof {
  display: inline-flex;
  align-items: center;
  gap: var(--wp--preset--spacing--2);
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-muted);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--pill);
  padding: var(--wp--preset--spacing--1) var(--wp--preset--spacing--3);
  text-decoration: none;
  margin-bottom: var(--wp--preset--spacing--4);
}
.partof:hover { border-color: var(--wp--preset--color--border-strong); color: var(--wp--preset--color--ink); }

/* --- structured project header ------------------------------------------- */
.project-header__meta {
  display: grid;
  gap: var(--wp--preset--spacing--4);
  margin-top: var(--wp--preset--spacing--5);
  padding-top: var(--wp--preset--spacing--5);
  border-top: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
}
@media (min-width: 48rem) {
  .project-header__meta { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
}

.meta-field { display: flex; flex-direction: column; gap: var(--wp--preset--spacing--2); }
.meta-field__label {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-faint);
}
.meta-field__value { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--ink); }

.linklist { display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--3); padding: 0; margin: 0; list-style: none; }
.linklist a { font-family: var(--wp--preset--font-family--mono); font-size: var(--wp--preset--font-size--meta); text-decoration: none; }
.linklist a:hover { text-decoration: underline; }

/* --- prose ---------------------------------------------------------------- */
/* * Wide container, narrow text. Body copy stays at the 68ch measure while
 * figures, code, callouts and the demo shell breathe past it — the classic
 * editorial move, and it makes the space to the right of the column read as a
 * deliberate margin rather than an unfinished layout. */
.prose {
  max-width: calc(var(--wp--custom--layout--width-prose) + var(--wp--preset--spacing--9));
  padding-block: var(--wp--preset--spacing--7);
}

.prose > p,
.prose > ul,
.prose > ol,
.prose > h2,
.prose > h3,
.prose > hr,
.prose > blockquote,
.prose > .footnotes,
.prose > .label { max-width: var(--wp--custom--layout--width-prose); }

.prose > * + * { margin-top: var(--wp--preset--spacing--5); }

.prose h2 {
  font-size: var(--wp--preset--font-size--heading-2);
  margin-top: var(--wp--preset--spacing--8);
  margin-bottom: var(--wp--preset--spacing--4);
  font-variation-settings: 'SOFT' 40;
}
.prose h3 {
  margin-top: var(--wp--preset--spacing--7);
  margin-bottom: var(--wp--preset--spacing--3);
}
.prose p { margin: 0 0 var(--wp--preset--spacing--5); }
.prose > *:last-child { margin-bottom: 0; }

.prose ul, .prose ol { margin: 0 0 var(--wp--preset--spacing--5); padding-inline-start: var(--wp--preset--spacing--5); }
.prose li { margin-bottom: var(--wp--preset--spacing--2); }
.prose li::marker { color: var(--wp--preset--color--ink-faint); }

.prose hr {
  border: 0;
  height: var(--wp--custom--border--hairline);
  background: var(--wp--preset--color--border);
  margin-block: var(--wp--preset--spacing--7);
}

.prose blockquote {
  margin: var(--wp--preset--spacing--6) 0;
  padding-inline-start: var(--wp--preset--spacing--5);
  border-inline-start: 2px solid var(--wp--preset--color--accent);
  font-family: var(--wp--preset--font-family--display);
  font-variation-settings: 'SOFT' 60;
  font-size: var(--wp--preset--font-size--heading-3);
  line-height: 1.45;
  color: var(--wp--preset--color--ink);
}
.prose blockquote cite {
  display: block;
  margin-top: var(--wp--preset--spacing--3);
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--meta);
  font-style: normal;
  color: var(--wp--preset--color--ink-faint);
}

.prose figure { margin: var(--wp--preset--spacing--6) 0; }
/* * Dimensions are reserved so CLS stays at zero when the real image lands.
 * The dashed treatment marks it as a placeholder during review — a plain empty
 * box at this size reads as a broken image, not as a held space. */
.prose figure > div {
  border: var(--wp--custom--border--hairline) dashed var(--wp--preset--color--border-strong);
  border-radius: var(--wp--custom--radius--md);
  background: var(--wp--preset--color--surface-raised);
  overflow: hidden;
  aspect-ratio: 16 / 9;
  display: grid;
  place-items: center;
}
.prose figure > div:empty::after {
  content: 'figure';
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-faint);
}
.prose figcaption {
  margin-top: var(--wp--preset--spacing--3);
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--meta);
  color: var(--wp--preset--color--ink-faint);
  line-height: 1.5;
}

.prose code {
  font-family: var(--wp--preset--font-family--mono);
  font-size: 0.9em;
  background: var(--wp--preset--color--accent-subtle);
  color: var(--wp--preset--color--ink);
  padding: 0.1em 0.35em;
  border-radius: var(--wp--custom--radius--sm);
}

/* * Code blocks stay dark in both modes. A light code block next to a dark
 * editor is a lie about where the code came from. */
/* * Core's code block is a bare <pre class="wp-block-code">, so the design's
 * container styles have to match it as well as the prototype's wrapper. The
 * language label has no core equivalent and is simply not rendered — inventing
 * a block to carry one would cost more than the label is worth. */
.codeblock,
.prose pre.wp-block-code {
  margin: var(--wp--preset--spacing--6) 0;
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border-strong);
  border-radius: var(--wp--custom--radius--md);
  overflow: hidden;
  background: #1C1917;
}
.codeblock__lang {
  display: block;
  padding: var(--wp--preset--spacing--2) var(--wp--preset--spacing--4);
  border-bottom: var(--wp--custom--border--hairline) solid #3D3831;
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #A8A099;
}
/* * The prototype's wrapper holds a <pre> inside it; core's <pre> is the
 * container itself. Margin belongs to the container, so it is set once above
 * and cleared here only for the nested case. */
.codeblock pre { margin: 0; }

.codeblock pre,
.prose pre.wp-block-code {
  padding: var(--wp--preset--spacing--4);
  overflow-x: auto;
}
.codeblock code,
.prose pre.wp-block-code code {
  background: none;
  padding: 0;
  color: #EDE8E0;
  font-size: var(--wp--preset--font-size--meta);
  line-height: 1.65;
}

.callout {
  margin: var(--wp--preset--spacing--6) 0;
  padding: var(--wp--preset--spacing--4) var(--wp--preset--spacing--5);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border-strong);
  border-inline-start: 2px solid var(--wp--preset--color--accent);
  border-radius: var(--wp--custom--radius--sm);
  background: transparent;
}
.callout__label {
  display: block;
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--accent);
  margin-bottom: var(--wp--preset--spacing--2);
}
.callout p { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--ink-muted); }

/* --- demo-frame: reserved shell for future interactive demos -------------- */
.demo-frame {
  margin: var(--wp--preset--spacing--6) 0;
  border: var(--wp--custom--border--hairline) dashed var(--wp--preset--color--border-strong);
  border-radius: var(--wp--custom--radius--md);
  background: var(--wp--preset--color--surface);
}
.demo-frame__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--wp--preset--spacing--3);
  padding: var(--wp--preset--spacing--2) var(--wp--preset--spacing--4);
  border-bottom: var(--wp--custom--border--hairline) dashed var(--wp--preset--color--border);
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-faint);
}
.demo-frame__body {
  display: grid;
  place-items: center;
  min-height: var(--wp--preset--spacing--10);
  padding: var(--wp--preset--spacing--5);
  text-align: center;
}
.demo-frame__body p { color: var(--wp--preset--color--ink-faint); font-size: var(--wp--preset--font-size--small); margin: 0; }

/* --- footnotes ------------------------------------------------------------ */
.footnotes {
  margin-top: var(--wp--preset--spacing--7);
  padding-top: var(--wp--preset--spacing--5);
  border-top: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
}
.footnotes ol { padding-inline-start: var(--wp--preset--spacing--5); margin: 0; }
.footnotes li { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--ink-muted); }
.fn-ref { font-size: 0.75em; vertical-align: super; text-decoration: none; }

/* --- author card ---------------------------------------------------------- */
.author {
  display: flex;
  gap: var(--wp--preset--spacing--5);
  align-items: center;
  flex-wrap: wrap;
  margin-top: var(--wp--preset--spacing--8);
  padding: var(--wp--preset--spacing--5);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--md);
  background: var(--wp--preset--color--surface);
}
.author__avatar {
  width: var(--wp--preset--spacing--8);
  height: var(--wp--preset--spacing--8);
  flex: none;
  border-radius: var(--wp--custom--radius--pill);
  overflow: hidden;
  background: var(--wp--preset--color--surface-raised);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  margin: 0;
}
.author__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.author__body { flex: 1 1 16rem; }
.author__body p { font-size: var(--wp--preset--font-size--small); color: var(--wp--preset--color--ink-muted); margin-bottom: var(--wp--preset--spacing--2); }

/* --- devlog list ---------------------------------------------------------- */
.devlogs { margin-top: var(--wp--preset--spacing--7); }

/* --- prev/next ------------------------------------------------------------ */
.pagernav {
  display: grid;
  gap: var(--wp--preset--spacing--4);
  margin-top: var(--wp--preset--spacing--8);
  padding-top: var(--wp--preset--spacing--5);
  border-top: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
}
@media (min-width: 48rem) { .pagernav { grid-template-columns: 1fr 1fr; } }

.pagernav a {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--1);
  padding: var(--wp--preset--spacing--4);
  border: var(--wp--custom--border--hairline) solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--md);
  text-decoration: none;
  color: var(--wp--preset--color--ink);
  transition: border-color var(--wp--custom--motion--dur-fast) var(--wp--custom--motion--ease-out);
}
.pagernav a:hover { border-color: var(--wp--preset--color--border-strong); }
.pagernav span {
  font-family: var(--wp--preset--font-family--mono);
  font-size: var(--wp--preset--font-size--label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wp--preset--color--ink-faint);
}
.pagernav strong { font-weight: 500; font-size: var(--wp--preset--font-size--small); }
.pagernav a:last-child { text-align: end; align-items: flex-end; }


/* =============================================================================
   REDUCED MOTION — every animation on the site is gated here
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }

  [data-reveal], .now__row {
    opacity: 1 !important;
    transform: none !important;
  }

  .mode-toggle:hover .mode-toggle__icon { transform: none; }
  .site-header[data-scrolled='true'] .site-header__inner { transform: none; }
  .card:hover { transform: none; }
  .btn:active { transform: none; }
  .availability::before, .now__pulse { animation: none; opacity: 1; }
}
