@import url('https://fonts.googleapis.com/css2?family=Belanosima:wght@400;600;700&family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700;9..144,900&family=Work+Sans:wght@300;400;500;600;700&display=swap');
/* Plank & Pattern design tokens, base typography/layout, header/footer and the
   kj-btn/kj-field/kj-notice classes kj-widgets emits — taken from the original landing
   pages (docs/platform/kerf-joint/*.html), ported to crates/kj-web/src/landing.rs
   (njreid/woodwerx#20). Every site page links this file; a page family keeps only its own
   layout CSS (e.g. static/landing.css). */

/* Tokens share the woodwerx viewer's own drafting-room palette (viewer/index.html's
   `:root`/`--paper`/`--ink`/`--sel`/`--mark`/`--join`), so a design's cutsheet and the
   site chrome around it read as one draftsman's desk: light is cream vellum with a
   teal ruling pen and an ochre grease pencil; dark is a 1960s blueprint print — chalk
   linework on navy, with the same grease-pencil marks now bright orange and yellow. */
:root {
  --bg: #EFE7D6;
  --surface: #F6F0E3;
  --surface-2: #FBF7EE;
  --ink: #22262A;
  --ink-soft: #4A4843;
  --ink-faint: #66625A;
  --accent: #2E6E6B;
  --accent-strong: #1F4E4C;
  --accent-ink: #F6F0E3;
  --accent-2: #7E5C12;
  --accent-2-ink: #F6F0E3;
  --line: #D8CCB4;
  --line-strong: #BFB29A;
  --focus: #C4622D;
  --danger: #A8322A;
  --danger-bg: #F3DDD6;
  --ok-bg: #E3E9D2;
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(34, 38, 42, 0.10), 0 8px 20px rgba(34, 38, 42, 0.08);
  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body: 'Work Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  /* The logo's own display face (docs/brand/): every heading site-wide matches it, except
     the Styles section (catalogue.css's `--style-font`/`.kj-style-page`), which shows each
     furniture style's own representative font instead — kept on --font-display so it stays
     untouched by this. */
  --font-heading: 'Belanosima', var(--font-display);

  /* Register gradient (docs/brand/three_register_landing.md §2, njreid/woodwerx#196). These
     are the Workshop stop, which is the site's original look: a page opts into another stop
     through the register attribute on <html> (shell.rs). `--action` is the primary-button colour and
     `--check` the "this was verified" colour; both are `--accent` here, and Heirloom splits
     them (walnut for action, teal only for checks). `--walnut` is the one new palette
     colour (docs/brand/consumer_brand_strategy.md §7.2); it has no dark value on purpose,
     see the Heirloom dark override below. */
  --walnut: #3B2A1F;
  --walnut-ink: #F6F0E3;
  --action: var(--accent);
  --action-ink: var(--accent-ink);
  --check: var(--accent);
  --reg-h1-weight: 700;
  --reg-heading-weight: 600;
  --reg-h1-size: clamp(2.1rem, 5vw + 1rem, 3.4rem);
  --reg-h2-size: clamp(1.5rem, 2.6vw + 1rem, 2.1rem);
  --reg-body-lh: 1.55;
  --reg-section-pad: 56px;
  --reg-gap: 18px;
  color-scheme: light;
}

/* Heirloom: warm, quiet, tactile (consumer_brand_strategy.md §7). Fraunces for headings,
   lighter and larger air; walnut for the primary action; teal stays for "checked" only. */
:root[data-register="heirloom"] {
  --action: var(--walnut);
  --action-ink: var(--walnut-ink);
  --font-heading: var(--font-display);
  --reg-h1-weight: 400;
  --reg-heading-weight: 400;
  --reg-h1-size: clamp(2.3rem, 5.4vw + 1.1rem, 3.8rem);
  --reg-h2-size: clamp(1.65rem, 2.9vw + 1.1rem, 2.35rem);
  --reg-body-lh: 1.65;
  --reg-section-pad: 88px;
  --reg-gap: 28px;
}

/* #188 §2.2: one class any numeric figure (dimension, price, receipt line) wraps its text
   in, once such markup exists — no call site yet (see tests/contrast.rs). */
:root[data-register="heirloom"] .kj-figure {
  font-variant-numeric: tabular-nums;
}

/* Trade: the machine-envelope/capability-match register (three_register_landing.md §4.1,
   njreid/woodwerx#265, follow-up to #196). The dense end of the gradient: a flat, cool,
   always-dark instrument-panel palette (not the light/dark *preference* toggle Workshop and
   Heirloom have — Tomasz's page is the same page under either OS setting, same as a CNC
   control panel doesn't go "cream" in daylight), Work Sans carrying headings (Fraunces drops
   to a label role only, so no --font-display override is needed here), denser type and
   spacing, sharp corners, and teal narrowed to exactly one meaning: a passed capability-match
   check (`--check`), never a general accent (`personae.md` §3, "the least decorative surface
   on the platform"). `--action` is a neutral steel tone, not teal, so a plain "apply" button
   is never mistaken for a check result. Pairs are pinned in tests/contrast.rs. */
:root[data-register="trade"] {
  --bg: #070D14;
  --surface: #0E1620;
  --surface-2: #16212D;
  --ink: #DCE3E8;
  --ink-soft: #AAB4BC;
  --ink-faint: #7C8891;
  --line: #1A2733;
  --line-strong: #526679;
  --accent: #3FC2B8;
  --accent-strong: #6ED8CF;
  --accent-ink: #06130F;
  --accent-2: #C98A3B;
  --accent-2-ink: #140D02;
  --action: #4C6578;
  --action-ink: #EDF2F5;
  --check: var(--accent);
  --focus: #E8B23C;
  --danger: #FF8A73;
  --danger-bg: #3A1810;
  --ok-bg: #16301F;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 6px 14px rgba(0, 0, 0, 0.4);
  --radius: 4px;
  --radius-sm: 3px;
  --font-heading: var(--font-body);
  --reg-h1-weight: 700;
  --reg-heading-weight: 700;
  --reg-h1-size: clamp(2.1rem, 5vw + 1rem, 3.4rem);
  --reg-h2-size: clamp(1.5rem, 2.6vw + 1rem, 2.1rem);
  --reg-body-lh: 1.35;
  --reg-section-pad: 32px;
  --reg-gap: 10px;
  font-size: 94%;
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0A1936;
    --surface: #10254D;
    --surface-2: #17326A;
    --ink: #EEF2EE;
    --ink-soft: #C9D8E6;
    --ink-faint: #B2CAE2;
    --accent: #FFA774;
    --accent-strong: #FFBE94;
    --accent-ink: #0A1936;
    --accent-2: #F5D06A;
    --accent-2-ink: #0A1936;
    --line: #1C3A70;
    --line-strong: #4F73AB;
    --focus: #F5D06A;
    --danger: #FFB0A4;
    --danger-bg: #47222A;
    --ok-bg: #1F3A2A;
    --shadow: 0 1px 2px rgba(2, 8, 22, 0.45), 0 10px 24px rgba(2, 8, 22, 0.4);
    color-scheme: dark;
  }
}

