/* ============================================
   DESIGN TOKENS  —  see TYPE-SYSTEM.md for the rules these encode.
   Four text colours, three fonts, nine sizes. Adding a value means updating
   that file too.
   Extracted from the live Webflow site.
   NOTE on fonts: Recoleta and Cabinetgrotesk Variable are
   paid/licensed fonts (not on Google Fonts) — they are declared
   below with safe fallbacks. Drop your licensed .woff2 files into
   an /assets/fonts folder and uncomment the @font-face blocks to
   get the exact original typefaces. Plus Jakarta Sans, IBM Plex
   Mono, and Lora are free Google Fonts and are already wired up.
   ============================================ */

:root {
  --bg-cream: #f7f4f2;
  --bg-band: #f0ebe7;   /* full-bleed band behind the homepage index sections */
  --surface: #eeece9;   /* image/media placeholder while loading */
  /* Hairlines. These MUST be literal values — a `var(--rule)` self-reference
     crept in during an earlier pass, which is a computed-value cycle: the
     token resolves to nothing, the whole `border: 1px solid var(--rule)`
     shorthand becomes invalid, and border-width computes to 0. Every divider
     on the site silently vanished in light mode (dark mode was unaffected
     because .dark-mode redeclares both with real values). */
  --rule: rgba(0, 0, 0, 0.18);        /* ~#cbc8c6 on cream — visible hairline */
  --rule-soft: rgba(0, 0, 0, 0.11);   /* ~#dcd9d7 — secondary dividers */
  --veil: rgba(255, 255, 255, 0.72);   /* floating buttons */
  --veil-strong: rgba(255, 255, 255, 0.95);
  --veil-border: rgba(17, 17, 17, 0.14);
  --bg-dark: #141412;      /* neutral ink */

  /* ---- TEXT COLOUR — four values on light, two on dark. Nothing else. ----
     Every one-off grey (#797979 #938d9c #8b8792 #a9a5b0 #b6b0c2 #262229) has
     been folded into these. If a new grey feels necessary, it isn't. */
  --text-primary: #111111;        /* headings, titles, links at rest */
  --text-body:    #333333;        /* body copy */
  --text-muted:   #756e64;        /* labels, meta, captions, nav at rest */
  --text-hover:   #000000;        /* the ONLY hover colour — darker, never a hue */
  --text-white:   #ffffff;
  --text-muted-on-dark: #b3aca1;

  /* The single accent. Japanese editorial red — reserved for numerals, end
     marks, and the footer action block. It never colours prose or hovers; the
     "hover darkens, never a hue" rule stands. */
  --accent: #c73e1d;

  --font-display: 'Fraunces', Georgia, serif;
  --font-sans: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
  --font-serif-body: 'Lora', Georgia, serif;

  --radius-sm: 0;
  --radius-md: 0;
  --radius-pill: 0;

  --wrap-width: 1320px;
  --wrap-gutter: 40px;

  /* Vertical rhythm. Four steps only — every block-level gap should come from
     one of these rather than a one-off number. */
  --space-page: 96px;   /* between major page sections (case study → case study) */
  --space-section: 72px;/* between a page section and the next section label */
  --space-block: 32px;  /* between a block and its neighbour inside a section */
  --space-el: 20px;     /* label → content, title → body */
}

/* Mobile runs a tighter rhythm — the desktop steps leave far too much air on a
   375px screen, where a 64px gap is a sixth of the viewport height. */
@media (max-width: 760px) {
  :root {
    --space-page: 48px;
    --space-section: 36px;
    --space-block: 20px;
    --space-el: 14px;
  }
}

/* Uncomment once you have licensed font files:
@font-face {
  font-family: 'Recoleta';
  src: url('assets/fonts/Recoleta-Medium.woff2') format('woff2');
  font-weight: 500;
}
@font-face {
  font-family: 'Cabinetgrotesk Variable';
  src: url('assets/fonts/CabinetGrotesk-Variable.woff2') format('woff2');
  font-weight: 100 900;
}
*/

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  /* Pin Fraunces to its display cut at every size — optical sizing was
     serving a different-looking low-contrast cut below ~40px, which read as
     a second serif. One serif, one sans, one cut each. */
  font-optical-sizing: none;
  font-variation-settings: "opsz" 84;
  background: var(--bg-cream);
  color: var(--text-body);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

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

a {
  color: inherit;
  text-decoration: none;
}

.wrap {
  max-width: var(--wrap-width);
  margin: 0 auto;
  padding: 0 var(--wrap-gutter);
}

@media (max-width: 760px) { :root { --wrap-gutter: 24px; } }

/* ---- touch targets ----
   Text links are only as tall as their line box (15–22px), which is well under
   the 44px touch guideline. Rather than inflate the type, give links a padded
   hit area on touch devices and pull it back with negative margin so nothing
   moves visually. `any-pointer: coarse` catches phones and tablets without
   penalising mouse users. */
@media (any-pointer: coarse) {
  /* Text links get HEIGHT only. A previous edit collapsed this rule into the
     .nav-toggle one below, so every CTA on the site inherited `width: 44px`
     and its label overflowed a 44px box on every touch device. */
  .thought-link,
  .footer-col a,
  .fx-links a,
  .nav-overlay-footer a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* the hamburger is genuinely a 44px square */
  .nav-toggle { width: 44px; height: 44px; }

  .logo { padding: 12px 0; margin: -12px 0; }

  /* the accordion row is the primary control on the homepage */
  .case-archive-toggle { min-height: 56px; }
}


/* ============================================
   PAGE RHYTHM
   One rule for vertical spacing between top-level sections, so no page invents
   its own. Every section owns the gap ABOVE it and nothing below; the last one
   adds the gap before the footer. That removes the doubling that produced
   96+96 between sections and 96+96 under the Playground header.
   ============================================ */

/* Hero: one padding-top on every page type, and no padding-bottom — the next
   section supplies that gap, same as everywhere else. */
.home-intro,
.page-hero,
.ed-hero,
.post-header,
.about-section:first-child { padding-top: 64px; padding-bottom: 0; }

/* Every other top-level section owns the gap ABOVE it. A comment had been
   inserted mid-selector-list here, which swallowed this rule's declaration
   block — so every section lost its padding-top and silently took the
   `padding-bottom` of the rule below. That is why the masthead sat flush
   against the first case study. */
.case-study,
.case-archive,
.home-section,
.craft-section,
.thoughts,
.cv,
.ed-chapter,
.ed-closing { padding-top: var(--space-page); padding-bottom: 0; }

/* Space before the footer — /thoughts previously had none at all */
main > :last-child { padding-bottom: var(--space-page); }

/* About is both first and last child, so `.about-section:first-child` (0,2,0)
   was out-specifying `main > :last-child` (0,1,1) and zeroing the gap — the
   photo grid ran straight into the footer. */
main > .about-section:last-child { padding-bottom: var(--space-page); }

/* A tinted band carries its own internal padding, and a small page-colour gap
   above so the fill edge reads as deliberate. 24 + 72 lands on the same 96px
   rhythm as every other section gap. */
.tint-band { margin-top: 24px; padding-top: var(--space-section); padding-bottom: var(--space-section); }
.craft-section.tint-band { padding-top: var(--space-section); }
main > .tint-band:last-child { padding-bottom: var(--space-section); }

.arrow { display: inline-block; transition: transform 0.2s ease; }
a:hover .arrow { transform: translateX(3px); }

/* ============ NAV ============ */

/* Ported from Pulse (css/styles.css `.top-bar`): the bar hides on scroll-down
   and returns on scroll-up, and grows a hairline once you're past it.
   One deviation — Pulse uses `position: fixed`, which needs the page to reserve
   the bar's height or the first section slides underneath. `sticky` gives the
   identical hide/reveal while staying in flow, so no compensating offset can
   drift out of sync with the bar's real height. */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  padding: 32px 0;
  background: var(--bg-cream);
  border-bottom: 1px solid transparent;
  transition: transform 0.3s ease, border-color 0.3s ease;
}

.nav.hidden-bar { transform: translateY(-100%); }
.nav.show-stroke { border-bottom-color: var(--rule); }

/* Tinted sections run full-bleed under the bar, so the bar matches whichever
   background it is currently sitting on rather than flashing cream over tint. */
@media (prefers-reduced-motion: reduce) {
  .nav { transition: none; }
}

/* ============================================
   HOME INDEX
   Replaces the three stacked Playground / Craft / Thoughts sections, which each
   repeated the same label → description → CTA → 3-up grid and read as a
   template. One header, one mixed grid, one row of links out. Each tile carries
   its own category tag so the buckets still read without three headers.
   ============================================ */

.index-head {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: 40px;
  align-items: baseline;
  padding-bottom: var(--space-block);
  border-bottom: 1px solid var(--rule);
}

.index-head .section-heading { margin: 0; }

.index-head p {
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 52ch;
  margin: 0;
  text-wrap: pretty;
}

/* Three columns, but the first tile of each row spans two — so the rhythm is
   uneven on purpose and doesn't read as another uniform 3-up grid. */
.index-links {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  margin-top: var(--space-section);
  padding-top: var(--space-block);
  border-top: 1px solid var(--rule);
}

@media (max-width: 860px) {
  .index-head { grid-template-columns: 1fr; gap: 12px; }
}

@media (max-width: 760px) {
    /* the feature tile spans the full width rather than an awkward 2-of-2 */
    .index-links { gap: 20px 28px; }
}

/* ---- floating actions / scroll to top (ported from Pulse) ---- */

.floating-actions {
  position: fixed;
  bottom: 2rem;
  right: 2rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  z-index: 50;
}

/* Docked (nav.js): rests at the bottom of the section before the footer
   instead of riding the fixed offset down over it. `top` is set inline by
   the script; `bottom` has to be cleared or the two would fight. */
.floating-actions.is-docked {
  position: absolute;
  bottom: auto;
}

.scroll-top-btn {
  width: 44px;
  height: 44px;
  border-radius: 0;
  background: var(--veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--veil-border);
  color: var(--text-primary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: opacity 0.3s, transform 0.3s, background 0.2s;
}

.scroll-top-btn.visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.scroll-top-btn:hover { background: var(--veil-strong); }

/* Theme toggle — same geometry as the scroll button (ported from Pulse). */
/* Lives in the nav, not the floating stack. As a fixed overlay it collided
   with the reading column on every page — worst on the case studies, where
   the sticky panel occupies exactly the bottom-right corner it sat in. In the
   nav it can never overlap content, and it travels with the bar. */
.theme-toggle-btn {
  width: 36px;
  height: 36px;
  margin-left: 28px;
  border: 0;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  flex-shrink: 0;
  transition: color 0.2s ease;
}

.theme-toggle-btn:hover { color: var(--text-hover); }

/* Dark is the default, so the sun (switch to light) shows first. */
.theme-toggle-btn .icon-sun { display: none; }
.theme-toggle-btn .icon-moon { display: block; }
.dark-mode .theme-toggle-btn .icon-sun { display: block; }
.dark-mode .theme-toggle-btn .icon-moon { display: none; }

@media (max-width: 760px) {
  .floating-actions { bottom: 1rem; right: 1rem; }
  /* beside the hamburger, ahead of it in reading order */
  .theme-toggle-btn { margin-left: 0; margin-right: 4px; }
}

.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* keeps the toggle tight against the nav links rather than pushed to the edge */
.nav-links { margin-left: auto; }

.logo { display: inline-flex; line-height: 0; }
.logo img { width: 204px; height: auto; display: block; }

.nav-links {
  display: flex;
  gap: 32px;
}

.nav-links a {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-muted);
}

/* Nav sits muted at rest so hover and current-page read as a genuine darkening
   rather than a colour change. */
.nav-links a:hover { color: var(--text-primary); }
.nav-links a.current { color: var(--text-primary); font-weight: 600; }

/* ---- mobile menu toggle ---- */

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  position: relative;
  z-index: 1100;
}

.nav-toggle span {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--text-primary);
  border-radius: 0;
  transition: transform 0.25s ease, opacity 0.2s ease;
}

body.nav-open .nav-toggle span { background: var(--text-white); }
body.nav-open .nav-toggle span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
body.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* ---- full-screen mobile menu ---- */

.nav-overlay {
  position: fixed;
  inset: 0;
  z-index: 1050;
  background: var(--bg-dark);
  display: flex;
  flex-direction: column;
  padding: 32px 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-12px);
  transition: opacity 0.3s ease, transform 0.3s ease, visibility 0s linear 0.3s;
}

body.nav-open .nav-overlay {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

/* Safety net: pointer-events is not transitioned, so even if the delayed
   `visibility` transition is ever skipped the closed overlay cannot swallow
   taps the way it did before. */
.nav-overlay { pointer-events: none; }
body.nav-open .nav-overlay { pointer-events: auto; }

.nav-overlay-top {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  text-align: left;
  width: 100%;
  margin: 0;
}

.nav-overlay-top .logo img { width: 138px; }

.nav-overlay-links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 28px;
  width: 100%;
  margin: 88px 0 0;
}

.nav-overlay-links a {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(30px, 9vw, 40px);
  color: var(--text-white);
  line-height: 1.1;
}

.nav-overlay-links a.current { color: var(--text-white); }
.nav-overlay-links a:active,
.nav-overlay-links a:hover { color: var(--text-white); }

.nav-overlay-footer {
  padding-top: 24px;
  border-top: 1px solid rgba(255,255,255,0.14);
  display: flex;
  justify-content: flex-start;
  gap: 28px;
  width: 100%;
  margin: auto 0 0;
}

.nav-overlay-footer a {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted-on-dark);
}

.nav-overlay-footer a:hover { color: var(--text-white); }

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

.hero {
  padding-top: 56px;
  padding-bottom: 40px;
}

.avatar-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 24px;
  position: relative;
}

.avatar {
  width: 64px;
  height: 64px;
  border-radius: 0;
  border: 1px solid var(--rule);
  object-fit: cover;
}

.pointer-icon {
  width: 16px;
  height: 16px;
  align-self: flex-start;
  margin-top: 4px;
}

.name-pill {
  background: var(--text-primary);
  color: var(--text-white);
  font-size: 13px;
  font-weight: 500;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  align-self: flex-start;
  margin-top: 2px;
}

.hero-heading {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 2.9vw, 34px);
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  max-width: 720px;
  margin: 0;
}

/* ============ CASE STUDIES ============ */



.case-media {
  display: block;
  border-radius: var(--radius-md);
  overflow: hidden;
  line-height: 0;
  transition: opacity 0.2s ease;
}

a.case-media:hover { opacity: 0.88; }

.case-media video, .case-media img {
  width: 100%;
  display: block;
}

.case-media-cover {
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-dark);
}

.case-media-cover .cover-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(52px, 10vw, 128px);
  line-height: 1.0;
  color: var(--text-white);
  margin: 0;
  text-align: center;
  padding: 0 24px;
}

/* `baseline`, not `center`: the CTA sits on the caption's first line so it
   lands in the same place regardless of how many lines the caption runs to.
   Under `center` the offset tracked the caption height — 24px next to a
   two-line caption, 41px next to a three-line one — so the two case studies
   on the homepage never lined up with each other.
   `flex-start` would align the boxes rather than the text, leaving the CTA
   sitting slightly high against 26px display type with its taller leading. */
.case-caption-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding-top: 16px;
  flex-wrap: wrap;
}

.case-caption {
  text-wrap: pretty;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: clamp(17px, 1.8vw, 21px);
  line-height: 1.45;
  color: var(--text-primary);
  max-width: 620px;
  margin: 0;
}

.case-caption-row .thought-link,
.ca-cta-row .thought-link { flex-shrink: 0; }

.btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--text-primary);
  color: var(--text-white);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 16px;   /* the one CTA size — see TYPE-SYSTEM.md */
  padding: 14px 22px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

/* Hover darkens. It never changes hue — this rule was still purple after the
   colour audit because that pass only looked at text colours. */
.btn-primary:hover { background: var(--text-hover); }

/* ============ PLAYGROUND TEASER ============ */

.playground-media {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  transition: opacity 0.2s ease;
}

a.playground-media:hover { opacity: 0.88; }

.playground-media img { width: 100%; height: 100%; display: block; border-radius: var(--radius-md); object-fit: cover; }

/* ============ CASE ARCHIVE (accordion) ============ */



.case-archive-list {
  border-top: 1px solid var(--rule);
}

.case-archive-item {
  border-bottom: 1px solid var(--rule);
}

.case-archive-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 24px 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-sans);
}

.ca-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 18px;
  color: var(--text-primary);
}

/* Same treatment as .tile-meta — both are a small metadata line beside a
   title, and they were running at different sizes, cases and weights. */
.ca-meta {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-left: auto;
  margin-right: 24px;
  white-space: nowrap;
}

.ca-icon {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  transition: transform 0.25s ease;
}

.ca-icon::before, .ca-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--text-primary);
}

.ca-icon::before { width: 100%; height: 2px; transform: translate(-50%, -50%); }
.ca-icon::after { width: 2px; height: 100%; transform: translate(-50%, -50%); }

.case-archive-item.is-open .ca-icon { transform: rotate(45deg); }

.case-archive-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}

.case-archive-item.is-open .case-archive-panel {
  grid-template-rows: 1fr;
}

.case-archive-panel-inner { overflow: hidden; }

/* Content fades in slightly behind the height. Starts at full opacity and is
   only dimmed while closed, so if the transition is ever skipped the content
   still ends up visible rather than stuck at 0. */
.case-archive-panel-content {
  opacity: 0;
  transition: opacity 0.25s ease 0.05s;
}

.case-archive-item.is-open .case-archive-panel-content { opacity: 1; }

.case-archive-panel-content {
  padding: 0 0 40px;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 40px;
}

.ca-thumb {
  display: block;
  flex: 1 1 55%;
  border-radius: var(--radius-sm);
  overflow: hidden;
  line-height: 0;
  transition: opacity 0.2s ease;
}

a.ca-thumb:hover { opacity: 0.88; }

.ca-info {
  flex: 1 1 45%;
  min-width: 0;
  padding-top: 4px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.ca-info-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.3;
  color: var(--text-primary);
  margin: 0;
}

.ca-info p {
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-body);
  margin: 0;
}

.ca-thumb video { width: 100%; display: block; }

/* ============ HOMEPAGE INDEX SECTIONS ============ */

/* Alternating full-bleed tint. Sections carry `tint-band` to opt in; the
   section spans the viewport while an inner .wrap holds the content on the
   usual measure. The top margin keeps a band off whatever precedes it —
   without it the Playground band sat flush against the accordion's bottom rule
   and that stroke read as the band's own edge.
   NB: named `tint-band`, not `band` — `.band` is already the case-study media
   band (centred, its own background and padding) and the collision leaked
   text-align:center into these sections. */
.tint-band { background: var(--bg-band); }   /* spacing lives in the PAGE RHYTHM block */



/* Label stacked over its description on the left, CTA pinned right on the same
   baseline. The old three-column split was sized for a 28px serif heading; once
   that became a 12px label its column sat almost empty and pushed the
   description into the middle of the row with nothing to anchor it. */
.home-section-head {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "label cta"
    "desc  cta";
  column-gap: 40px;
  align-items: end;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--rule);
}

.home-section-head h2 { grid-area: label; margin: 0 0 6px; }
.home-section-head .thought-link { grid-area: cta; align-self: end; justify-self: end; white-space: nowrap; }

.home-section-head h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(22px, 2.4vw, 26px);
  line-height: 1.2;
  color: var(--text-primary);
  margin: 0;
}

.home-section-head p {
  grid-area: desc;
  text-wrap: pretty;   /* no single-word last line */
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-body);
  margin: 0;
  max-width: 52ch;
}



.home-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  padding-top: 32px;
}

.home-tiles.cols-1 { grid-template-columns: 1fr; }

.home-tile { display: block; }

.home-tile-media {
  border-radius: var(--radius-md);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--bg-dark);
  margin-bottom: 14px;
  transition: opacity 0.2s ease;
}

.home-tiles.cols-1 .home-tile-media { aspect-ratio: auto; }

.home-tile:hover .home-tile-media { opacity: 0.88; }

/* Stills sit in a fixed 4:3 tile and crop to fill. */
.home-tile-media img,
.home-tile-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  display: block;
}

/* Screen recordings are portrait phone captures, and a 4:3 crop reduced them to
   the top bezel. They also arrive at slightly different ratios (0.48–0.54), so
   letting each run at its own height left the row uneven. One shared ratio for
   the tile plus object-fit:contain gives every clip the same height with none
   of it cropped — the thin side/top bars fall on the band colour, so they read
   as breathing room rather than letterboxing. */
.home-tile-media.is-video {
  aspect-ratio: 0.52;
  background: none;
}

.home-tile-media.is-video video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.home-tiles.cols-1 .home-tile-media img,
.home-tiles.cols-1 .home-tile-media video { height: auto; }

.home-tile-media.is-cover {
  display: flex;
  align-items: center;
  justify-content: center;
}

.home-tile-media.is-cover p {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 3vw, 38px);
  color: var(--text-white);
  margin: 0;
}

.home-tile h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 18px;
  line-height: 1.3;
  color: var(--text-primary);
  margin: 0 0 5px;
}

/* One metadata style for the whole site: 12px is the readability floor, and
   both copies of this rule were sitting a pixel under it. */
/* `> span` matters: the descendant form also matched the .arrow span inside the
   CTA next to it, turning the arrow into a block and pushing it onto its own
   line. Only the direct child (the date/read-time) should take this style. */
.tile-meta,
.thought-meta > span,
.ed-kicker,
.ed-minihead,
.ed-body p.ed-minihead,
.ed-num,
.ed-stat-label,
.ed-artifact-label,
.ed-moment .step,
.ed-compare-card .tag,
.ed-meta-grid dt,
.ed-artifact-cols h5,
.ed-table th,
.sec-kicker,
.feature-head span,
.ca-num,
.cs-fig-num,
.cs-label {   /* out-specifies `.ed-body p`, which was winning at 16px */
  display: block;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 12px;
  /* 0.14em, up from 0.08em — the editorial pass. Wide-tracked caps are what
     separate a label voice from merely small text; measured 0.12–0.4em on
     every reference (Stone, Cereal-school sites, The Gentlewoman). */
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ---- the other two shared type rules for case studies ----
   Everything dense inside a block (list items, card copy, table cells,
   captions) is one style; every small heading inside a block is another.
   These were previously eleven near-identical declarations that drifted to
   sans-700 in four places, which broke the "headings are never sans" rule. */

.ed-numbered p,
.ed-moment p,
.ed-compare-card p,
.ed-compare-card li,
.ed-artifact-cols li,
.ed-meta-grid dd,
.ed-table,
.cs-caption,
.cs-figure figcaption {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-body);
}

.ed-numbered h4,
.ed-moment h4,
.ed-compare-card h4,
.ed-artifact h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.3;
  color: var(--text-primary);
}

/* ============ MASONRY GALLERY ============ */

/* Disclosure note under a section label. Deliberately kept — the one-description-
   per-page rule is about decorative copy, and this is an attribution statement
   that has to stay with the work. */
.section-note {
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 620px;
  margin: -8px 0 var(--space-el);
  padding-left: 14px;
  border-left: 2px solid rgba(0, 0, 0, 0.18);
}

.section-note strong { color: var(--text-primary); font-weight: 600; }

/* Project title + lead, sits under a section label like "Built" */
/* sits below its media, above the write-up */
.project-intro { margin: var(--space-block) 0 0; }

.project-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 3vw, 34px);
  line-height: 1.2;
  color: var(--text-primary);
  margin: 0 0 12px;
}

.project-lead {
  font-size: 18px;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 620px;
  margin: 0;
}

@media (max-width: 760px) {
  .project-lead { font-size: 16px; }
}

/* Pulse write-up under the Built section */
.pulse-notes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
  margin-top: var(--space-block);
  padding-top: var(--space-block);
  border-top: 1px solid var(--rule);
}

.pulse-note h3 {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 12px;
}

.pulse-note p {
  font-size: 16px;
  line-height: 1.65;
  color: var(--text-body);
  margin: 0;
}

@media (max-width: 860px) {
  .pulse-notes { grid-template-columns: 1fr; gap: 28px; }
}

/* Row-aligned gallery grid.
   Was CSS multicol (column-count), which had two problems: tiles flowed DOWN
   column 1 before starting column 2, so reading order never matched source
   order; and every column ended at a different height, so captions drifted out
   of line and read as a bug. A grid keeps the varied tile heights but pins the
   top of every tile in a row to the same line. */
.section-heading::before,
.home-section-head h2::before {
  content: "";
  width: 40px;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
  flex-shrink: 0;
}

.masonry {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px 20px;
  align-items: start;
  margin-top: 0;
}

.masonry.cols-2 { grid-template-columns: repeat(2, 1fr); }
.masonry.cols-1 { grid-template-columns: 1fr; }

/* Packed state — only ever set by the script. Until then (and forever, if the
   script fails or is blocked) the rule above is still a perfectly good grid:
   nothing is hidden, nothing overlaps, the wide tiles just sit in ragged rows
   the way they do today. */
@media (max-width: 560px) {
  /* One column: a 4:3 desktop screen at half of a 375px viewport is unreadable,
     so the wide tiles stop spanning and everything goes full width. */
    }

.masonry-item {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  margin: 0;
  display: block;
  width: 100%;
}

.masonry-item img,
.masonry-item video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  /* every gallery tile opens the lightbox, so it has to read as clickable */
  cursor: pointer;
}

/* Playground's screens are mostly tall app captures — anchoring the crop to
   the top keeps the header/nav of each screen visible instead of slicing
   through the middle of the UI. Craft stays centred. */
.masonry.align-top .masonry-item img,
.masonry.align-top .masonry-item video {
  object-position: top;
  display: block;
  background: var(--surface);
}

.masonry-item video { background: var(--bg-dark); }

/* Gallery tiles show the whole image — no crop, no height ceiling. An earlier
   version capped them at 1.25x their own width with object-fit:cover to even
   out the rows, but that sliced the bottom off anything tall and made half the
   gallery unreadable. Row tops still align because the grid uses
   align-items:start; tiles just differ in height, which is the mosaic. */

/* ============ LIGHTBOX ============ */