/* Heirloom's own dark theme (njreid/woodwerx#266, consumer_brand_strategy.md §7.2/§11 item 6):
   warm charcoal, never the maker register's navy. Teal stays "checked" only (lightened to
   read on charcoal); walnut has no dark value, so the primary action is a light tan fill with
   dark ink. Pairs are pinned in tests/contrast.rs. */
@media (prefers-color-scheme: dark) {
  :root[data-register="heirloom"]:not([data-theme="light"]) {
    --bg: #1E1A17;
    --surface: #28231F;
    --surface-2: #332D28;
    --ink: #F1EADF;
    --ink-soft: #D3C9BA;
    --ink-faint: #B5AA99;
    --accent: #7FC4BF;
    --accent-strong: #A3D6D2;
    --accent-ink: #1E1A17;
    --accent-2: #E0B65A;
    --accent-2-ink: #1E1A17;
    --action: #D9B88F;
    --action-ink: #22190F;
    --check: var(--accent);
    --line: #3D3630;
    --line-strong: #7A6E61;
    --focus: #E0B65A;
    --danger: #F0A99B;
    --danger-bg: #432420;
    --ok-bg: #2B3322;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 10px 24px rgba(0, 0, 0, 0.4);
  }
}
:root[data-register="heirloom"][data-theme="dark"] {
  --bg: #1E1A17;
  --surface: #28231F;
  --surface-2: #332D28;
  --ink: #F1EADF;
  --ink-soft: #D3C9BA;
  --ink-faint: #B5AA99;
  --accent: #7FC4BF;
  --accent-strong: #A3D6D2;
  --accent-ink: #1E1A17;
  --accent-2: #E0B65A;
  --accent-2-ink: #1E1A17;
  --action: #D9B88F;
  --action-ink: #22190F;
  --check: var(--accent);
  --line: #3D3630;
  --line-strong: #7A6E61;
  --focus: #E0B65A;
  --danger: #F0A99B;
  --danger-bg: #432420;
  --ok-bg: #2B3322;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 10px 24px rgba(0, 0, 0, 0.4);
}