.lb {
  position: fixed;
  inset: 0;
  z-index: 2000;
  /* neutral ink — this carried the old violet-navy #0b0619 and read blue */
  background: rgba(18, 18, 16, 0.97);
  display: none;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.lb.is-open { display: flex; opacity: 1; }

.lb img,
.lb video {
  max-width: 92vw;
  max-height: 86vh;
  object-fit: contain;
  border-radius: var(--radius-sm);
  display: block;
}
.lb img[hidden], .lb video[hidden] { display: none; }

.lb-btn {
  position: absolute;
  background: rgba(255,255,255,0.1);
  border: 0;
  color: #fff;
  width: 44px;
  height: 44px;
  border-radius: 0;
  cursor: pointer;
  font-size: 20px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s ease;
}

.lb-btn:hover { background: rgba(255,255,255,0.22); }

.lb-close { top: 24px; right: 24px; }
.lb-prev { left: 24px; top: 50%; transform: translateY(-50%); }
.lb-next { right: 24px; top: 50%; transform: translateY(-50%); }

.lb-count {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.08em;
  color: rgba(255,255,255,0.6);
}

body.lb-open { overflow: hidden; }

/* ============ CRAFT ============ */

/* Galleries need more air between them than a normal section break — each one
   ends on a ragged row of images, so 72px read as a continuation rather than a
   division. Steps up to the largest rhythm value. */
/* When a gallery section also carries .tint-band, the band supplies the padding
   — otherwise the two stack and the section floats in the middle of the fill. */



.craft-section .section-body { margin: 0 0 28px; }

.craft-grid {
  border-radius: var(--radius-md);
  width: 100%;
  display: block;
  margin-bottom: 24px;
}


.section-heading,
.home-section-head h2 {
  /* every section heading carries the editorial leading rule — one treatment,
     site-wide, identical to .sec-kicker on the homepage */
  display: flex;
  align-items: center;
  gap: 18px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--text-muted);
  max-width: none;
  margin: 0 0 var(--space-el);
}

.section-body {
  max-width: 620px;
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-body);
}

/* ============ THOUGHTS ============ */

/* spacing comes from .band on the homepage; standalone pages keep their own */


.thoughts .section-heading { margin-bottom: 32px; }

.thoughts-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
}

.thought-card { display: flex; flex-direction: column; }

.thought-media {
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: 20px;
  border: 1px solid rgba(0,0,0,0.06);
}

/* Item title, medium. Was Plus Jakarta Sans 700 — the only item title on the
   homepage not set in the display serif. */
.thought-card h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.3;
  margin: 0 0 10px;
  color: var(--text-primary);
  text-wrap: pretty;
}

.thought-card h3 a:hover { color: var(--text-hover); }

.thought-card > p {
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-body);
  margin: 0 0 20px;
}

.thought-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 16px;
  font-size: 13px;
  margin-top: auto;
}

.thought-meta span { color: var(--text-muted); }

/* The single CTA style. One size, one weight, one colour, everywhere on the
   site — it was previously rendering at 13px, 15px and 16px depending on where
   it landed. Hover darkens; it never changes hue. */
.thought-link {
  /* same recipe as the resume page's View-the-PDF button */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-primary);
  padding: 13px 20px;
  border: 1px solid rgba(0, 0, 0, 0.18);
  white-space: nowrap;
}

.thought-link:hover {
  background: var(--text-primary);
  color: var(--bg-cream);
  border-color: var(--text-primary);
}

.dark-mode .thought-link { border-color: rgba(255, 255, 255, 0.22); }
.dark-mode .thought-link:hover { background: var(--text-primary); color: var(--bg-dark); }

/* flex-column parents stretch children; a boxed CTA must hug its label */
.ca-info .thought-link { align-self: flex-start; }

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

/* The stroke container, from the "La edición" reference: a hairline box
   holding spec-sheet rows, with the wordmark straddling the top border the
   way their headings sit half-in, half-out of the frame. The breach works
   like a fieldset legend — ink background on the mark hides the border
   behind it. */
/* ============ RESPONSIVE ============ */

@media (max-width: 760px) {
  .nav-links { display: none; }
  .nav-toggle { display: flex; }
  .logo img { width: 138px; }
  .playground-media { grid-template-columns: 1fr; }
  .thoughts-grid { grid-template-columns: 1fr; }
      .case-caption-row { align-items: flex-start; }
  /* The row was `flex-wrap: wrap` with `order:1` on the meta, so where it broke
     depended on how long each title was — every row landed differently. An
     explicit grid pins it: title and meta stack on the left, the +/- stays put
     on the right across both rows. */
  .case-archive-toggle {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-areas: "title icon" "meta  icon";
    column-gap: 16px;
    row-gap: 4px;
    align-items: start;
    padding: 18px 0;
  }
  .ca-title { grid-area: title; line-height: 1.25; }
  .ca-meta  { grid-area: meta; margin: 0; white-space: normal; }
  .ca-icon  { grid-area: icon; align-self: center; }
  .case-archive-panel-content { flex-direction: column; }
  .home-section-head { grid-template-columns: 1fr;
    grid-template-areas: "label" "desc" "cta"; row-gap: 10px; }
  .home-section-head .thought-link { justify-self: start; margin-top: 4px; }
  .home-tiles { grid-template-columns: 1fr; gap: 32px; }
  .masonry, .masonry.cols-2 { grid-template-columns: repeat(2, 1fr); }
  .masonry.cols-1 { grid-template-columns: 1fr; }
  .lb-prev { left: 10px; }
  .lb-next { right: 10px; }
  .lb-close { top: 14px; right: 14px; }
}

/* 4 columns holds at every desktop/tablet width; only phones drop to 2 */

/* ============================================
   CASE STUDY PAGES  —  OUTSIDE THE TYPE SYSTEM
   These two pages (/tfc, /esignatures) keep their own colours (#000, #494949,
   #797979) and sizes because they are a deliberate 1:1 reproduction of the live
   Webflow site. Do not fold them into the tokens in TYPE-SYSTEM.md without
   agreeing to lose that fidelity.
   Type scale, colours and layout measured from
   the live Webflow case studies (/tfc, /esignatures).
   ============================================ */

:root {
  --cs-text: #333333;
  --cs-text-2: #494949;
  --cs-text-3: #797979;
  --cs-band: #eeece9;
  --cs-tag: #f3d7d7;
  --cs-mark: #f2dcff;
  --cs-note-bg: #fff4cf;
  --cs-note-border: #ffcd29;
  --cs-rule: rgba(0, 0, 0, 0.12);
}

.cs-wrap {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 47px;
}

/* ---- two column grid: 606 / 67 gap / 673 ---- */

.cs-cols {
  display: grid;
  grid-template-columns: 45% 50%;
  justify-content: space-between;
  align-items: start;
  gap: 40px 5%;
}

/* ---- type ---- */

.cs-title,
.cs-h {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 2.6vw, 32px);
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: #000;
  margin: 0;
}

.cs-h-lg {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(38px, 4.6vw, 64px);
  line-height: 1.1;
  letter-spacing: 0.01em;
  color: #000;
  margin: 0;
}

/* the big statement headings sit centred in the left column */
.cs-h-lg.centred { max-width: 340px; margin: 0 auto; }

.cs-body { font-size: 16px; line-height: 1.3; color: var(--cs-text-2); }
.cs-body p { margin: 0 0 16px; }
.cs-body p:last-child { margin-bottom: 0; }
.cs-body strong { font-weight: 600; color: var(--cs-text); }

mark { background: var(--cs-mark); color: inherit; padding: 0 1px; }

/* ---- page header ---- */

.cs-header { padding-top: 40px; }

.cs-title { margin-bottom: 18px; }

.cs-headrow {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 48px;
}

.cs-intro {
  font-size: 16px;
  line-height: 1.3;
  color: var(--cs-text-2);
  max-width: 880px;
  margin: 0;
}

.cs-role {
  font-size: 16px;
  line-height: 1.3;
  color: var(--cs-text-3);
  white-space: nowrap;
}

.cs-hero-media {
  margin-top: 26px;
  border-radius: 0;
  overflow: hidden;
  line-height: 0;
}

.cs-hero-media video, .cs-hero-media img { width: 100%; display: block; }

/* ---- sections ---- */

.cs-section { padding-top: 90px; }

/* ---- impact stats ---- */

.impact { padding-top: 90px; }

.stat-list { padding-top: 60px; display: grid; gap: 10px; max-width: 505px; }

.stat-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
}

.stat-value {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(32px, 3.4vw, 48px);
  line-height: 1.1;
  color: #000;
}

.stat-label {
  font-size: 16px;
  line-height: 1.3;
  color: var(--cs-text-2);
  text-align: right;
}

.stat-footnote {
  font-size: 12.8px;
  line-height: 1.25;
  color: var(--cs-text-3);
  margin: 26px 0 0;
  max-width: 380px;
}

/* ---- fact rows (timeframe / context / problem / solution) ---- */

.fact-row {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 28px;
  padding: 26px 0;
  border-top: 1px solid var(--cs-rule);
}

.fact-row:last-child { border-bottom: 1px solid var(--cs-rule); }

.fact-label {
  font-size: 16px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--cs-text-2);
}

.fact-content p {
  font-size: 16px;
  line-height: 1.3;
  color: var(--cs-text-2);
  margin: 0 0 16px;
}

.fact-content p:last-child { margin-bottom: 0; }

/* ---- table of contents ---- */

.toc-list { list-style: none; margin: 26px 0 0; padding: 0; max-width: 428px; }

.toc-list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 15px 0;
  border-bottom: 1px solid var(--cs-rule);
  font-size: 16px;
  color: var(--cs-text);
}

.toc-list li:first-child a { border-top: 1px solid var(--cs-rule); }
.toc-list a:hover { color: #000; }

.toc-aside img { width: 100%; max-width: 367px; margin-left: auto; }

/* ---- quote stack ---- */

.quote-stack {
  display: grid;
  gap: 26px;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(20px, 2vw, 26px);
  line-height: 1.3;
  color: #000;
  margin: 0;
}

/* ---- feedback tag grid ---- */

.tag-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 12px;
  margin-top: 50px;
}

.tag-pill {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--cs-tag);
  border-radius: 0;
  padding: 11px 18px;
  font-size: 13px;
  line-height: 1.3;
  color: var(--cs-text);
}

.tag-pill img { width: 14px; height: 14px; flex-shrink: 0; }

/* ---- media bands ---- */

.band {
  background: var(--cs-band);
  border-radius: 0;
  padding: 40px 0 20px;
  text-align: center;
  margin-top: 40px;
}

.band .inner { padding: 0 40px; }
.band img, .band video { max-width: 100%; margin: 0 auto; display: block; }

/* rendered widths measured from the live case studies */
.band img.device { width: 399px; max-width: 100%; }
.band img.wire { width: 532px; max-width: 100%; }
.band video.wire { width: 665px; max-width: 100%; }
.band .w399 { width: 399px; max-width: 100%; }
.band .w532 { width: 532px; max-width: 100%; }
.band .w665 { width: 665px; max-width: 100%; }
.band .w808 { width: 808px; max-width: 100%; }
.band .w1077 { width: 1077px; max-width: 100%; }

.band-label {
  font-size: 16px;
  line-height: 1.3;
  color: var(--cs-text);
  margin: 20px 0 0;
}

.band-row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 40px; }
.band-row .band { margin-top: 0; }

.band-caption {
  font-size: 16px;
  line-height: 1.3;
  color: var(--cs-text);
  margin: 20px 0 0;
  max-width: 606px;
}

/* ---- yellow note ---- */

.note {
  background: var(--cs-note-bg);
  border: 6px solid var(--cs-note-border);
  border-radius: 0;
  padding: 20px 32px 20px 40px;
  text-align: left;
}

.note h3 {
  font-family: var(--font-sans);
  font-size: clamp(24px, 2.4vw, 32px);
  font-weight: 700;
  line-height: 1.2;
  color: #000;
  margin: 0 0 14px;
}

.note p { font-size: 16px; line-height: 1.3; color: var(--cs-text); margin: 0 0 14px; }
.note p:last-child { margin-bottom: 0; }

/* ---- white card ---- */

.card { background: #fff; border-radius: 0; padding: 28px 34px; text-align: left; }
.card ul { margin: 0; padding-left: 20px; }
.card li { font-size: 16px; line-height: 1.4; color: var(--cs-text); padding-left: 16px; margin-bottom: 9px; }
.card li:last-child { margin-bottom: 0; }
.card li strong { font-weight: 600; }

/* ---- usability results ---- */

.result { display: grid; grid-template-columns: auto 1fr; gap: 18px; align-items: end; margin-bottom: 22px; }
.result-name { font-size: 16px; line-height: 1.3; color: var(--cs-text); margin: 0 0 2px; }

.result-value {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(30px, 4.4vw, 48px);
  line-height: 1.05;
  color: #000;
}

.result-note { font-size: 14px; line-height: 1.3; color: var(--cs-text); padding-bottom: 34px; }
.result-footnote { font-size: 12.8px; line-height: 1.35; color: var(--cs-text); margin: 18px 0 0; }

/* ============ RESPONSIVE (case study) ============ */

@media (max-width: 1100px) {
  .tag-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 860px) {
  .cs-wrap { padding: 0 24px; }
  .cs-cols { grid-template-columns: 1fr; }
  .cs-headrow { flex-direction: column; gap: 14px; }
  .cs-role { text-align: left; white-space: normal; }
  .cs-h-lg.centred { margin: 0; max-width: none; }
  .tag-grid { grid-template-columns: repeat(2, 1fr); }
  .band-row { grid-template-columns: 1fr; }
  .fact-row { grid-template-columns: 1fr; gap: 10px; }
  .stat-row { justify-content: flex-start; gap: 16px; }
  .stat-label { text-align: left; }
  .toc-aside img { margin: 0; }
}

/* ============ SITE PAGES (about / photo / playground / thoughts) ============ */

/* Homepage intro. Doubles as the page's only h1 — the homepage previously had
   none at all. Deliberately short and image-free: it states who and what, then
   gets out of the way of the work. */


.home-intro h1 {
  text-wrap: pretty;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(24px, 2.9vw, 34px);
  line-height: 1.32;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  max-width: 30ch;
  margin: 0 0 16px;
}

.home-intro p {
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 54ch;
  margin: 0;
}

@media (max-width: 760px) {
  .nav { padding: 18px 0; }
  .home-intro,
  .page-hero,
  .ed-hero,
  .post-header,
  .about-section:first-child { padding-top: 32px; }
  .home-intro { padding-top: 32px; }
  .home-intro h1 { max-width: none; }
  .home-intro p { font-size: 16px; }
}



.page-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(38px, 5.5vw, 84px);
  line-height: 1.02;
  letter-spacing: -0.015em;
  color: var(--text-primary);
  margin: 0 0 24px;
  max-width: 20ch;
  text-wrap: pretty;
}

.page-intro {
  text-wrap: pretty;
  font-size: 18px;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 620px;
  margin: 0;
}

/* ---- work archive ---- */

.work-list {
  display: flex;
  flex-direction: column;
  margin-top: 24px;
  border-top: 1px solid var(--rule);
}

.work-row {
  display: grid;
  grid-template-columns: 240px 1fr auto;
  gap: 32px;
  align-items: center;
  padding: 32px 0;
  border-bottom: 1px solid var(--rule);
}

.work-thumb {
  border-radius: var(--radius-sm);
  overflow: hidden;
  aspect-ratio: 16 / 9;
  line-height: 0;
  background: var(--bg-dark);
}

.work-thumb video, .work-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.work-thumb.is-cover { display: flex; align-items: center; justify-content: center; }

.work-thumb.is-cover p {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 18px;
  color: var(--text-white);
  margin: 0;
  padding: 0 12px;
  text-align: center;
}

.work-info h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(19px, 2vw, 23px);
  color: var(--text-primary);
  margin: 0 0 8px;
  transition: color 0.15s ease;
}

.work-info p { font-size: 14px; line-height: 1.55; color: var(--text-body); margin: 0; max-width: 480px; }

.work-meta {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}

.work-row:hover .work-info h3 { color: var(--text-hover); }

/* ---- about ---- */



.about-bio {
  display: grid;
  grid-template-columns: 340px 1fr;
  gap: 88px;
  align-items: start;
}

.about-bio-title {
  font-family: var(--font-display);
  font-weight: 500;
  /* same step as .page-title — About was the one page whose title sat two
     sizes down from every other hero */
  font-size: clamp(38px, 5.5vw, 84px);
  line-height: 1.02;
  letter-spacing: -0.015em;
  color: var(--text-primary);
  margin: 0 0 24px;
  text-wrap: pretty;
}

.about-bio-copy p {
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-body);
  margin: 0 0 20px;
}

.about-bio-copy p:last-child { margin-bottom: 0; }

.photo-grid-2col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.photo-grid-2col img {
  border-radius: var(--radius-md);
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  display: block;
  background: var(--surface);
  transition: opacity 0.2s ease;
}

.photo-grid-2col img:hover { opacity: 0.88; }

.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: start;
}


/* L3. Not an L2 label: this is a section heading with voice ("Don't just take
   my word..."), not a category divider, and setting it in 12px uppercase read
   as a system label rather than a sentence. Sized well under the page title so
   the two never compete. */
/* ---- photo / playground grids ---- */

.media-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  padding-top: 48px;
}

.media-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }

.media-tile { border-radius: var(--radius-md); overflow: hidden; background: var(--surface); }
.media-tile img { width: 100%; display: block; }
.media-tile.tall { grid-row: span 2; }

.media-card { background: none; border: 0; }

.media-card img,
.media-card video,
.media-card .case-media-cover {
  width: 100%;
  display: block;
  border-radius: var(--radius-md);
}

.media-card-body { padding: 20px 0 0; }

.media-card h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(19px, 2vw, 23px);
  line-height: 1.25;
  color: var(--text-primary);
  margin: 0 0 10px;
}

.media-card p { font-size: 14px; line-height: 1.6; color: var(--text-body); margin: 0; max-width: 46ch; }

.coming-soon-tile {
  border-radius: var(--radius-md);
  border: 1px dashed rgba(0,0,0,0.2);
  background: #f1efec;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 220px;
  text-align: center;
  padding: 24px;
}

.coming-soon-tile p { font-size: 14px; color: var(--text-muted); margin: 0; max-width: 260px; }

/* ---- blog post template ---- */

/* ---- Article pages: single centred reading column ---- */
.post-header,
.post-cover,
.post-body,
.post-footer { max-width: 740px; margin-left: auto; margin-right: auto; }



/* Articles sit on the same hero step as every other page — this carried a
   fixed 48px from the old Medium-style pass and was the last title off-scale. */
.post-header .page-title {
  line-height: 1.06;
  margin: 0 0 20px;
}

.post-subtitle {
  font-family: var(--font-sans);
  font-size: 18px;
  line-height: 1.5;
  color: var(--text-muted);
  margin: 0 0 32px;
}

/* byline: author, date, read time */
.post-byline {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 20px 0;
  border-top: 1px solid var(--rule-soft);
  border-bottom: 1px solid var(--rule-soft);
  margin-bottom: 40px;
}

.post-byline .byline-name {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}

.post-meta {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-muted);
  margin: 0;
}

.post-meta span + span::before { content: '\00b7'; margin: 0 8px; }

.post-cover { border-radius: var(--radius-md); overflow: hidden; margin: 0 auto 12px; }
.post-cover img { width: 100%; display: block; }

.post-caption {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-muted);
  text-align: center;
  margin: 0 auto 48px;
  max-width: 740px;
}

.post-body {
  margin-top: 40px;
  font-family: var(--font-serif-body);
  font-size: 18px;
  line-height: 1.7;
  color: var(--text-body);
  letter-spacing: -0.003em;
}

.post-body p { margin: 0 0 30px; }

.post-body > p:first-of-type { font-size: 20px; line-height: 1.6; }

.post-body ul { margin: 0 0 30px; padding-left: 22px; }
.post-body li { margin-bottom: 12px; }
.post-body li::marker { color: var(--text-muted); }

.post-body h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 26px;
  line-height: 1.3;
  color: var(--text-primary);
  margin: 52px 0 16px;
}

.post-body blockquote {
  margin: 36px 0;
  padding-left: 24px;
  border-left: 3px solid var(--text-primary);
  font-style: italic;
  color: var(--text-primary);
}

.post-body a { text-decoration: underline; text-underline-offset: 3px; }

/* end-of-article divider + back link */
.post-footer {
  margin-top: var(--space-section);
  padding-top: 32px;
  border-top: 1px solid var(--rule-soft);
}

@media (max-width: 760px) {
  .post-header { padding-top: 40px; }
  .post-header .page-title { font-size: clamp(30px, 8.4vw, 38px); }
  .post-subtitle { font-size: 18px; }
  .post-body { font-size: 16px; }
  .post-body > p:first-of-type { font-size: 18px; }
  .post-body h2 { font-size: 24px; margin-top: 40px; }
}

.draft-flag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: #fff4cf;
  border: 1px solid #ffcd29;
  border-radius: var(--radius-sm);
  padding: 10px 16px;
  font-size: 13px;
  color: #6b5900;
  margin-bottom: 32px;
}

/* ============ RESPONSIVE (site pages) ============ */

@media (max-width: 760px) {
  .two-col { grid-template-columns: 1fr; gap: 32px; }
  .about-bio { grid-template-columns: 1fr; gap: 28px; }
  .photo-grid-2col { grid-template-columns: 1fr; }
    .media-grid, .media-grid.cols-2 { grid-template-columns: 1fr; }
  .media-tile.tall { grid-row: auto; }
  .work-row { grid-template-columns: 1fr; gap: 16px; }
  .work-meta { order: -1; }
}

/* ============================================
   EDITORIAL CASE STUDY (Factor+, RAF, etc.)
   Sticky-scroll chapter pattern: a flex row per
   section, with a short sticky label column and a
   taller scrolling content column. Deliberately
   separate from both the homepage system and the
   tactical cs-* system used by /tfc, /esignatures.
   ============================================ */

.ed-body-bg { background: var(--bg-cream); }



.ed-kicker {
  display: block;
  margin-bottom: 20px;
}

.ed-hero-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(48px, 9vw, 104px);
  line-height: 0.98;
  color: var(--text-primary);
  margin: 0 0 28px;
}

.ed-hero-sub {
  font-size: 18px;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 680px;
  margin: 0;   /* section padding supplies the gap below */
}

.ed-meta-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  padding: 32px 0;
  border-top: 1px solid rgba(0,0,0,0.12);
  border-bottom: 1px solid rgba(0,0,0,0.12);
}

.ed-meta-grid dt { margin: 0 0 8px; }
.ed-meta-grid dd { color: var(--text-primary); font-weight: 500; margin: 0; }

.ed-hero-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  padding-top: 56px;
}

.ed-stat-value {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(32px, 4vw, 48px);
  color: var(--text-primary);
  margin: 0 0 8px;
  line-height: 1;
}

.ed-stat-label {
  line-height: 1.4;
  margin: 0;
  max-width: 22ch;
}

/* ---- chapter rows ---- */

.ed-chapter { border-top: 1px solid var(--rule); }   /* spacing from PAGE RHYTHM */

.ed-row {
  display: flex;
  gap: 64px;
  align-items: flex-start;
}

.ed-label {
  flex: 0 0 200px;
  position: sticky;
  top: 48px;
}

.ed-label .ed-num {
  display: block;
}

.ed-content { flex: 1; min-width: 0; max-width: 760px; }

.ed-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 2.9vw, 34px);
  line-height: 1.15;
  color: var(--text-primary);
  margin: 0 0 28px;
}

.ed-body p {
  font-size: 16px;
  line-height: 1.7;
  color: var(--text-body);
  margin: 0 0 22px;
}

.ed-body p:last-child { margin-bottom: 0; }
.ed-body strong { color: var(--text-primary); font-weight: 600; }

.ed-minihead {
  margin: 36px 0 10px;
}

.ed-body .ed-minihead:first-child { margin-top: 0; }

.ed-pull-quote {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(22px, 2.4vw, 26px);
  line-height: 1.4;
  color: var(--text-primary);
  margin: 40px 0;
  padding-left: 24px;
  border-left: 3px solid rgba(0,0,0,0.15);
}

/* ---- numbered list (principles / questions / steps) ---- */

.ed-numbered {
  list-style: none;
  margin: 32px 0;
  padding: 0;
  display: grid;
  gap: 28px;
}

.ed-numbered li { display: flex; gap: 20px; }

.ed-numbered .n {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  color: var(--text-muted);
  flex-shrink: 0;
  width: 28px;
}

.ed-numbered h4 { margin: 0 0 6px; }

.ed-numbered p { margin: 0; }

/* ---- lifecycle / moment grid ---- */

.ed-moment-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  margin: 32px 0;
}

.ed-moment {
  border-top: 2px solid var(--text-primary);
  padding-top: 14px;
}

.ed-moment .step { margin-bottom: 6px; }
.ed-moment h4 { margin: 0 0 8px; }
.ed-moment p { margin: 0; }

/* ---- comparison cards (Option A/B, Control/Variant) ---- */

.ed-compare {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin: 32px 0;
}

.ed-compare.cols-3 { grid-template-columns: repeat(3, 1fr); }

.ed-compare-card {
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  padding: 24px 26px;
}

.ed-compare-card .tag { margin-bottom: 10px; }
.ed-compare-card h4 { margin: 0 0 10px; }
.ed-compare-card p { margin: 0; }
.ed-compare-card.is-recommended { border-color: var(--text-primary); }

.ed-compare-card ul { list-style: none; margin: 0; padding: 0; }
.ed-compare-card li {
  padding: 9px 0;
  border-top: 1px solid var(--rule-soft);
}
.ed-compare-card li:first-child { border-top: 0; padding-top: 0; }

/* ---- strategy artifact panel ---- */

.ed-artifact {
  background: #fff;
  border: 1px solid rgba(0,0,0,0.12);
  border-radius: var(--radius-md);
  padding: 32px 36px;
  margin: 40px 0;
}

.ed-artifact-label {
  display: block;
  margin-bottom: 10px;
}

.ed-artifact h3 { margin: 0 0 24px; }

.ed-artifact-cols {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  padding-bottom: 24px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--rule);
}

.ed-artifact-cols h5 { margin: 0 0 10px; }

.ed-artifact-cols ul { margin: 0; padding: 0; list-style: none; }
.ed-artifact-cols li { line-height: 1.7; }

/* ---- stat rows (results) ---- */

.ed-stat-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  margin: 32px 0;
}

.ed-stat-row.cols-5 { grid-template-columns: repeat(5, 1fr); }

/* ---- data table ---- */

.ed-table { width: 100%; border-collapse: collapse; margin: 32px 0; }
.ed-table th, .ed-table td { text-align: left; padding: 14px 16px; border-bottom: 1px solid var(--rule); }

.ed-table td { color: var(--text-body); }
/* first-column emphasis applies to data cells only — it was also recolouring
   the uppercase header label away from the shared label style. */
.ed-table td:first-child { font-weight: 500; color: var(--text-primary); }
.ed-table-wrap { overflow-x: auto; }

/* ---- closing ---- */

.ed-closing {
  padding-top: 96px;
  padding-bottom: 96px;
  border-top: 1px solid var(--rule);
}