:root[data-theme="dark"] {
  --bg: #0A1936;
  --surface: #10254D;
  --surface-2: #17326A;
  --ink: #EEF2EE;
  --ink-soft: #C9D8E6;
  --ink-faint: #B2CAE2;
  --accent: #FFA774;
  --accent-strong: #FFBE94;
  --accent-ink: #0A1936;
  --accent-2: #F5D06A;
  --accent-2-ink: #0A1936;
  --line: #1C3A70;
  --line-strong: #4F73AB;
  --focus: #F5D06A;
  --danger: #FFB0A4;
  --danger-bg: #47222A;
  --ok-bg: #1F3A2A;
  --shadow: 0 1px 2px rgba(2, 8, 22, 0.45), 0 10px 24px rgba(2, 8, 22, 0.4);
  color-scheme: dark;
}

/* Typed custom properties (njreid/woodwerx#264, plan Task 3): every property above that the
   heirloom and trade register blocks (and base `:root`, and their nested dark-mode blocks)
   redeclare with a different value, so the ladder's token tween (shell.rs's ladder-only
   `transition`, static/register-transition.js) has something it's actually allowed to
   interpolate — an untyped custom property cannot be the target of a CSS `transition` at
   all; it snaps straight to the new value instead of tweening. Each `initial-value` below is
   that property's own base (Register::Workshop) `:root` value, written literally because an
   `@property` `initial-value` can't contain `var()`; parity with the base `:root` block is
   proven by `tests/register_transition.rs`. No register block's values change here — only
   typing is added, and typing alone changes no computed style, since every register still
   sets its own value in its own register-selector rule above.

   Enumerated by grepping every `--[a-z-]+:` across the base `:root` and the heirloom/trade
   register blocks above, and their nested dark-mode blocks, for a name redeclared with a
   different value in more than one of those blocks (plan correction 4; restated in full in
   the PR description):
     colour-valued (typed `<color>` below): --bg, --surface, --surface-2, --ink, --ink-soft,
       --ink-faint, --accent, --accent-strong, --accent-ink, --accent-2, --accent-2-ink,
       --action, --action-ink, --check, --line, --line-strong, --focus, --danger, --danger-bg,
       --ok-bg.
     length-valued (typed `<length>` below): --reg-section-pad, --reg-gap, --radius,
       --radius-sm.
   Deliberately NOT registered, one reason each:
     --font-heading: not a typed primitive CSS can tween (D-T4's snap-not-tween decision); the
       JS fallback snaps this one explicitly instead (static/register-transition.js step 3).
     --reg-h1-weight, --reg-heading-weight: same reasoning, spec §6's "nothing to gain" — a
       font-weight tween reads as a flash, not a slide; also snapped by the JS fallback.
     --shadow: a multi-part `<color> <length> <length>` list (two full box-shadow layers); no
       single `@property` syntax primitive matches a box-shadow list (correction 4's finding).
     --reg-body-lh: a unitless line-height multiplier (1.55/1.65/1.35). This grep turns it up
       too (redeclared differently in all three registers), but it fits neither `<color>` nor
       `<length>` — a bare number needs `syntax: '<number>'`, which is outside the exact
       property list Task 3's own text asks this pass to type — so it's left untyped and snaps,
       same as the weight tokens above. A small addendum to correction 4's enumeration, found
       by the same grep, not silently dropped. */