.ed-closing-quote {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(26px, 2.9vw, 34px);
  line-height: 1.25;
  color: var(--text-primary);
  max-width: 820px;
  margin: 0;
}

.ed-cta-row {
  margin-top: 56px;
  padding-top: 40px;
  border-top: 1px solid var(--rule);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.ed-cta-row p {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  color: var(--text-primary);
  max-width: 460px;
  margin: 0;
}

.ed-cta-row .btn-primary { flex-shrink: 0; }

/* ============ RESPONSIVE (editorial) ============ */

@media (max-width: 900px) {
  /* align-items:flex-start carries over from the desktop row; in a column flex
     container that makes children size to their CONTENT width, so the results
     table stretched .ed-content to 503px inside a 375px screen. Stretch instead
     so children fill the column and the table scrolls in its own wrapper. */
  .ed-row { flex-direction: column; gap: 24px; align-items: stretch; }
  .ed-label { position: static; flex: none; }
  .ed-meta-grid, .ed-hero-stats { grid-template-columns: repeat(2, 1fr); }
  .ed-moment-grid, .ed-compare, .ed-compare.cols-3, .ed-artifact-cols { grid-template-columns: 1fr; }
  .ed-stat-row, .ed-stat-row.cols-5 { grid-template-columns: repeat(2, 1fr); }
}

/* ============ ACCESSIBILITY ============ */

a:focus-visible, .btn-primary:focus-visible {
  outline: 2px solid var(--text-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .arrow { transition: none; }
  .nav-overlay, .nav-toggle span { transition: none; }
}

body.nav-open { overflow: hidden; }

/* ============================================
   SCROLL REVEALS
   Driven by IntersectionObserver, not `animation-timeline: view()`. That CSS
   feature is unsupported in Firefox and only landed in Safari 26, so on those
   browsers the effect simply never ran.

   The hidden state is scoped to `html.js-reveal`, a class set by theme.js in
   <head>. If the script never runs, the class is never added and every element
   stays visible — the animation can't strand content the way a CSS-only hidden
   state could. There is also a failsafe timer in nav.js.
   ============================================ */

@media (prefers-reduced-motion: no-preference) {
  .js-reveal .reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
                transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: opacity, transform;
  }

  /* dense grids move less, so a row of tiles doesn't read as a wave */
  .js-reveal .masonry-item.reveal,
  .js-reveal .home-tile.reveal,
  .js-reveal .pulse-note.reveal { transform: translateY(10px); }

  .js-reveal .reveal.is-visible {
    opacity: 1;
    transform: none;
  }
}

/* ============================================
   DARK MODE
   Theme is a token swap — every surface, rule and text colour above resolves
   through a variable, so this block is the whole theme rather than a parallel
   set of component rules. `.dark-mode` is set on <html> by theme.js before
   first paint. Cool blue-grey darks — the channels run B > G > R, the inverse
   of the warm cream, rather than the brown-greys this started as.
   ============================================ */

.dark-mode {
  /* Neutral ink and off-white. The one cool surface on the site is the
     Elsewhere band, scoped separately below — the page ground itself stays
     neutral so it never reads blue. */
  --bg-cream: #151514;
  --bg-band: #1b1b1a;
  --surface: #252524;
  --text-primary: #f1f1ee;
  --text-body: #cbccc6;
  --text-muted: #90918b;
  --text-hover: #ffffff;
  --rule: rgba(255,255,255,0.14);
  --rule-soft: rgba(255,255,255,0.09);
  --veil: rgba(255,255,255,0.10);
  --veil-strong: rgba(255,255,255,0.20);
  --veil-border: rgba(255,255,255,0.18);
  --cs-text: #ecece8;
  --cs-text-2: #c2c3bd;
  --cs-text-3: #90918b;
  --cs-band: #252524;
  --cs-rule: rgba(255,255,255,0.14);
}

/* ---- Elsewhere band: the one cool surface ----
   Scoped token overrides, so every descendant (kicker, rules, list titles,
   metadata) shifts with it and nothing else on the page moves. */
.home-index.tint-band {
  /* Same colour family as the page, a step off it — a change of tone, not of
     hue. The cool blue-grey tried here first fought the neutral ground on
     either side and read as a mismatched insert. */
  --bg-band: #e6e2dd;
  --surface: #dbd6cf;
}

.dark-mode .home-index.tint-band {
  --bg-band: #181816;
  --surface: #201f1d;
}

.dark-mode body { background: var(--bg-cream); }

/* The wordmark is a dark PNG — swap to the white one. */
.dark-mode .nav .logo img {
  content: url('https://cdn.prod.website-files.com/667c2ea7460262b0b32b6add/67b0da14fbeb23da169d5c42_jmlogo-white.png');
}

/* Footer is already dark; lift it off the page so it still reads as a band. */

/* Highlight swatches from the case studies are light-mode pastels. */
.dark-mode mark { background: #3c3560; color: var(--text-primary); }
.dark-mode .tag-pill { background: #3e3140; color: var(--text-primary); }
.dark-mode .note-block { background: #2f3142; border-color: #4d5675; color: var(--text-primary); }

/* ============================================
   SITE FOOTER — THE OKUZUKE
   Every Japanese book ends on a colophon sheet: a ruled box of cells holding
   the title, the maker, the date, the seal. This footer is that page. One
   bordered grid, 1px gaps painted by the rule colour so every cell edge is a
   crisp hairline; the real wordmark, the invitation, navigation, a spec sheet,
   a vermilion action block, and vertical text up the right edge. Ink in both
   themes via local token overrides.
   ============================================ */
.site-footer {
  /* Inverts against the page: ink under a cream page, paper under an ink one
     (see .dark-mode .site-footer). Local token overrides mean every
     descendant — kicker, rules, links, spine, folio — follows automatically. */
  --fx-surface: var(--bg-dark);
  background: var(--fx-surface);
  --text-primary: #f4f4f0;
  --text-body: #d6d7d1;
  --text-muted: #9c9d96;
  --text-hover: #ffffff;
  /* one stroke weight in the sheet — every internal line, the cell gaps, and
     the outer border share this exact value */
  --rule: rgba(255, 255, 255, 0.24);
  --rule-soft: rgba(255, 255, 255, 0.24);
  color: var(--text-body);
  padding: var(--space-page) 0 0;
  /* clips the overshooting label rules at the viewport edge */
  overflow: hidden;
}

.fx-bleed { width: 100%; }

.fx {
  display: grid;
  grid-template-columns: 1.45fr 1fr 64px;
  grid-template-areas:
    "hello hello spine"
    "nav   social spine"
    "spec  spec  spine"
    "folio cta   spine";
  gap: 1px;
  background: var(--rule);
  border-top: 1px solid var(--rule);
  border-bottom: 0;   /* the page edge closes the box */
}

.fx-cell { background: var(--fx-surface); padding: 30px var(--wrap-gutter); }

/* The "LAS MUESTRAS" device from the reference: label, then a rule that runs
   on past the text and ends in a small square terminal. The rules overshoot
   their cells on purpose — crossing the gap into the neighbouring column is
   the imperfection that makes the sheet feel set by hand, not by grid. */
.fx-label {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 18px;
  position: relative;
  z-index: 1;
}

.fx-label::before { content: "\25B7"; color: var(--text-muted); font-size: 9px; }

.fx-label::after {
  content: "";
  flex: 1;
  height: 5px;
  background:
    linear-gradient(var(--rule), var(--rule)) 0 50% / calc(100% - 11px) 1px no-repeat,
    linear-gradient(var(--text-muted), var(--text-muted)) 100% 50% / 5px 5px no-repeat;
}

/* the overshoots — each rule runs further than its cell */
.fx-hello .fx-label  { margin-right: calc(-1 * var(--wrap-gutter) - 64px); }  /* crosses the spine */
.fx-nav .fx-label    { margin-right: calc(-1 * var(--wrap-gutter) - 18px); }  /* pokes into Connect */
.fx-social .fx-label { margin-right: calc(-1 * var(--wrap-gutter) + 8px); }   /* runs to its cell edge */

/* — the invitation, with the real wordmark — */
.fx-hello { grid-area: hello; padding: 44px var(--wrap-gutter) 40px; }

.fx-logo { width: 190px; height: auto; display: block; margin-bottom: 28px; }

/* Dark page -> light footer. The markup ships the white wordmark, so the dark
   one is swapped in here the same way the nav swaps its logo. */
.dark-mode .site-footer {
  /* Not the page cream (#f7f4f2) — that reads as a slab of white against the
     ink page. A half-step down sits as paper rather than a lightbox. */
  --fx-surface: #e8e4e0;
  --text-primary: #111111;
  --text-body: #333333;
  --text-muted: #5f5b54;   /* darkened: 4.24 -> passes on #e8e4e0 */
  --text-hover: #000000;
  --rule: rgba(0, 0, 0, 0.24);
  --rule-soft: rgba(0, 0, 0, 0.24);
}

.dark-mode .fx-logo {
  content: url('https://cdn.prod.website-files.com/667c2ea7460262b0b32b6add/667c3cfd7cdc38dd4ea5f75a_jmlogo.png');
}

.fx-invite {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(20px, 2.2vw, 29px);
  line-height: 1.35;
  color: var(--text-primary);
  margin: 0;
  max-width: 30ch;
  text-wrap: pretty;
}

/* — navigation cells — */
.fx-nav { grid-area: nav; }
.fx-social { grid-area: social; }

.fx-links { display: flex; flex-wrap: wrap; gap: 10px 22px; }

.fx-links a {
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  color: var(--text-body);
}

.fx-links a:hover { color: var(--text-hover); }

/* — spec rows — */
/* margin 0: the UA gives <dl> a 1em block margin, which pushed the cell off
   the grid track and let the rule-colour background show through as a thick
   grey band above and below the spec rows. */
.fx-spec { grid-area: spec; margin: 0; padding-top: 6px; padding-bottom: 6px; }

.fx-spec > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  padding: 13px 0;
  border-top: 1px solid var(--rule);
}

.fx-spec > div:first-child { border-top: 0; }

.fx-spec dt {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}

.fx-spec dt::before { content: "\25B7"; color: var(--text-muted); margin-right: 10px; font-size: 9px; }

.fx-spec dd { font-size: 14px; color: var(--text-primary); margin: 0; text-align: right; }
.fx-spec dd a { color: var(--text-primary); }
.fx-spec dd a:hover { color: var(--text-hover); }

/* — the seal: red as a structural block, not a sticker — */
.fx-cta {
  grid-area: cta;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: var(--accent);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(22px, 2vw, 30px);
  transition: background 0.2s ease;
}

.fx-cta:hover { background: #a63317; color: #fff; }
.fx-cta .arrow { font-family: var(--font-sans); font-size: 0.8em; }

/* — folio — */
.fx-folio {
  grid-area: folio;
  display: flex;
  /* stacked, not space-between: the colophon is a subordinate line and reads
     as a second byline when it sits out on the opposite edge. */
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  font-size: 12px;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

/* — the spine — */
.fx-spine {
  grid-area: spine;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px 0;
}

.fx-spine .v-text { color: var(--text-muted); }

@media (max-width: 900px) {
  .fx {
    grid-template-columns: 1fr;
    grid-template-areas: "hello" "nav" "social" "spec" "cta" "folio";
  }
  .fx-spine { display: none; }
  .fx-cell { padding: 24px 20px; }
  .fx-hello .fx-label,
  .fx-nav .fx-label,
  .fx-social .fx-label { margin-right: 0; }
  .fx-hello { padding: 32px 20px 28px; }
  .fx-cta { padding: 24px 20px; }
  .fx-spec dd { text-align: right; }
}

/* ============================================
   EDITORIAL HOME
   The homepage as a magazine front: masthead (kicker / statement /
   standfirst), features carrying folio rows, the archive as a numbered
   index, and section kickers with a short leading rule. Numbering runs
   01–04 across features and index so the whole page reads as one
   table of contents.
   ============================================ */

.home-masthead .ed-kicker { display: block; margin-bottom: 24px; }

.home-masthead h1 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(40px, 5.5vw, 76px);
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  margin: 0 0 24px;
  max-width: 1100px;
}

.home-masthead p {
  font-size: 18px;
  line-height: 1.6;
  color: var(--text-body);
  max-width: 56ch;
}

/* folio row above each feature cover */
/* the bare hairline above a featured tile — the label row it replaced was
   removed, the line itself was missed. Soft variant reads thinner. */
.feature-rule {
  border-top: 1px solid var(--rule-soft);
  margin-bottom: 24px;
}

.feature-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  border-top: 1px solid var(--rule);
  padding-top: 14px;
  margin-bottom: 16px;
}

/* section kicker: label voice with a short leading rule */
.sec-kicker {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 0 0 28px;
}

.sec-kicker::before {
  content: "";
  width: 40px;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
  flex-shrink: 0;
}

/* index numbers in the archive rows */
.ca-num {
  display: inline;
  margin-right: 14px;
}

/* Elsewhere: sentence-case serif heading in the head grid.
   Compound selector because `.index-head p` outranks a bare class — the same
   specificity trap as .ed-minihead and .pv-standfirst before it. */
.index-head p.index-title,
.index-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(28px, 3.2vw, 40px);
  line-height: 1.1;
  color: var(--text-primary);
  margin: 0;
  text-wrap: pretty;
}

@media (max-width: 1100px) {
  /* the 360px photo reservation only exists while the photo does */
  .home-masthead h1 { max-width: none; }
}

@media (max-width: 760px) {
  .home-masthead h1 { font-size: clamp(34px, 9vw, 44px); }
  .feature-head { padding-top: 12px; margin-bottom: 12px; }
}



/* figure numbers read inline ahead of their caption */
.cs-fig-num { display: inline; margin-right: 10px; color: var(--text-primary); }

/* end mark — the editorial full stop */
.ed-closing-quote::after {
  content: "";
  display: inline-block;
  width: 0.32em;
  height: 0.32em;
  background: var(--accent);
  margin-left: 0.35em;
  vertical-align: baseline;
}

/* ============================================
   EDITORIAL — THE LOUD LAYER
   Japanese editorial devices: vertical text in the gutters, one vermilion
   accent doing all the numbering, taped ephemera, and hard
   double rules. Everything here is furniture on top of the existing system —
   no new text sizes, the accent is the only new colour.
   ============================================ */

/* vertical text utility — reads top-to-bottom like a spine */
.v-text {
  writing-mode: vertical-rl;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Red is rationed to three moments per visit: the masthead full stop, the
   footer action block, and the case-study end mark. Numerals and markers
   return to the quiet label voice. */
.no,
.ca-num { color: var(--text-muted); }
.cs-fig-num { color: var(--text-primary); }
.home-masthead .no { color: var(--accent); }

/* attached print — white border, faint hairline, one degree of rotation.
   The tape strips and heavy tilt read as scrapbook; this reads as a print
   placed on the page. */
.taped {
  position: relative;
  display: block;
  background: #fff;
  padding: 8px 8px 12px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  transform: none;
}

.taped img { display: block; width: 100%; height: auto; }

/* ---- masthead, loud ---- */
.home-masthead { position: relative; }

.home-masthead h1 {
  font-size: clamp(48px, 8vw, 118px);
  line-height: 0.98;
  letter-spacing: -0.015em;
  /* Held a gap for the taped print that used to sit here; that print is gone,
     so this is now just a comfortable measure. The old
     `min(1000px, 100vw - 360px)` collapsed to 15px at 375px wide and broke the
     title into one word per line. */
  max-width: 20ch;
}

.home-masthead h1 em {
  font-style: italic;
  font-weight: 400;
}

/* the closing period is the one red mark on the page */
.home-masthead h1 .no { font-style: normal; }

/* ---- feature covers: spine text up the inside edge ---- */
.case-media-cover { position: relative; }

.cover-spine {
  position: absolute;
  top: 28px;
  left: 28px;
  color: rgba(255, 255, 255, 0.55);
}

.case-media-cover .cover-spine { z-index: 2; }

@media (max-width: 760px) {
  .cover-spine { display: none; }
}

/* the About grid becomes a drift of attached prints */
.photo-grid-2col .taped { margin: 0; }
/* straight — the print border carries the ephemera feel on its own */
.photo-grid-2col .taped.t0,
.photo-grid-2col .taped.t1,
.photo-grid-2col .taped.t2 { transform: none; }
/* the About grid no longer opens a lightbox, so it must not invite a click */
.photo-grid-2col .taped img { cursor: default; }

/* ---- Elsewhere: the index as a headline act ----
   A magazine contents page at poster scale: giant serif rows, vermilion
   numerals, and a sticky media pane that answers every hover. Pulse holds
   the pane by default, so the one working product is always on screen. */
.else-split {
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: 64px;
  align-items: start;
  padding-top: var(--space-block);
}

.else-head {
  display: flex;
  justify-content: space-between;
  gap: 22px;
  padding-bottom: 12px;
}

.else-head span {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

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

.else-list a {
  display: flex;
  align-items: baseline;
  gap: 22px;
  padding: 20px 0;
  border-top: 1px solid var(--rule);
}

.else-list li:last-child a { border-bottom: 1px solid var(--rule); }

.else-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(24px, 2.4vw, 36px);
  line-height: 1.05;
  color: var(--text-primary);
  transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Hovering the list dims every row except the one under the cursor — the
   same focus move the sidebar figures use — while a rule draws itself in
   under the live row. No italic, no hue: emphasis by attention. */
.else-list:hover a { opacity: 0.35; transition: opacity 0.25s ease; }
.else-list:hover a:hover { opacity: 1; }

.else-list a { position: relative; }

.else-list a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--text-primary);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}

.else-list a:hover::after,
.else-list a:focus-visible::after { transform: scaleX(1); }
.else-list a .tile-meta { margin-left: auto; white-space: nowrap; }

.else-media {
  position: sticky;
  top: 88px;
  aspect-ratio: 1 / 1;
  overflow: hidden;
}

/* contain, never cover: the assets are mostly 4:3 landscape and a portrait
   pane was amputating them. The full frame always shows, letterboxed on the
   surface tone like a plate on paper. */
.else-media img,
.else-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.35s ease;
}

.else-media .is-on { opacity: 1; }

@media (max-width: 900px) {
  .else-split { grid-template-columns: 1fr; gap: 28px; }
  /* relative, NOT static: the pane's img/video children are absolutely
     positioned, so removing the containing block let them resolve against an
     ancestor near the top of the page and paint over the masthead. */
  .else-media { position: relative; aspect-ratio: 4 / 3; order: -1; }
  .else-title { font-size: 22px; }
  .else-list a { padding: 16px 0; }
}

/* ============================================
   CASE STUDY LAYOUT BLOCKS  (Factor+ / RAF)
   The pages were 12,000px of unbroken text in a single repeating block, with
   16 different text styles and no images at all. This adds a small set of
   layout blocks so the page has rhythm — full-bleed media, side-by-side pairs,
   a two-column split — and cuts the type down to six styles.

   Type on these pages, and nothing else — every one of these is a step that
   already existed in TYPE-SYSTEM.md, so nothing new was invented here:
     hero title    Recoleta 500  clamp(48–104)
     chapter title Recoleta 500  clamp(26–34)   also the closing statement
     pull quote    Recoleta 500  clamp(22–26)   italic
     block title   Recoleta 500  20             card / artifact / step headings
     lead          Jakarta  400  18             hero deck
     body          Jakarta  400  16 / 1.7
     small         Jakarta  400  14             cards, lists, tables, captions
     label         Jakarta  600  12 caps muted
   ============================================ */

/* ---- image placeholders ---- */
.cs-ph {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: var(--surface);
  /* Deliberately louder than a normal rule. These are scaffolding — they should
     read as "an image belongs here", not as a finished surface. --rule at 0.1
     alpha on top of --surface was almost invisible against the page. */
  border: 1px dashed var(--veil-border);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  padding: 24px;
  min-height: 200px;
}
.cs-ph span { max-width: 40ch; line-height: 1.6; }
.cs-ph.is-wide   { aspect-ratio: 16 / 7; }
.cs-ph.is-screen { aspect-ratio: 4 / 3; }
.cs-ph.is-phone  { aspect-ratio: 3 / 5; }

/* ---- media band ----
   .cs-figure is the wrapper for every image beat. It nulls the margins of a
   pair/trio nested inside it, otherwise the two margins stack and the gap
   between chapters doubles. */
.cs-figure { margin: var(--space-section) 0; }
.cs-figure > .cs-pair,
.cs-figure > .cs-trio,
.cs-figure > .cs-split { margin: 0; }

/* Edge-to-edge. Breaks out of .wrap by cancelling the gutter and the max-width
   in one move — the calc is the distance from the wrap edge to the viewport
   edge, which is what a negative margin has to cover. */
.cs-figure.is-bleed {
  max-width: none;
  padding: 0;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  width: 100vw;
  margin-top: var(--space-section);
  margin-bottom: var(--space-section);
}
.cs-figure.is-bleed .cs-ph { border-radius: 0; border-left: 0; border-right: 0; }
.cs-figure.is-bleed .cs-caption {
  padding: 0 var(--wrap-gutter);
  max-width: var(--wrap-width);
  margin-left: auto;
  margin-right: auto;
}

.cs-figure figcaption,
.cs-caption {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-muted);
  margin-top: 12px;
  max-width: 60ch;
}

/* ---- side-by-side pair ---- */
.cs-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin: var(--space-section) 0;
}

/* ---- three-up ---- */
.cs-trio {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin: var(--space-section) 0;
}

/* ---- two-column split: text one side, media the other ----
   The heading here borrows the chapter-title step rather than adding one, so a
   split reads as a beat inside the story, not a new section. */
.cs-split-text .cs-label { margin-bottom: 14px; }
.cs-split-text h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(22px, 2.4vw, 26px);   /* Title L — same step as the pull quote */
  line-height: 1.25;
  color: var(--text-primary);
  margin: 0 0 16px;
}
.cs-split-text p {
  font-size: 16px;
  line-height: 1.7;
  color: var(--text-body);
  margin: 0;
}

.cs-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
  margin: var(--space-section) 0;
}
.cs-split.is-flipped .cs-split-media { order: -1; }
.cs-split h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.3;
  color: var(--text-primary);
  margin: 0 0 12px;
}
.cs-split p { font-size: 16px; line-height: 1.75; color: var(--text-body); margin: 0 0 16px; }
.cs-split p:last-child { margin-bottom: 0; }

@media (max-width: 860px) {
  .cs-split { grid-template-columns: 1fr; gap: 24px; }
  .cs-split.is-flipped .cs-split-media { order: 0; }
  .cs-pair, .cs-trio { grid-template-columns: 1fr; gap: 16px; }
  .cs-ph { min-height: 160px; }
}

/* ============================================
   TITLE VOICE — GROTESQUE
   The display cut of Fraunces is built for headline sizes; below ~30px its
   hairlines go faint and reading gets hard. So the split is now clean:
   Fraunces for HEADERS (masthead, page titles, hero and chapter titles,
   statement lines), Instrument Sans semibold for every mid-size TITLE
   (list rows, cards, gallery captions, block headings).
   ============================================ */
.masonry-item h3,
.ca-title,
.ca-info-title,
.thought-card h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0;
  line-height: 1.4;
}

.ed-numbered h4,
.ed-moment h4,
.ed-compare-card h4,
.ed-artifact h3 {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 16px;
  line-height: 1.4;
}

.else-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(17px, 1.5vw, 22px);
  letter-spacing: -0.01em;
}

.fx-invite {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: clamp(18px, 1.9vw, 25px);
  line-height: 1.45;
  letter-spacing: -0.005em;
}

/* ============================================
   EDITORIAL GALLERIES
   Plates, not tiles. Every gallery item takes a museum plate number from a
   CSS counter; section heads carry the reference's rule-and-terminal device
   with the work count as data; hovering focuses one plate the way the
   homepage index focuses one row.
   ============================================ */
.masonry-item {
  counter-increment: plate;
  display: flex;
  flex-direction: column;
}

/* section head: leading rule (existing ::before) — title — long rule with
   square terminal — count */
.sec-line {
  flex: 1;
  height: 5px;
  background:
    linear-gradient(var(--rule), var(--rule)) 0 50% / calc(100% - 11px) 1px no-repeat,
    linear-gradient(var(--text-muted), var(--text-muted)) 100% 50% / 5px 5px no-repeat;
  margin: 0 4px;
}

.sec-count {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}

/* ---- thumbnail rollover ----
   Copied from awwwards.com/inspiration_search/gallery (measured, not eyeballed):
   an absolutely positioned layer, inset 0, carrying
   `linear-gradient(0deg, rgba(0,0,0,.5) 0%, rgba(0,0,0,0) 100%)`, opacity
   0 -> 1 over 0.3s, laid out as a flex row aligned to flex-end with
   space-between so details sit bottom-left and the index bottom-right. */
.masonry-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 100%);
  opacity: 0;
  transition: opacity 0.3s;
  z-index: 2;
  pointer-events: none;   /* clicks still reach the image for the lightbox */
}

/* details: category label over title, bottom-left, revealed with the gradient */
.mi-meta {
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: 16px;
  z-index: 3;
  opacity: 0;
  transition: opacity 0.3s;
  pointer-events: none;
}

.mi-kind {
  display: block;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
  margin-bottom: 4px;
}

.mi-meta h3 {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.3;
  color: #fff;
  text-wrap: pretty;
}

.masonry-item:hover::before,
.masonry-item:hover .mi-meta { opacity: 1; }

/* Touch devices have no hover: show the gradient and details permanently so
   the captions are never unreachable. */
@media (hover: none) {
  .masonry-item::before,
  .masonry-item::after,
  }