@property --bg { syntax: '<color>'; inherits: true; initial-value: #EFE7D6; }
@property --surface { syntax: '<color>'; inherits: true; initial-value: #F6F0E3; }
@property --surface-2 { syntax: '<color>'; inherits: true; initial-value: #FBF7EE; }
@property --ink { syntax: '<color>'; inherits: true; initial-value: #22262A; }
@property --ink-soft { syntax: '<color>'; inherits: true; initial-value: #4A4843; }
@property --ink-faint { syntax: '<color>'; inherits: true; initial-value: #66625A; }
@property --accent { syntax: '<color>'; inherits: true; initial-value: #2E6E6B; }
@property --accent-strong { syntax: '<color>'; inherits: true; initial-value: #1F4E4C; }
@property --accent-ink { syntax: '<color>'; inherits: true; initial-value: #F6F0E3; }
@property --accent-2 { syntax: '<color>'; inherits: true; initial-value: #7E5C12; }
@property --accent-2-ink { syntax: '<color>'; inherits: true; initial-value: #F6F0E3; }
/* --action/--action-ink/--check's base `:root` declaration is `var(--accent)`/
   `var(--accent-ink)`/`var(--accent)` (kj.css above) — an `@property` initial-value can't
   reference `var()`, so these are written as --accent/--accent-ink's own resolved literals,
   which is exactly what `var()` resolves to at the base `:root`; tests/register_transition.rs
   checks this resolution, not just a string match. */
@property --action { syntax: '<color>'; inherits: true; initial-value: #2E6E6B; }
@property --action-ink { syntax: '<color>'; inherits: true; initial-value: #F6F0E3; }
@property --check { syntax: '<color>'; inherits: true; initial-value: #2E6E6B; }
@property --line { syntax: '<color>'; inherits: true; initial-value: #D8CCB4; }
@property --line-strong { syntax: '<color>'; inherits: true; initial-value: #BFB29A; }
@property --focus { syntax: '<color>'; inherits: true; initial-value: #C4622D; }
@property --danger { syntax: '<color>'; inherits: true; initial-value: #A8322A; }
@property --danger-bg { syntax: '<color>'; inherits: true; initial-value: #F3DDD6; }
@property --ok-bg { syntax: '<color>'; inherits: true; initial-value: #E3E9D2; }
@property --reg-section-pad { syntax: '<length>'; inherits: true; initial-value: 56px; }
@property --reg-gap { syntax: '<length>'; inherits: true; initial-value: 18px; }
@property --radius { syntax: '<length>'; inherits: true; initial-value: 12px; }
@property --radius-sm { syntax: '<length>'; inherits: true; initial-value: 8px; }

/* ---------- base typography and layout ---------- */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 16px;
  line-height: var(--reg-body-lh);
  overflow-x: hidden;
}

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

a { color: var(--accent-strong); }
a:not(.kj-btn) { text-decoration-thickness: 1.5px; text-underline-offset: 2px; }

h1, h2, h3 {
  font-family: var(--font-heading);
  font-weight: var(--reg-heading-weight);
  line-height: 1.12;
  color: var(--ink);
  margin: 0 0 0.5em;
}
h1 { font-size: var(--reg-h1-size); font-weight: var(--reg-h1-weight); }
h2 { font-size: var(--reg-h2-size); }
h3 { font-size: 1.15rem; font-weight: var(--reg-heading-weight); }

p { margin: 0 0 1em; color: var(--ink-soft); }
.lede { font-size: 1.15rem; color: var(--ink); }

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

.wrap {
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding-left: 20px;
  padding-right: 20px;
}

/* ---------- header ---------- */
.kj-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.kj-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 14px;
  padding-bottom: 14px;
  flex-wrap: wrap;
}
/* The header spans the full page: logo at the far left, the nav after it, Profile pushed to
   the far right. (Page content below keeps its own 1080px `.wrap`.) */
.kj-header .wrap { max-width: none; }
.kj-header nav { margin-left: 12px; }
.kj-profile { position: relative; margin-left: auto; display: flex; align-items: center; gap: 14px; }
/* #781: the magnifying-glass icon beside "New Project" expands into a plain text input —
   collapsed by default (icon only), `.kj-search-toggle-open` (added by `SEARCH_TOGGLE_JS` on
   click or `/`) reveals the box. No JS means no box, only the icon, which still submits a
   normal `GET /search?q=` on Enter if JS never ran -- degrades to "the icon is a link-shaped
   button with a text field next to it", not a dead control. */
.kj-search-toggle { position: relative; display: flex; align-items: center; gap: 6px; }
.kj-search-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.95rem;
}
.kj-search-icon-btn:hover, .kj-search-icon-btn[aria-expanded="true"] { border-color: var(--line-strong); }
.kj-search-toggle-input {
  width: 0;
  padding: 6px 0;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.85rem;
  opacity: 0;
  transition: width 150ms ease, opacity 150ms ease, padding 150ms ease;
}
.kj-search-toggle-open .kj-search-toggle-input {
  width: min(220px, calc(100vw - 220px));
  padding: 6px 12px;
  border-color: var(--line);
  opacity: 1;
}
/* #890: type-ahead dropdown under the header box, filled by `static/search-suggest.js`. */
.kj-suggest {
  position: absolute; top: calc(100% + 6px); right: 0;
  width: min(340px, calc(100vw - 32px)); z-index: 50;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); overflow: hidden;
}
.kj-suggest[hidden] { display: none; }
.kj-suggest-row {
  display: flex; align-items: center; gap: 10px; padding: 8px 12px;
  color: var(--ink); text-decoration: none; font-size: 0.9rem;
}
.kj-suggest-row:hover, .kj-suggest-row[aria-selected="true"] { background: var(--accent); color: var(--accent-ink); }
.kj-suggest-row:hover .kj-kind-icon, .kj-suggest-row[aria-selected="true"] .kj-kind-icon { color: inherit; }
.kj-suggest-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kj-suggest-kind { font-size: 0.75rem; opacity: 0.75; }
/* #new-project-btn reuses the shared `.kj-btn--primary` hero-CTA style elsewhere on the
   site, but beside Profile in the header it needs to match Profile's compact pill, not a
   hero button's size — scoped to this one spot rather than resizing `.kj-btn` globally. */
#new-project-btn {
  padding: 6px 12px;
  font-size: 0.85rem;
  border-width: 1px;
}
.kj-profile-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
}
.kj-profile-btn:hover, .kj-profile-btn[aria-expanded="true"] { border-color: var(--line-strong); }
.kj-profile-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  z-index: 30;
  width: min(280px, calc(100vw - 32px));
  padding: 8px;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: var(--shadow);
}
.kj-profile-menu[hidden] { display: none; }
.kj-profile-note, .kj-profile-soon {
  margin: 4px 8px 8px;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--ink-soft);
}
.kj-profile-soon { margin: 8px 8px 4px; color: var(--ink-faint); }
.kj-profile-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-radius: 8px;
  padding: 8px;
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
}
.kj-profile-item:hover:not(:disabled) { background: var(--surface); }
.kj-profile-item:disabled { color: var(--ink-faint); cursor: default; }
.kj-wordmark {
  display: flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}
/* The wordmark SVG (docs/brand/): full colour for light, the reversed (cream ink, same
   amber) for dark — one <img> per theme, toggled by display rather than swapping `src`, so
   there's no flash or extra request on a theme change. */
.kj-wordmark-logo { display: block; height: 55px; width: auto; }
.kj-wordmark-logo--dark { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .kj-wordmark-logo--light { display: none; }
  :root:not([data-theme="light"]) .kj-wordmark-logo--dark { display: block; }
}
:root[data-theme="dark"] .kj-wordmark-logo--light { display: none; }
:root[data-theme="dark"] .kj-wordmark-logo--dark { display: block; }
.kj-wordmark small {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.7rem;
  color: var(--ink-faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.kj-header nav {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  align-items: center;
}
.kj-header nav a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 500;
  padding: 6px 10px;
  border-radius: 999px;
}
.kj-header nav a:hover { background: var(--surface); color: var(--ink); }
.kj-nav-btn { display: none; }
/* Almanac / Technology submenus (njreid/woodwerx#891). The parent is a real link to its landing
   page; the caret button (wired by NAV_MENU_JS) opens the list. Nothing is hover-only, and with
   no JS the list stays closed and the parent link is the way in. */
.kj-nav-group { position: relative; display: inline-flex; align-items: center; }
.kj-header nav .kj-nav-parent { padding-right: 4px; }
.kj-nav-caret {
  background: none;
  border: 0;
  color: var(--ink-soft);
  font-size: 0.8rem;
  line-height: 1;
  padding: 8px 8px 8px 2px;
  border-radius: 999px;
  cursor: pointer;
}
.kj-nav-caret:hover { color: var(--ink); background: var(--surface); }
.kj-nav-group-open .kj-nav-caret span { display: inline-block; transform: rotate(180deg); }
.kj-nav-sub {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 30;
  min-width: 12rem;
  margin: 4px 0 0;
  padding: 6px;
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
}
.kj-nav-group-open .kj-nav-sub { display: block; }
.kj-header nav .kj-nav-sub a { display: block; border-radius: 8px; white-space: nowrap; }
@media (max-width: 640px) {
  .kj-header nav { order: 3; flex-basis: 100%; margin-left: 0; }
  /* Collapse behind the Menu button once nav JS has run (no JS: links stay visible). */
  .kj-nav-js .kj-nav-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--surface);
    border: 1px solid var(--line);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 0.85rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
  .kj-nav-js:not(.kj-nav-open) nav { display: none; }
  /* Phone: the submenus are plain indented lists inside the open Menu panel; no carets. */
  .kj-nav-group { display: block; width: 100%; }
  .kj-nav-caret { display: none; }
  .kj-nav-sub {
    display: block;
    position: static;
    min-width: 0;
    margin: 0;
    padding: 0 0 0 18px;
    background: none;
    border: 0;
    box-shadow: none;
  }
}
.kj-theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
}
.kj-theme-toggle:hover { border-color: var(--line-strong); }

/* ---------- footer ---------- */
.kj-footer {
  padding: 32px 0 44px;
  color: var(--ink-faint);
  font-size: 0.88rem;
}
.kj-footer a { color: var(--ink-faint); }
.kj-footer .foot-row {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

/* ---------- buttons (kj-widgets::button) ---------- */
.kj-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.98rem;
  padding: 12px 22px;
  border-radius: 999px;
  border: 2px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.kj-btn--primary { background: var(--action); color: var(--action-ink); }
.kj-btn--primary:hover { box-shadow: var(--shadow); }
.kj-btn--secondary { background: transparent; border-color: var(--line-strong); color: var(--ink); }
.kj-btn--secondary:hover { background: var(--surface); }
@media (prefers-reduced-motion: reduce) {
  .kj-btn { transition: none; }
}

/* ---------- fields (kj-widgets::text_field) ---------- */
.kj-field { margin-bottom: 20px; }
.kj-field label,
fieldset.kj-field legend {
  display: block;
  font-weight: 600;
  font-size: 0.94rem;
  margin-bottom: 6px;
  color: var(--ink);
}
.kj-optional { font-weight: 400; color: var(--ink-faint); }
.kj-hint { font-size: 0.85rem; color: var(--ink-faint); margin-top: 4px; }
.kj-field input[type="text"],
.kj-field input[type="email"],
.kj-field select,
.kj-field textarea {
  width: 100%;
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--ink);
  background: var(--surface-2);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
}
.kj-field textarea { resize: vertical; min-height: 88px; }

/* ---------- choice field (kj-widgets::choice_field, #702) ---------- */
fieldset.kj-choice { border: none; padding: 0; margin: 0 0 20px; }
.kj-choice-radios { display: flex; flex-wrap: wrap; gap: 6px 16px; }
.kj-choice-radio {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 400;
  font-size: 0.94rem;
  color: var(--ink);
  cursor: pointer;
}
.kj-choice-radio input[type="radio"] { margin: 0; }

/* ---------- notices (kj-widgets::notice) ---------- */
.kj-notice {
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin-bottom: 18px;
}
.kj-notice--ok { color: var(--ink); background: var(--ok-bg); border-color: var(--line); }
.kj-notice--error { color: var(--danger); background: var(--danger-bg); border-color: var(--danger); }
.kj-notice strong { color: var(--accent-strong); }

/* kj-widgets `notice` (moved from waitlist.css so every page has it). */
.kj-notice {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 22px;
  margin-bottom: 18px;
}
.kj-notice--ok {
  background: var(--ok-bg);
}
.kj-notice--error {
  background: var(--danger-bg);
  border-color: var(--danger);
  color: var(--danger);
}
.kj-notice p {
  margin: 0;
}
.kj-notice h2,
.kj-notice h3 {
  margin: 0 0 6px;
}

/* ---------- sign-in page (#823 redesign: Google SSO / email magic-link, equal weight) ---------- */
.kj-signin { max-width: 680px; }
.kj-signin-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  margin: 24px 0;
  align-items: start;
}
.kj-signin-option {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.kj-signin-option h2 { margin: 0; font-size: 1.1rem; font-family: var(--font-heading); }
.kj-signin-option .kj-btn { align-self: flex-start; margin-top: 4px; }
.kj-signin-passkey { margin-top: 4px; }

/* The emailed six-digit code, kept as the fallback for a mail client that strips links. */
.kj-disclosure { margin-top: 18px; }
.kj-disclosure summary { cursor: pointer; font-weight: 600; color: var(--ink); margin-bottom: 10px; }

/* ---------- post-sign-in passkey enrollment nudge (#823) ---------- */
#passkey-nudge { display: flex; flex-direction: column; gap: 10px; }
#passkey-nudge .kj-nudge-actions { display: flex; gap: 10px; flex-wrap: wrap; }
#passkey-nudge[hidden] { display: none; }

/* ---------- embedded-viewer loading overlay ---------- */
/* `ww web`'s own bundle always boots whichever project it happens to default to before
   configure.js/catalogue-viewer.js can switch the iframe to the one this page actually wants
   (both files' own doc comments explain why the switch can't just be a URL parameter) — so
   without this, the wrong model visibly flashes first. The page that owns the iframe
   (`.kj-configure`/`.kj-cat-hero-viewer`, both already `position: relative`) shows this by
   default and hides it once that switch is confirmed. */
.kj-viewer-loading {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: var(--surface-2);
  border-radius: var(--radius);
  color: var(--ink-faint);
  font-size: 0.88rem;
}
.kj-viewer-loading[hidden] { display: none; }
.kj-viewer-spinner {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 3px solid var(--line-strong);
  border-top-color: var(--accent);
  animation: kj-viewer-spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .kj-viewer-spinner { animation-duration: 2.4s; }
}
@keyframes kj-viewer-spin {
  to { transform: rotate(360deg); }
}

/* ---------- accounts: Settings dialog, My projects, Public profile ---------- */
/* njreid/woodwerx#625: these pages passed `extra_css: None` and relied on `.wrap`'s bare
   left/right padding, so content sat flush under the sticky header and flush above the
   footer, and the profile-edit form's bare `label`/`input` pairs (no `.kj-field` wrapper)
   ran together inline with no spacing at all — the single worst offender. */
.kj-account-page {
  padding-top: 32px;
  padding-bottom: 48px;
}
.kj-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -8px 0 20px;
  font-weight: 400;
  font-size: 0.94rem;
  color: var(--ink);
}
.kj-dialog {
  width: min(28rem, calc(100vw - 32px));
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  padding: 1.25rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.kj-dialog::backdrop { background: rgba(10, 25, 54, 0.55); }
.kj-dialog h2 { margin-top: 0; font-size: 1.5rem; }
.kj-dialog h3 { margin: 1.25rem 0 0.4rem; }
.kj-dialog label { display: block; margin: 0.6rem 0; font-weight: 600; font-size: 0.94rem; }
.kj-dialog select {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 400;
  color: var(--ink);
  background: var(--surface-2);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-sm);
}
.kj-dialog a { color: var(--accent-strong); }
.kj-dialog-close { float: right; margin: 0; }
.kj-dialog-close button {
  width: 2rem;
  height: 2rem;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  cursor: pointer;
}
.kj-dialog-close button:hover { background: var(--surface-2); }
.kj-dialog :focus-visible, .kj-profile-item:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.kj-error { color: var(--danger); }
.kj-inline-form { display: inline; margin-left: .5rem; }
.kj-muted { color: var(--ink-faint); }
.kj-project-list { list-style: none; padding: 0; margin: 1rem 0 1.5rem; }
.kj-project-list li { padding: 12px 0; border-bottom: 1px solid var(--line); }

/* ---------- hero image and its render label (crates/kj-web/src/hero.rs, njreid/woodwerx#193) ----------
   The chip sits on an arbitrary picture, so its colours are fixed, not theme tokens, and chosen to stay
   legible over the worst case (a white image): hero.rs's test blends them over white and black. */
:root {
  --render-chip-bg: rgba(34, 38, 42, 0.86);
  --render-chip-ink: #F4EFE3;
}
.kj-hero-figure { margin: 0; }
.kj-hero-frame { position: relative; line-height: 0; border-radius: var(--radius); overflow: hidden; }
.kj-hero-img { display: block; width: 100%; height: auto; }
.kj-render-chip {
  position: absolute; left: 12px; bottom: 12px;
  padding: 4px 10px; border-radius: 6px;
  font: 600 0.8125rem/1.3 var(--font-body); letter-spacing: 0.02em;
  background: var(--render-chip-bg); color: var(--render-chip-ink);
}
.kj-hero-caption { margin: 8px 2px 0; font-size: 0.875rem; line-height: 1.4; color: var(--ink-soft); }

/* ---------- agent setup: three-tab IA, Connect dropdown, liveness spinner (issue #832) ---------- */
.kj-agents-tabs {
  display: flex; gap: 4px; flex-wrap: wrap;
  margin: 18px 0; border-bottom: 1px solid var(--line);
}
.kj-agents-tabs button {
  background: none; border: none; border-bottom: 2px solid transparent;
  padding: 8px 4px; margin-right: 16px;
  font: inherit; font-weight: 600; color: var(--ink-faint); cursor: pointer;
}
.kj-agents-tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
.kj-agents-tabpanel { margin-top: 12px; }
.kj-agents-client { margin: 12px 0; }
.kj-agents-caveat { font-style: italic; }
.kj-agents-waiting { display: flex; align-items: center; gap: 10px; }
/* Same zero-asset technique as `.kj-viewer-spinner` above, sized for an inline line of text
   rather than a full-frame overlay. */
.kj-spinner {
  display: inline-block; flex: none;
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--accent);
  animation: kj-spin 0.8s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .kj-spinner { animation-duration: 2.4s; }
}
@keyframes kj-spin {
  to { transform: rotate(360deg); }
}
.kj-agents-activity td, .kj-agents-calls td { vertical-align: top; }
.kj-agents-events { padding-left: 1.2em; }

/* The favorites toast (issue #68, static/favorites.js's `toast`): bottom-centre, auto-hidden. */
.kj-favorites-toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 40;
  max-width: min(32rem, calc(100vw - 32px));
  padding: 10px 16px; border-radius: var(--radius);
  background: var(--ink); color: var(--bg); box-shadow: var(--shadow);
  font: 500 0.9375rem/1.4 var(--font-body);
}
.kj-favorites-toast[hidden] { display: none; }

/* A label kept for assistive tech only, visually collapsed (clip-path, not display:none, so
   it still reads to a screen reader) — the header search box's own `<label>`, #781. */
.kj-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* #781: `GET /search?q=` results, one relevance-ranked list with kind icons and filters (#890). */
.kj-search-page .kj-search-form { display: flex; gap: 8px; margin: 12px 0 24px; max-width: 32rem; }
.kj-search-page .kj-search-form input[type="search"] {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.95rem;
}
.kj-kind-icon { flex: none; vertical-align: -4px; color: var(--ink-soft); }
.kj-search-filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 20px; }
.kj-search-filter {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink); font-size: 0.85rem; text-decoration: none;
}
.kj-search-filter:hover { border-color: var(--line-strong); }
.kj-search-filter[aria-current="true"] { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.kj-search-filter[aria-current="true"] .kj-kind-icon { color: inherit; }
.kj-search-count { opacity: 0.75; font-variant-numeric: tabular-nums; }
.kj-search-results { list-style: none; margin: 0; padding: 0; }
.kj-search-results li { display: flex; gap: 12px; margin-bottom: 16px; }
.kj-search-results li > .kj-kind-icon { margin-top: 3px; }
.kj-search-result-body { min-width: 0; }
.kj-search-result-body a { font-weight: 600; }
.kj-search-kind { margin-left: 6px; color: var(--ink-soft); font-size: 0.8rem; }
.kj-search-snippet { margin: 4px 0 0; color: var(--ink-soft); font-size: 0.9rem; }
.kj-search-snippet mark { background: var(--accent); color: var(--accent-ink); padding: 0 2px; border-radius: 2px; }

/* #863: New Project two-step chooser — two big choice cards on step 1, kind cards on step 2.
   Single column on a phone, two columns from 720px. */
.kj-new-project-lead { margin: 0 0 1rem; color: var(--kj-muted, inherit); }
.kj-new-project-choices { display: grid; gap: 12px; grid-template-columns: 1fr; margin: 1rem 0; }
@media (min-width: 720px) { .kj-new-project-choices { grid-template-columns: 1fr 1fr; } }
.kj-new-project-choice { display: block; width: 100%; text-align: left; padding: 16px; min-height: 44px; border: 1px solid currentColor; border-radius: 8px; background: transparent; color: inherit; text-decoration: none; font: inherit; cursor: pointer; }
.kj-new-project-choice h2 { margin: 0 0 4px; font-size: 1.1rem; }
.kj-new-project-choice p { margin: 0; }
.kj-new-project-choice:hover, .kj-new-project-choice:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
.kj-new-project-recent ul { list-style: none; padding: 0; margin: 0; }
.kj-new-project-recent li a { display: block; padding: 10px 0; min-height: 44px; }

/* ---------- Almanac / Technology landing pages (njreid/woodwerx#891) ---------- */
.kj-section-page { max-width: 46rem; padding-top: 32px; padding-bottom: 40px; }
.kj-section-lede { font-size: 1.1rem; color: var(--ink-soft); }
.kj-section-cards {
  list-style: none;
  margin: 24px 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 12px;
}
.kj-section-card a {
  display: block;
  height: 100%;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
}
.kj-section-card a:hover { border-color: var(--line-strong); }
.kj-section-card strong { display: block; margin-bottom: 4px; }
.kj-section-card span { color: var(--ink-soft); font-size: 0.92rem; }
.kj-section-steps li { margin-bottom: 6px; }