/* The Built strip is a single showcase video, not a thumbnail grid. */
.masonry.cols-1 { grid-template-columns: 1fr; }
.masonry.cols-1 .masonry-item { aspect-ratio: auto; }
.masonry.cols-1 .masonry-item img,
.masonry.cols-1 .masonry-item video { height: auto; object-fit: contain; }
.masonry.cols-1 .masonry-item::before,
.masonry.cols-1 @media (max-width: 1000px) { .masonry { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 760px)  { .masonry { grid-template-columns: repeat(2, 1fr); gap: 20px 12px; }
    }

/* ============================================
   MASONRY  (Playground › Product work)
   Mirrors the Pulse profile gallery, which uses masonry-layout@4 with
   { columnWidth: '.grid-sizer', gutter: 24, percentPosition: true,
     horizontalOrder: true }. Implemented directly in nav.js instead of
   pulling the 24KB library, since the site ships no third-party JS and the
   horizontalOrder algorithm is just "item i goes in column i % cols" —
   verified against the live Pulse DOM.

   Column count is driven entirely from here: JS reads .grid-sizer's width
   and derives the rest, so breakpoints stay in CSS.
   ============================================ */
.masonry.is-masonry {
  display: block;
  position: relative;
}

.masonry.is-masonry .grid-sizer { position: absolute; width: calc(33.333% - 16px); }
.masonry.is-masonry .masonry-item { width: calc(33.333% - 16px); }

/* natural heights — nothing is cropped in this layout */
.masonry.is-masonry .masonry-item {
  aspect-ratio: auto;
  margin-bottom: 24px;
}

.masonry.is-masonry .masonry-item img,
.masonry.is-masonry .masonry-item video {
  height: auto;
  object-fit: fill;
  object-position: center;
}

@media (max-width: 900px) {
  .masonry.is-masonry .grid-sizer,
  .masonry.is-masonry .masonry-item { width: calc(50% - 12px); }
}

@media (max-width: 640px) {
  .masonry.is-masonry .grid-sizer,
  .masonry.is-masonry .masonry-item { width: 100%; }
}


/* ============================================
   MOBILE  (≤760px)
   Declared last on purpose. The earlier mobile overrides for the masthead sat
   *above* the later unconditioned .home-masthead rule, and media queries add
   no specificity — so the desktop values won and the title rendered at 48px
   inside a 15px column. Anything overriding a late rule has to come after it.
   ============================================ */

@media (max-width: 760px) {

  /* ---- masthead ---- */
  .home-masthead h1 {
    font-size: clamp(30px, 8.4vw, 38px);
    line-height: 1.08;
    max-width: none;
  }

  .home-masthead p { font-size: 16px; line-height: 1.55; }
  .home-masthead .ed-kicker { font-size: 10px; margin-bottom: 18px; }
      /* ---- nav ---- */
  /* .nav-links is display:none here, so its margin-left:auto no longer pushes
     anything — without this the toggle floats mid-bar between logo and menu. */
  .theme-toggle-btn { margin-left: auto; margin-right: 2px; }

  /* ---- page heros ---- */
  .page-title,
  .about-bio-title { font-size: clamp(30px, 8.4vw, 38px); line-height: 1.08; max-width: none; }
  .page-intro { font-size: 16px; }

  /* ---- section + feature type ---- */
  .section-heading, .home-section-head h2 { font-size: clamp(24px, 6.5vw, 30px); }
  .sec-kicker { font-size: 10px; }
  .index-title { font-size: clamp(24px, 6.5vw, 30px); }
  .case-caption { font-size: 17px; line-height: 1.35; }
  .feature-head span { font-size: 10px; }
  .cover-title, .case-media-cover .cover-title { font-size: clamp(34px, 11vw, 52px); }
  .ca-title { font-size: 15px; }
  .ca-meta { font-size: 10px; }

  /* ---- footer: tighter on a phone ---- */
  .site-footer { padding-top: var(--space-section); }
  .fx-cell { padding: 20px 20px; }
  .fx-hello { padding: 24px 20px 22px; }
  .fx-logo { width: 148px; margin-bottom: 18px; }
  .fx-invite { font-size: 17px; line-height: 1.4; }
  .fx-label { font-size: 10px; margin-bottom: 12px; }
  .fx-links { gap: 8px 18px; }
  .fx-links a { font-size: 14px; }
  .fx-spec > div { padding: 10px 0; }
  .fx-spec dd { font-size: 13px; }
  .fx-cta { font-size: 20px; padding: 20px; }
  .fx-folio { font-size: 11px; gap: 6px; }
}


/* ---- overlay menu: close control + editorial numbering ----
   A real close button rather than reusing the hamburger. The hamburger lives
   inside .nav, which has z-index:100 and therefore its own stacking context —
   so the toggle's z-index:1100 could never rise above the overlay at 1050 and
   the X was simply invisible once the menu opened. */
.nav-overlay-top {
  justify-content: space-between !important;
  align-items: center;
}

.nav-close {
  position: relative;
  width: 44px;
  height: 44px;
  border: 0;
  background: none;
  cursor: pointer;
  flex-shrink: 0;
}

.nav-close span {
  position: absolute;
  left: 10px;
  right: 10px;
  top: 50%;
  height: 2px;
  background: var(--text-white);
}

.nav-close span:nth-child(1) { transform: rotate(45deg); }
.nav-close span:nth-child(2) { transform: rotate(-45deg); }

.nav-overlay-links a {
  display: flex;
  align-items: baseline;
  gap: 16px;
}

.nav-no {
  font-family: var(--font-sans);
  font-style: normal;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--text-muted-on-dark);
  flex-shrink: 0;
}

@media (max-width: 760px) {
  .nav-overlay { padding: 18px 0; }
  .nav-overlay-links a { font-size: clamp(30px, 9vw, 42px); gap: 12px; }
  .nav-no { font-size: 10px; }
  .nav-overlay-footer a { font-size: 11px; }
}


/* ---- Elsewhere on mobile ----
   The desktop version leans entirely on hover to drive the media pane, which
   is unreachable on touch. Below 900px the pane is dropped and each row
   carries its own thumbnail instead, so every item shows its image without
   any interaction. */
.else-thumb { display: none; }

@media (max-width: 900px) {
  .else-media { display: none; }
  .else-split { display: block; }

  .else-head { display: none; }

  .else-list a {
    display: grid;
    grid-template-columns: 86px 24px 1fr;
    align-items: center;
    gap: 14px;
    padding: 12px 0;
  }

  .else-thumb {
    display: block;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    background: var(--surface);
  }

  .else-thumb .et-media { width: 100%; height: 100%; object-fit: cover; display: block; }

  .else-title { font-size: 17px; line-height: 1.3; }
  .else-list a .tile-meta { display: none; }
  .else-list:hover a { opacity: 1; }     /* no dimming without a pointer */
  .else-list a::after { display: none; } /* the drawn rule is a hover affordance */
}

@media (max-width: 760px) {
  .index-links {
    display: flex;
    flex-wrap: wrap;
    gap: 14px 22px;
  }
  /* flex-shrink:0 — these were collapsing to 44px each while their nowrap text
     needed ~110px, so the labels overlapped ("PlaygroundCraft"). */
  .index-links a { font-size: 14px; white-space: nowrap; flex: 0 0 auto; }
}

@media (max-width: 760px) {
  /* The section head is a flex row (rule · title · line · count). At 375px a
     long title plus "15 WORKS" overran the gutter, so the row wraps and the
     connecting line — decorative — is dropped. */
  .section-heading,
  .home-section-head h2 { flex-wrap: wrap; row-gap: 6px; }
  .sec-line { display: none; }
  .sec-count { flex: 0 0 auto; }
}


/* ---- archival fine print ---- */
.fine-print {
  font-family: var(--font-sans);
  font-size: 11px;
  line-height: 1.7;
  color: var(--text-muted);
  max-width: 52ch;
  margin-top: 24px;
}

p.fine-print.wrap { margin-top: var(--space-block); }

/* ============================================
   MOTION
   The liveliness layer. Everything here is decorative and therefore sits
   behind prefers-reduced-motion; the site is fully usable with none of it.
   One easing (the site's standard cubic-bezier), durations 0.3–0.8s, and
   nothing that moves more than a few pixels or 4% of scale.
   ============================================ */
@media (prefers-reduced-motion: no-preference) {

  /* colour changes never snap */
  a, button { transition: color 0.2s ease, opacity 0.2s ease, background-color 0.2s ease; }

  /* ---- arrival: heros introduce themselves ---- */
  @keyframes rise {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: none; }
  }

  .js-reveal .home-masthead > *,
  .js-reveal .page-hero > *,
  .js-reveal .about-bio-text > * {
    animation: rise 0.7s cubic-bezier(0.22, 1, 0.36, 1) backwards;
  }

  .js-reveal .home-masthead > :nth-child(1), .js-reveal .page-hero > :nth-child(1), .js-reveal .about-bio-text > :nth-child(1) { animation-delay: 0.05s; }
  .js-reveal .home-masthead > :nth-child(2), .js-reveal .page-hero > :nth-child(2), .js-reveal .about-bio-text > :nth-child(2) { animation-delay: 0.13s; }
  .js-reveal .home-masthead > :nth-child(3), .js-reveal .page-hero > :nth-child(3), .js-reveal .about-bio-text > :nth-child(3) { animation-delay: 0.21s; }
  .js-reveal .home-masthead > :nth-child(4), .js-reveal .page-hero > :nth-child(4), .js-reveal .about-bio-text > :nth-child(4) { animation-delay: 0.29s; }

  /* ---- nav: the drawn-in rule, same language as the index rows ---- */
  .nav-links a { position: relative; }

  .nav-links a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -6px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .nav-links a:hover::after,
  .nav-links a.current::after { transform: scaleX(1); }

  /* ---- galleries: slow settle into the frame ---- */
  .masonry-item img,
  .masonry-item video { transition: transform 0.8s cubic-bezier(0.22, 1, 0.36, 1); }
  .masonry-item:hover img,
  .masonry-item:hover video { transform: scale(1.04); }

  /* ---- feature covers ---- */
  .case-media-cover .cover-title { transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
  .case-media-cover:hover .cover-title { transform: scale(1.02); }
  .cover-spine { transition: color 0.4s ease; }
  .case-media-cover:hover .cover-spine { color: rgba(255, 255, 255, 0.85); }

  /* ---- Elsewhere pane: each swap eases down from a breath of zoom ---- */
  .else-media img,
  .else-media video {
    transform: scale(1.04);
    transition: opacity 0.35s ease, transform 0.9s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .else-media .is-on { transform: scale(1); }

  /* ---- theme toggle: the sun turns ---- */
  #theme-toggle-btn svg { transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1); }
  #theme-toggle-btn:hover svg { transform: rotate(30deg); }

  /* ---- reveal stagger: plates arrive left to right, not as one slab ---- */
  .js-reveal .masonry .masonry-item.reveal:nth-child(3n+2) { transition-delay: 0.06s; }
  .js-reveal .masonry .masonry-item.reveal:nth-child(3n)   { transition-delay: 0.12s; }
}


/* ============================================
   THEME LOCK  (/tfc, /esignatures)
   These two pages are 1:1 reproductions of the live Webflow case studies and
   have no dark palette — in dark mode their cs-* overrides produced a half-
   converted page. They now pin to light permanently: every token is restated
   on body.theme-locked, which out-cascades the html.dark-mode values for the
   whole subtree. The footer keeps its light-mode ink treatment, the logos
   stay dark-on-light, and the theme toggle is hidden here since it could not
   change anything.
   ============================================ */
body.theme-locked {
  --bg-cream: #f7f4f2;
  --bg-band: #f0ebe7;
  --surface: #eeece9;
  --rule: rgba(0, 0, 0, 0.18);
  --rule-soft: rgba(0, 0, 0, 0.11);
  --veil: rgba(255, 255, 255, 0.72);
  --veil-strong: rgba(255, 255, 255, 0.95);
  --veil-border: rgba(17, 17, 17, 0.14);
  --text-primary: #111111;
  --text-body: #333333;
  --text-muted: #756e64;
  --text-hover: #000000;
  --cs-text: #333333;
  --cs-text-2: #494949;
  --cs-text-3: #797979;
  --cs-band: #eeece9;
  --cs-rule: rgba(0, 0, 0, 0.12);
}

/* dark-mode logo swaps must not fire inside a locked page */
.theme-locked .nav .logo img {
  content: url('https://cdn.prod.website-files.com/667c2ea7460262b0b32b6add/667c3cfd7cdc38dd4ea5f75a_jmlogo.png');
}

/* footer keeps the light-page treatment: ink block, white wordmark */
.theme-locked .site-footer {
  --fx-surface: var(--bg-dark);
  --text-primary: #f4f4f0;
  --text-body: #d6d7d1;
  --text-muted: #9c9d96;
  --text-hover: #ffffff;
  --rule: rgba(255, 255, 255, 0.24);
  --rule-soft: rgba(255, 255, 255, 0.24);
}

.theme-locked .fx-logo {
  content: url('https://cdn.prod.website-files.com/667c2ea7460262b0b32b6add/67b0da14fbeb23da169d5c42_jmlogo-white.png');
}

.theme-locked #theme-toggle-btn { display: none; }



/* ============================================
   COVER PHOTO
   A case cover can carry campaign imagery behind its title. The scrim keeps
   the white Fraunces title at contrast; the photo is darkened rather than
   the title outlined.
   ============================================ */

.case-media-cover.has-photo { overflow: hidden; }

.case-media-cover.has-photo .cover-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 35%;
  filter: brightness(0.72) saturate(0.95);
}

/* a touch more weight at the base, where the spine and title edges sit */
.case-media-cover.has-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(12, 10, 8, 0.18) 0%, rgba(12, 10, 8, 0.34) 100%);
}

.case-media-cover.has-photo .cover-title { position: relative; z-index: 2; }
/* the spine is already absolutely positioned; it only needs to outrank the scrim */
.case-media-cover.has-photo .cover-spine { z-index: 2; }

/* ============================================
   RESUME  (/resume)

   A CV is a table of facts, so it is set like one: a fixed rail of section
   labels on the left, the record on the right, and a rule between every
   entry. The only display type on the page is the name — everything below it
   is the sans, because a hiring manager scans this, they don't read it.
   ============================================ */

.cv { max-width: 1100px; }

/* — masthead — */
.cv-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: 48px;
  align-items: start;
  padding-bottom: 56px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.18);
}

.cv-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(46px, 7vw, 86px);
  line-height: 0.94;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin: 14px 0 0;
}

.cv-head-meta { padding-top: 8px; }

.cv-role {
  font-family: var(--font-sans);
  font-size: clamp(20px, 2.2vw, 27px);
  line-height: 1.25;
  color: var(--text-primary);
  margin: 0 0 32px;
  max-width: 22ch;
}

/* the positioning line: the JDs lead with problem definition, so the page
   should too rather than going straight from the name to the first job */
.cv-lede {
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.65;
  color: var(--text-body);
  margin: 0 0 30px;
  max-width: 46ch;
}

.cv-contact { margin: 0 0 32px; }

.cv-contact > div {
  display: grid;
  grid-template-columns: 100px minmax(0, 1fr);
  gap: 12px;
  padding: 9px 0;
  border-top: 1px solid rgba(0, 0, 0, 0.11);
}

.cv-contact dt {
  font-family: var(--font-sans);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.cv-contact dd {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-body);
}

.cv-contact a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--rule); }
.cv-contact a:hover { color: var(--text-hover); border-bottom-color: currentColor; }

.cv-download {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-primary);
  text-decoration: none;
  padding: 13px 20px;
  border: 1px solid rgba(0, 0, 0, 0.18);
}

.cv-download:hover { background: var(--text-primary); color: var(--bg-cream); border-color: var(--text-primary); }

/* — the rail + record — */
.cv-block {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr);
  gap: 40px;
  padding-top: 64px;
}

.cv-rail {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 6px 0 0;
  /* the label tracks the column it names through a long scroll */
  position: sticky;
  top: 110px;
  align-self: start;
}

.cv-role-entry { padding: 28px 0 32px; border-top: 1px solid rgba(0, 0, 0, 0.11); }
.cv-role-entry:first-child { border-top: 0; padding-top: 0; }

.cv-role-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  margin-bottom: 14px;
}

.cv-role-head h3 {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: clamp(19px, 2vw, 23px);
  line-height: 1.3;
  color: var(--text-primary);
  margin: 0;
}

.cv-role-head h3 b { font-weight: 600; }
.cv-title { color: var(--text-muted); }
.cv-title::before { content: "\2013\00a0"; }

.cv-dates {
  flex: none;
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  white-space: nowrap;
}

/* the one place the accent is spent on this page */
.cv-role-entry.is-current .cv-dates { color: var(--accent); }

.cv-org-note {
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: -6px 0 14px;
}

.cv-body p {
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.65;
  color: var(--text-body);
  margin: 0 0 12px;
  max-width: 70ch;
}

.cv-body p:last-child { margin-bottom: 0; }

.cv-body a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--accent);
}

.cv-body a:hover { color: var(--accent); }
.cv-body p b { font-weight: 600; color: var(--text-primary); }

/* — practice list — */
.cv-spec > div {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: 24px;
  padding: 20px 0;
  border-top: 1px solid rgba(0, 0, 0, 0.11);
}

.cv-spec > div:first-child { border-top: 0; padding-top: 0; }

.cv-spec dt {
  font-family: var(--font-sans);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding-top: 3px;
}

.cv-spec dd {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-body);
}

.cv-footnote {
  margin: 64px 0 0;
  padding-top: 22px;
  border-top: 1px solid rgba(0, 0, 0, 0.18);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 62ch;
}

/* dark mode: the hairlines have to be redeclared, they are literal rgba */
.dark-mode .cv-head,
.dark-mode .cv-footnote { border-color: rgba(255, 255, 255, 0.22); }

.dark-mode .cv-contact > div,
.dark-mode .cv-role-entry,
.dark-mode .cv-spec > div { border-color: rgba(255, 255, 255, 0.14); }

.dark-mode .cv-download { border-color: rgba(255, 255, 255, 0.22); }
.dark-mode .cv-download:hover { background: var(--text-primary); color: var(--bg-dark); }

/* — narrow — */
@media (max-width: 900px) {
  .cv-head { grid-template-columns: 1fr; gap: 32px; padding-bottom: 40px; }
  .cv-name { font-size: clamp(40px, 12vw, 62px); }
  .cv-head-meta { padding-top: 0; }
  .cv-block { grid-template-columns: 1fr; gap: 20px; padding-top: 48px; }
  .cv-rail { position: static; }
}

@media (max-width: 600px) {
  /* the dates can't share a line with a wrapping company name at this width */
  .cv-role-head { flex-direction: column; align-items: flex-start; gap: 6px; }
  .cv-title { display: block; }
  .cv-title::before { content: ""; }
  .cv-contact > div,
  .cv-spec > div { grid-template-columns: 1fr; gap: 4px; }
}

/* — print: this page should survive Cmd-P as a real resume — */
@media print {
  .nav, .nav-overlay, .site-footer, .floating-actions, .cv-download { display: none !important; }
  .cv { padding: 0; max-width: none; }
  .cv-block { padding-top: 28px; break-inside: avoid; }
  .cv-role-entry { break-inside: avoid; }
  .cv-body p { font-size: 10.5pt; }
}


/* ============================================
   CASE-STUDY GATE
   ============================================ */

html.cs-locked { overflow: hidden; }
/* The case pages have no <main>, so hide every top-level block except the gate.
   The nav and its mobile overlay are deliberately spared: without them the gate
   was a dead end with no logo, no menu, and no way back to the site. */
html.cs-locked body > :not(.cs-gate):not(.nav):not(.nav-overlay) { visibility: hidden; }

.cs-gate {
  position: fixed;
  inset: 0;
  /* below .nav (100) so the header stays clickable above the veil */
  z-index: 90;
  background: var(--bg-cream);
  display: flex;
  align-items: center;
  justify-content: center;
  /* clears the sticky nav on short viewports */
  padding: 120px 24px 48px;
}

.cs-gate-card { max-width: 460px; }

.cs-gate-kicker {
  display: block;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 18px;
}

.cs-gate-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(28px, 4vw, 40px);
  line-height: 1.12;
  color: var(--text-primary);
  margin: 0 0 14px;
}

.cs-gate-note {
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-body);
  margin: 0 0 26px;
}

.cs-gate-note a { color: var(--text-primary); text-decoration: underline; text-underline-offset: 3px; }
.cs-gate-note a:hover { color: var(--accent); }

.cs-gate-form { display: flex; gap: 10px; flex-wrap: wrap; }

.cs-gate-form input {
  flex: 1 1 200px;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text-primary);
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 0;
  padding: 12px 16px;
  outline: none;
}

.cs-gate-form input:focus { border-color: var(--text-primary); }
.cs-gate-form input::placeholder { color: var(--text-muted); }

.cs-gate-form button { cursor: pointer; background: transparent; }

.cs-gate-error {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--accent);
  margin: 14px 0 0;
  min-height: 1em;
}

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

.cs-gate-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 28px;
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  text-decoration: none;
}

.cs-gate-back:hover { color: var(--text-primary); }


/* ============================================
   MOBILE PASS
   The desktop layout doesn't shrink well: at 375px the cover tiles rendered
   327x184, gallery cells 158x118, and five heading styles sat between 24 and
   31px. This block treats mobile as its own product, the way an app differs
   from the site it mirrors: type steps down, chrome steps back, and imagery
   becomes the hero instead of the casualty. Appended last so nothing earlier
   in the cascade can out-rank it; it must never touch desktop.
   ============================================ */

@media (max-width: 700px) {

  /* --- type scale: one step down across the board --- */
  .home-masthead h1 { font-size: 28px; line-height: 1.12; }
  .home-masthead p { font-size: 15px; line-height: 1.55; max-width: 30ch; }
  .page-title { font-size: 26px; line-height: 1.15; }
  .page-intro { font-size: 15px; line-height: 1.55; }
  .about-bio-title { font-size: 26px; }
  .about-bio-copy p { font-size: 15px; }
  .index-head p.index-title,
  .index-title { font-size: 22px; }
  .section-heading { font-size: 18px; }
  .case-caption { font-size: 15px; line-height: 1.5; }

  /* --- homepage covers: full-bleed and tall, the app-hero treatment.
         327x184 was a thumbnail; edge-to-edge 4:5 is 375x469. --- */
  .case-study .case-media-cover {
    margin-left: calc(-1 * var(--wrap-gutter));
    margin-right: calc(-1 * var(--wrap-gutter));
    aspect-ratio: 4 / 5;
  }
  .case-media-cover .cover-title { font-size: 34px; }
  /* the hairline above each tile belonged to the framed layout */
  .feature-rule { display: none; }
  .case-caption-row { margin-top: 16px; }

  /* --- galleries: one full-width column instead of two thumbnails.
         The hover-reveal labels don't exist on touch, so the scrim and
         label ride along permanently, slightly quieter than on hover. --- */
  .masonry:not(.is-masonry),
  .masonry.cols-2:not(.is-masonry) { grid-template-columns: 1fr; }
  .masonry-item::before { opacity: 0.85; }
  .mi-meta { opacity: 1; }

  /* --- case-study panel: compact the spec sheet so the story starts
         sooner. Meta rows pair up; results already sit two-up. --- */
  .pv-title { font-size: 22px; }
  .pv-meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 16px;
  }
  .pv-meta div { padding: 8px 0; }

  /* --- quieter chrome --- */
  .cs-gate-title { font-size: 26px; }

  /* --- footer: it measured 813px, a full viewport of stacked cells.
         The ruled NAVIGATE / CONNECT labels go; their links merge into
         dot-separated rows. The spec sheet keeps its two meaningful rows
         and drops the flavour. Target is roughly half the height. --- */
  .fx-cell { padding: 20px var(--wrap-gutter); }

  .fx-nav .fx-label,
  .fx-social .fx-label { display: none; }
  .fx-nav { padding-bottom: 8px; }
  .fx-social { padding-top: 8px; }

  .fx-links { gap: 6px 0; }
  .fx-links a { font-size: 14px; min-height: 36px; display: inline-flex; align-items: center; }
  .fx-links a:not(:last-child)::after {
    content: "\00B7";
    margin: 0 12px;
    color: var(--text-muted);
  }

  .fx-hello .fx-invite { font-size: 14px; line-height: 1.5; }
  .fx-logo { max-width: 120px; }

  .fx-spec div { padding: 7px 0; }
  /* Studio and Archive rows are flavour; Discipline and Currently carry it */
  .fx-spec div:nth-child(3),
  .fx-spec div:nth-child(4) { display: none; }

  .fx-cta { padding-top: 16px; padding-bottom: 16px; }
  .fx-folio { padding-top: 14px; padding-bottom: 20px; font-size: 11px; }
}

/* Very small phones: hold the floor rather than keep scaling. */
@media (max-width: 380px) {
  .home-masthead h1 { font-size: 26px; }
  .case-media-cover .cover-title { font-size: 30px; }
}


/* ---- theme toggle inside the mobile menu ---- */
.overlay-theme {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.75);
}

.overlay-theme:hover { color: #ffffff; }

/* its own row above the social links; crammed at the row's end the
   two-word label wrapped against LET'S CHAT */
.nav-overlay-footer { flex-wrap: wrap; row-gap: 20px; }
.overlay-theme { width: 100%; order: -1; margin: 0; justify-content: flex-start; }
.overlay-theme .ot-label { white-space: nowrap; }

/* the label states the destination, not the current mode */
.overlay-theme .ot-label::after { content: "Dark Mode"; }
.dark-mode .overlay-theme .ot-label::after { content: "Light Mode"; }

/* pages that lock the theme hide both toggles */
.theme-locked .overlay-theme { display: none; }

@media (max-width: 760px) {
  /* the bar toggle moves into the menu on mobile */
  #theme-toggle-btn { display: none; }

  /* 44px stays the tap target; the drawn lines slim to 24px */
  .nav-toggle { align-items: flex-end; }
  .nav-toggle span { width: 24px; }
}

@media (max-width: 700px) {
  /* masthead: identity was spending the whole first viewport. Tighter
     rhythm and one fewer sentence lets the first tile crest the fold. */
  .home-intro.home-masthead { padding-top: 20px; }
  .home-masthead .ed-kicker { margin-bottom: 14px; }
  .home-masthead h1 { margin-bottom: 14px; }
  .home-masthead p { margin: 0; }
  .mast-prev { display: none; }
  .case-study { padding-top: 32px; }
}


/* ---- the menu follows the theme ----
   The overlay was hardcoded ink, so toggling the theme from inside it changed
   nothing the eye could see until the menu closed. In light mode it now paints
   cream with ink type, flipping the moment the toggle is tapped. */

body:not(.dark-mode) .nav-overlay { background: var(--bg-cream); }

body:not(.dark-mode) .nav-overlay-links a,
body:not(.dark-mode) .nav-overlay-links a.current { color: var(--text-primary); }

body:not(.dark-mode) .nav-no,
body:not(.dark-mode) .nav-overlay-footer a { color: var(--text-muted); }
body:not(.dark-mode) .nav-overlay-footer a:hover { color: var(--text-primary); }

body:not(.dark-mode) .nav-overlay-footer { border-top-color: rgba(0, 0, 0, 0.14); }

body:not(.dark-mode) .nav-close span { background: var(--text-primary); }

body:not(.dark-mode) .nav-overlay-top .logo img {
  content: url('https://cdn.prod.website-files.com/667c2ea7460262b0b32b6add/667c3cfd7cdc38dd4ea5f75a_jmlogo.png');
}

body:not(.dark-mode) .overlay-theme { color: var(--text-muted); }
body:not(.dark-mode) .overlay-theme:hover { color: var(--text-primary); }
