/* ==========================================================================
   Ugaru — Product strategy and digital product studio
   Design system: editorial, monochrome, flat grey, precision over polish.
   ========================================================================== */

/* ---------- Fonts (self-hosted: no third-party requests) ---------- */
@font-face {
  font-family: "Instrument Sans";
  src: url("/assets/fonts/instrument-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Instrument Sans";
  src: url("/assets/fonts/instrument-sans-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Instrument Sans";
  src: url("/assets/fonts/instrument-sans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
/* Display serif. Variable optical size, weight pinned to 400 —
   one 49 KB file covers the 68px headline and the 15px card titles. */
@font-face {
  font-family: "Newsreader";
  src: url("/assets/fonts/newsreader.woff2") format("woff2-variations");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* Instrument Sans has no IPA. These eight phonetic glyphs come from Inter,
   and only these — unicode-range keeps every other character on the brand face. */
@font-face {
  font-family: "Instrument Sans";
  src: url("/assets/fonts/ipa.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0259, U+0261, U+027E, U+0283, U+0292, U+02C8, U+02CC, U+02D0;
}

/* Subset to ウ ガ ル only — the three glyphs the brand device uses. */
@font-face {
  font-family: "Ugaru Kana";
  src: url("/assets/fonts/noto-sans-jp-kana-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
  unicode-range: U+30A6, U+30AC, U+30EB;
}

/* ---------- Tokens ---------- */
:root {
  --bg:        #F4F3F0;
  --bg-soft:   #EEEDE9;
  --surface:   #FAFAF8;
  --surface-2: #F1F0EC;

  --ink:       #121211;
  --ink-2:     #45443F;
  --ink-3:     #6E6C65;
  --ink-4:     #918F87;

  --line:      rgba(18, 18, 17, 0.13);
  --line-soft: rgba(18, 18, 17, 0.08);
  --line-firm: rgba(18, 18, 17, 0.28);

  /* Two flat greys. The hero sits a shade below the rest of the page. */
  --grey-hero: #F4F3F1;
  --grey-page: #F7F6F4;

  /* The one white fill in the system. Cards only — nothing else earns it. */
  --glass: rgba(255, 255, 255, 0.38);

  --radius-s:  10px;
  --radius-m:  16px;
  --radius-l:  22px;

  --wrap:      1240px;
  --gutter:    clamp(20px, 5vw, 64px);

  --sans:  "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  --jp:    "Ugaru Kana", var(--sans);

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* ---------- Type scale ----------
     One ladder, two tiers. Nothing sets a font-size outside these tokens.

     Tier 1 — UI and body copy: FIXED. Interface text does not need to scale;
     it needs to stay legible, and 13/14.5/16/17 is right on every device.
     Making it fluid only produces sub-pixel drift and breaks the rhythm.

     Tier 2 — Editorial and display: FLUID, interpolated 390px -> 1440px.
     Each token's *minimum* is placed deliberately on a ladder that sits
     above the fixed tier, so the hierarchy is monotonic at both ends of the
     range. This is the part that was broken before: the clamps all bottomed
     out at minimums chosen case by case, which then collided with the fixed
     sizes (a card heading outranking a section heading, the hero claim
     falling below body copy).

     Mobile ladder (390px): 17 / 19 / 20 / 22 / 26 / 31 / 37 / 54
     Desktop ladder (1440px): 17 / 20.5 / 20.8 / 24.8 / 36.8 / 46.4 / 64.8 / 190 */

  --fs-caption: 13px;
  --fs-small:   14.5px;
  --fs-base-s:  16px;
  --fs-base:    17px;

  --fs-h5:      18.4px; /* card and row titles — flat by design, they sit inside components */
  --fs-lead:    clamp(19px,   1.153rem + 0.143vw,  20.5px);
  --fs-h4:      clamp(20px,   1.232rem + 0.076vw,  20.8px);
  --fs-quote:   clamp(21px,   1.280rem + 0.133vw,  22.4px);
  --fs-h3:      clamp(22px,   1.310rem + 0.267vw,  24.8px);
  --fs-h2:      clamp(26px,   1.374rem + 1.029vw,  36.8px);
  --fs-display: clamp(31px,   1.580rem + 1.467vw,  46.4px);
  --fs-h1:      clamp(37px,   1.667rem + 2.648vw,  64.8px);
  --fs-kana:    clamp(54px,   0.218rem + 12.952vw, 196px);
  --fs-kana-sm: clamp(38.4px, 1.954rem + 1.829vw,  57.6px);
}

/* ---------- Dark mode ----------
   Same structure, inverted tone: near-black ground, warm off-white ink,
   the same two accent blobs turned down so they read as embers rather than
   daylight. Toggled via [data-theme="dark"] on <html>, set by ugaru.js
   before first paint to avoid a flash. */
html[data-theme="dark"] {
  --bg:        #0F0E0D;
  --bg-soft:   #141310;
  --surface:   #141310;
  --surface-2: #191714;

  --ink:       #F3F1EC;
  --ink-2:     #C7C4BC;
  --ink-3:     #9A968C;
  --ink-4:     #716D64;

  --line:      rgba(243, 241, 236, 0.13);
  --line-soft: rgba(243, 241, 236, 0.08);
  --line-firm: rgba(243, 241, 236, 0.26);

  --grey-hero: #0C0B0A;
  --grey-page: #100F0E;

  --glass: rgba(255, 255, 255, 0.045);
}
html[data-theme="dark"] .tone-a { background: #121110; }
html[data-theme="dark"] .tone-b { background: #141210; }
html[data-theme="dark"] .tone-g { background: #090908; }
/* Multi-stop, eased falloff — the anti-banding measure.

   A two-stop gradient hands the browser one long linear alpha ramp, and a
   linear ramp on a near-black ground quantises into even, visible steps.
   These curves do two things instead:

   1. Eight stops on a smoothstep-like curve. The browser now interpolates
      eight short segments rather than one long one, so no single stretch is
      flat enough to resolve into a band.
   2. The endpoint is rgba(R,G,B,0), never `transparent`. The `transparent`
      keyword is rgba(0,0,0,0), so fading to it drags the ramp through black
      and adds a dirty halo — which on a dark ground is itself a visible edge.

   Light mode keeps its simple two-stop version: it never bands, because the
   eye barely resolves 8-bit steps at the top of the luminance curve. */
html[data-theme="dark"] .hero::before,
html[data-theme="dark"] .section::before {
  background:
    radial-gradient(40% 34% at 14% 18%,
      rgba(214, 190, 156, 0.029)  0%,
      rgba(214, 190, 156, 0.027) 10%,
      rgba(214, 190, 156, 0.023) 20%,
      rgba(214, 190, 156, 0.018) 31%,
      rgba(214, 190, 156, 0.013) 42%,
      rgba(214, 190, 156, 0.008) 53%,
      rgba(214, 190, 156, 0.005) 64%,
      rgba(214, 190, 156, 0)     76%),
    radial-gradient(36% 32% at 86% 82%,
      rgba(120, 150, 180, 0.032)  0%,
      rgba(120, 150, 180, 0.030) 10%,
      rgba(120, 150, 180, 0.026) 20%,
      rgba(120, 150, 180, 0.020) 31%,
      rgba(120, 150, 180, 0.014) 42%,
      rgba(120, 150, 180, 0.009) 53%,
      rgba(120, 150, 180, 0.005) 64%,
      rgba(120, 150, 180, 0)     76%);
}
html[data-theme="dark"] .tone-b::before {
  background:
    radial-gradient(38% 32% at 88% 16%,
      rgba(214, 190, 156, 0.026)  0%,
      rgba(214, 190, 156, 0.024) 10%,
      rgba(214, 190, 156, 0.020) 20%,
      rgba(214, 190, 156, 0.017) 31%,
      rgba(214, 190, 156, 0.011) 42%,
      rgba(214, 190, 156, 0.008) 53%,
      rgba(214, 190, 156, 0.004) 64%,
      rgba(214, 190, 156, 0)     76%),
    radial-gradient(34% 30% at 12% 84%,
      rgba(120, 150, 180, 0.029)  0%,
      rgba(120, 150, 180, 0.027) 10%,
      rgba(120, 150, 180, 0.023) 20%,
      rgba(120, 150, 180, 0.018) 31%,
      rgba(120, 150, 180, 0.013) 42%,
      rgba(120, 150, 180, 0.008) 53%,
      rgba(120, 150, 180, 0.005) 64%,
      rgba(120, 150, 180, 0)     76%);
}
/* Same restraint in dark: barely a lift off the flat ground. At this alpha
   the ramp spans only ~3 of the 255 levels, so it cannot band regardless. */
html[data-theme="dark"] .tone-a::before {
  background:
    radial-gradient(40% 34% at 14% 18%,
      rgba(214, 190, 156, 0.008)  0%,
      rgba(214, 190, 156, 0.005) 35%,
      rgba(214, 190, 156, 0)     76%),
    radial-gradient(36% 32% at 86% 82%,
      rgba(120, 150, 180, 0.008)  0%,
      rgba(120, 150, 180, 0.005) 35%,
      rgba(120, 150, 180, 0)     76%);
}
/* Grain is the dither. On a near-black ground the blurred colour blobs shift
   luminance by only ~17 of the 255 available levels, spread over hundreds of
   pixels — roughly one 8-bit step every 30px, which reads as banding. Noise
   at this amplitude (~±11 levels) breaks the steps up. It has to be STRONGER
   in dark than in light, not weaker: light mode never bands, because the eye
   barely resolves quantisation steps at the top of the curve. */
html[data-theme="dark"] .hero::after,
html[data-theme="dark"] .section::after { opacity: 0.09; }
html[data-theme="dark"] .field input,
html[data-theme="dark"] .field textarea,
html[data-theme="dark"] .field select { background: rgba(255,255,255,0.045); }
html[data-theme="dark"] .field input:focus,
html[data-theme="dark"] .field textarea:focus,
html[data-theme="dark"] .field select:focus { background: rgba(255,255,255,0.07); }
html[data-theme="dark"] .btn--dark { background: var(--ink); color: #0F0E0D; }
html[data-theme="dark"] .btn--dark:hover { background: #DEDBD2; }
html[data-theme="dark"] .skip-link { background: var(--ink); color: #0F0E0D; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  html[data-theme="dark"] .site-head { background: rgba(12, 11, 10, 0.9); }
  html[data-theme="dark"] .mobile-nav { background: #121110; }
}
html[data-theme="dark"] .mobile-nav { background: rgba(16, 15, 14, 0.78); }

/* Smooth, quiet transition between themes — not a hard cut. */
body, .site-head, .mobile-nav, .card, .work-card, .force, .field input,
.field textarea, .field select {
  transition: background-color 0.25s var(--ease), color 0.25s var(--ease), border-color 0.25s var(--ease);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Same fill as the hero. The header is fully transparent, so whatever is
     behind it — the html canvas itself on overscroll/before body paints —
     has to match the hero tone, or it reads as a white bar under the blur. */
  background: var(--grey-hero);
}

/* The page carries the hero tone so the strip behind the sticky bar matches
   the hero instead of the sections below it. Everything after the hero
   repaints itself in the page tone. */
body {
  margin: 0;
  background: var(--grey-hero);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-base);
  line-height: 1.62;
  font-weight: 400;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

/* Headings are set in the serif; body copy stays in the sans. */
h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-optical-sizing: auto;
  letter-spacing: -0.012em;
  line-height: 1.12;
  text-wrap: balance;
}

p { margin: 0 0 1.05em; }
p:last-child { margin-bottom: 0; }

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

ul, ol { margin: 0; padding: 0; }

hr { border: 0; border-top: 1px solid var(--line); margin: 0; }

/* ---------- Focus & skip link ---------- */
:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 3px;
}
.skip-link {
  position: absolute;
  left: 16px;
  top: -100px;
  z-index: 200;
  background: var(--ink);
  color: #fff;
  padding: 10px 18px;
  border-radius: 999px;
  font-size: var(--fs-small);
  transition: top 0.2s var(--ease);
}
.skip-link:focus { top: 14px; }

/* Read aloud, never seen. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- Layout ---------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* Anchored sections must clear the sticky bar when jumped to. */
:target, [id] { scroll-margin-top: 96px; }

.section { padding-block: clamp(64px, 9vw, 132px); background: var(--grey-page); }
.section--tight { padding-block: clamp(48px, 6vw, 84px); }
/* Dividers retired in favour of tonal shifts between sections — see below. */
.section--line { border-top: none; }
.section--soft { background: rgba(18,18,17,0.022); }

/* ---------- Section tones ----------
   One tone per body — hero (grey), Selected work (white), The Ugaru Method
   (sand), Contact (a shade darker grey) — each close enough to its neighbour
   that the page shifts rather than breaks. Every section also carries a pair
   of very soft, very large blurred fields in colours barely apart from its
   own tone. */
.tone-a { background: #FAFAF8; }
.tone-b { background: #F9F7F3; }
.tone-g { background: #ECEAE5; }

.hero,
.section {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.hero::before,
.section::before {
  content: "";
  position: absolute;
  inset: -20%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(40% 34% at 14% 18%, rgba(214, 190, 156, 0.14), transparent 72%),
    radial-gradient(36% 32% at 86% 82%, rgba(196, 206, 214, 0.12), transparent 72%);
  filter: blur(95px);
}
.tone-b::before {
  background:
    radial-gradient(38% 32% at 88% 16%, rgba(214, 190, 156, 0.13), transparent 72%),
    radial-gradient(34% 30% at 12% 84%, rgba(196, 206, 214, 0.11), transparent 72%);
}
/* Selected work is the one section carrying real imagery. The colour fields
   are pulled almost to nothing here so the product screens are the only
   thing with hue on the page — a quarter of the usual strength, just enough
   that the section is not perfectly dead flat. */
.tone-a::before {
  background:
    radial-gradient(40% 34% at 14% 18%, rgba(214, 190, 156, 0.035), rgba(214, 190, 156, 0) 72%),
    radial-gradient(36% 32% at 86% 82%, rgba(196, 206, 214, 0.030), rgba(196, 206, 214, 0) 72%);
}
.hero > .wrap, .section > .wrap { position: relative; z-index: 1; }

/* Grain lives inside the gradients themselves, not as a layer over the whole
   page. A second, unblurred pseudo-element carries fine noise across each
   section's own box, so the soft colour blobs above read as grainy gradients
   rather than perfectly smooth airbrushed fields — the way film grain sits
   inside a colour, not on top of it as a separate texture. */
.hero::after,
.section::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='4' intercept='-1.5'/%3E%3CfeFuncG type='linear' slope='4' intercept='-1.5'/%3E%3CfeFuncB type='linear' slope='4' intercept='-1.5'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

.stack > * + * { margin-top: 22px; }
.measure { max-width: 66ch; }
.measure-s { max-width: 52ch; }

/* ---------- Type scale ---------- */

.h1  { font-size: var(--fs-h1); }
.h2  { font-size: var(--fs-h2); }
.h3  { font-size: var(--fs-h3); }
.h4  { font-size: var(--fs-h4); }

/* The name, glossed. A borrowed dictionary shape — the gloss describes the
   studio, not a Japanese word. See the note in README. */
.gloss {
  font-size: var(--fs-base-s);
  line-height: 1.5;
  color: var(--ink-3);
  margin: 0;
}

/* The claim. Sans, large, dark — the only thing at this size on the page. */
.claim {
  font-family: var(--sans);
  font-weight: 400;
  font-size: var(--fs-h3);
  line-height: 1.32;
  letter-spacing: -0.022em;
  color: var(--ink);
  max-width: min(100%, 445px);
  text-wrap: pretty;
}

.closing {
  display: grid;
  gap: 20px;
  justify-items: start;
  max-width: 56ch;
}

.lead {
  font-size: var(--fs-lead);
  line-height: 1.55;
  color: var(--ink-2);
  letter-spacing: -0.012em;
}
.body-lg { font-size: var(--fs-base-s); color: var(--ink-2); }
.muted { color: var(--ink-3); }
.small { font-size: var(--fs-small); line-height: 1.55; }
.eyebrow {
  font-size: var(--fs-caption);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin: 0;
}

.statement {
  font-size: var(--fs-display);
  line-height: 1.14;
  letter-spacing: -0.03em;
  font-weight: 500;
  max-width: 20ch;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 11px 22px;
  border-radius: 999px;
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: -0.008em;
  line-height: 1.2;
  border: 1px solid transparent;
  cursor: pointer;
  font-family: inherit;
  transition: background-color 0.22s var(--ease), color 0.22s var(--ease),
              border-color 0.22s var(--ease), transform 0.22s var(--ease);
}
.btn--dark { background: var(--ink); color: #FBFBF9; }
.btn--dark:hover { background: #2C2B28; }
.btn--ghost { border-color: var(--line-firm); color: var(--ink); background: transparent; }
.btn--ghost:hover { background: rgba(18,18,17,0.045); border-color: var(--ink); }
.btn--lg { padding: 14px 26px; font-size: var(--fs-base-s); }

.btn-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }

/* Arrow link */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--ink);
  border-bottom: 1px solid var(--line-firm);
  padding-bottom: 2px;
  transition: border-color 0.22s var(--ease), color 0.22s var(--ease);
}
.arrow-link .ar { transition: transform 0.22s var(--ease); display: inline-block; }
.arrow-link:hover { border-color: var(--ink); }
.arrow-link:hover .ar { transform: translateX(4px); }

/* ---------- Header ---------- */
/* Glass, not frosting: it blurs what passes underneath without washing it white. */
/* Transparent at every scroll position — the blur alone separates it from
   whatever is passing underneath. Nothing changes on scroll. */
/* The bar is an overlay, not a block in the flow: it floats directly over
   the hero from the very first pixel, so there is no separate "frame" above
   it for its background to disagree with. The hero supplies its own top
   padding to clear it (see .hero below). */
.site-head {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: transparent;
  backdrop-filter: saturate(160%) blur(16px);
  -webkit-backdrop-filter: saturate(160%) blur(16px);
  border: 0;
}

/* Where backdrop-filter is unavailable, fall back to a light tint so the
   navigation never sits unreadably on top of page content. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-head { background: rgba(247, 246, 244, 0.9); }
}

.head-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  min-height: 74px;
}
.brand {
  font-size: var(--fs-h5);
  font-weight: 600;
  letter-spacing: 0.11em;
  color: var(--ink);
  flex: none;
}
.nav-main {
  display: flex;
  gap: clamp(18px, 2.6vw, 38px);
  margin-left: auto;
}
.nav-main a {
  font-size: var(--fs-base-s);
  color: var(--ink-2);
  position: relative;
  padding-block: 4px;
  transition: color 0.2s var(--ease);
}
.nav-main a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.26s var(--ease);
}
.nav-main a:hover { color: var(--ink); }
.nav-main a:hover::after,
.nav-main a[aria-current]::after { transform: scaleX(1); }
.nav-main a[aria-current] { color: var(--ink); }

.head-actions { display: flex; align-items: center; gap: 14px; flex: none; }

/* ---------- Theme switch ---------- */
.theme-switch {
  display: inline-flex;
  align-items: center;
  width: 42px;
  height: 24px;
  padding: 2px;
  border-radius: 999px;
  border: 1px solid var(--line-firm);
  background: transparent;
  cursor: pointer;
  flex: none;
  transition: background-color 0.22s var(--ease), border-color 0.22s var(--ease);
}
.theme-switch-knob {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--bg);
  transform: translateX(0);
  transition: transform 0.26s var(--ease), background-color 0.22s var(--ease);
}
.theme-switch-knob svg { width: 12px; height: 12px; }
.theme-switch-knob .i-moon { display: none; }
html[data-theme="dark"] .theme-switch-knob { transform: translateX(18px); }
html[data-theme="dark"] .theme-switch-knob .i-sun { display: none; }
html[data-theme="dark"] .theme-switch-knob .i-moon { display: block; }
.mobile-nav .theme-switch { margin-top: 4px; }

.nav-toggle {
  display: none;
  width: 42px; height: 42px;
  border: 1px solid var(--line-firm);
  border-radius: 999px;
  background: transparent;
  cursor: pointer;
  padding: 0;
  align-items: center;
  justify-content: center;
}
.nav-toggle span {
  display: block;
  width: 16px; height: 1.5px;
  background: var(--ink);
  position: relative;
  transition: background-color 0.2s var(--ease);
}
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  position: absolute;
  left: 0;
  width: 16px; height: 1.5px;
  background: var(--ink);
  transition: transform 0.24s var(--ease);
}
.nav-toggle span::before { top: -5px; }
.nav-toggle span::after  { top: 5px; }
.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after  { transform: translateY(-5px) rotate(-45deg); }

.mobile-nav {
  display: none;
  border-top: 1px solid var(--line-soft);
  /* Denser than the bar: this one has to sit legibly over whole sections. */
  background: rgba(250, 250, 248, 0.72);
  backdrop-filter: saturate(180%) blur(22px);
  -webkit-backdrop-filter: saturate(180%) blur(22px);
  padding: 12px var(--gutter) 28px;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .mobile-nav { background: #FAFAF8; }
}
.mobile-nav.is-open { display: block; }
.mobile-nav a.mnav-item {
  display: block;
  padding: 15px 0;
  font-size: var(--fs-lead);
  letter-spacing: -0.02em;
  border-bottom: 1px solid var(--line-soft);
}
.mobile-nav .btn { margin-top: 22px; width: 100%; justify-content: center; }

/* Lock the page behind the open menu. */
html.nav-open, html.nav-open body { overflow: hidden; }

@media (max-width: 900px) {
  .nav-main { display: none; }
  .nav-toggle { display: inline-flex; }
  .head-actions .btn { display: none; }
}

/* ---------- Hero ---------- */
/* The hero sits on its own flat tone; the break at its foot is the only
   separation the page needs there. */
.hero {
  position: relative;
  background: var(--grey-hero);
  /* 74px clears the fixed bar (its own height); the rest is the hero's
     usual top breathing room. */
  padding-top: calc(74px + clamp(56px, 8vw, 108px));
  padding-bottom: clamp(56px, 7vw, 96px);
}

.kana {
  font-family: var(--jp);
  font-weight: 700;
  line-height: 0.86;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 clamp(26px, 3.5vw, 46px);
  user-select: none;
  white-space: nowrap;   /* three glyphs, one line, always */
  font-size: var(--fs-kana);
}
.kana--sm { font-size: var(--fs-kana-sm); margin: 0 0 26px; line-height: 1; }

/* Stacked hero (inner pages): left-aligned, no artwork. */
.hero--stacked .h1 { font-size: var(--fs-h1); max-width: 19ch; }
.hero--stacked .lead { max-width: 44ch; margin-top: 24px; }
.hero--stacked .btn-row { margin-top: 32px; gap: 22px; align-items: center; }

/* Split hero (home): copy left, layered artwork right.
   The kana leads the hierarchy; the H1 supports it. */
/* Home hero: kana, serif headline, the gloss, then the plain description.
   The right-hand space is the composition, not a gap to fill. */
.hero--home .kana {
  margin-bottom: clamp(14px, 1.8vw, 26px);
}
.hero--home .gloss { max-width: 52ch; }
.hero--home .claim { margin-top: clamp(20px, 2.2vw, 32px); }
/* Taller than a stacked-page hero: this is the cover, it can afford the
   extra air above and below the mark. */
.hero--home {
  padding-top: calc(74px + clamp(96px, 11vw, 160px));
  padding-bottom: clamp(88px, 10vw, 140px);
}

/* Home hero particle field — full-bleed background covering the whole hero
   section, edge to edge, top to bottom. The copy sits on top of it.
   Decorative (aria-hidden) — a simplified, audio-free port of the Manu Abascal
   player's Preset 15 simulator. */
.hero--home .hero-viz {
  position: absolute;
  inset: 0;                /* cover the entire hero section */
  z-index: 0;              /* above the ::before colour wash (-1), below .wrap (1) */
  pointer-events: none;    /* never intercept text selection or clicks */
}
.hero--home .hero-viz canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* Section subtitle, sans, sitting under a serif heading. */
.sub {
  font-size: var(--fs-lead);
  color: var(--ink-2);
  margin: 8px 0 0;
  max-width: 60ch;
}

/* ---------- Three forces ---------- */
.forces {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.4vw, 18px);
  margin-top: clamp(24px, 2.8vw, 36px);
}
.force {
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  padding: clamp(40px, 4.5vw, 56px) clamp(28px, 3vw, 36px);
  text-align: left;
  display: grid;
  justify-items: start;
  gap: 5px;
}
.force h3 { font-size: var(--fs-h4); margin-top: 14px; }
.force p { font-size: var(--fs-base-s); color: var(--ink-3); margin: 0; }

.ico {
  width: 34px; height: 34px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.1;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--ink);
  display: block;
}

/* ---------- Operations rail ---------- */
.opsline {
  margin: 36px 0 60px;
}
.opsline-rail {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(18px, 2vw, 30px);
  align-items: center;
  height: 12px;
  margin-bottom: clamp(26px, 3vw, 40px);
}
/* The line crosses the full card; the nodes sit on top of it. */
.opsline-rail::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 50%;
  height: 1px;
  background: var(--line);
}
.opsline-rail .node {
  position: relative;
  width: 11px; height: 11px;
  border: 1px solid var(--line-firm);
  border-radius: 50%;
  background: #FAFAF8;
  margin: 0 auto;
}
.opsline-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(18px, 2vw, 30px);
}
.opstep {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 4px;
}
.opstep h3 { font-size: var(--fs-h4); margin-top: 12px; }
.opstep p { font-size: var(--fs-base-s); color: var(--ink-3); margin: 0; }

.opsline-note {
  text-align: center;
  font-size: var(--fs-base-s);
  color: var(--ink-2);
  margin: clamp(26px, 3vw, 38px) 0 0;
}

@media (max-width: 860px) {
  .forces { grid-template-columns: 1fr; }
  .opsline-rail { display: none; }
  .opsline-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 34px; }
}
/* Two columns hold down to 380px; below that the labels start to wrap badly. */
@media (max-width: 379px) {
  .opsline-grid { grid-template-columns: 1fr; }
}

/* ---------- Cards ---------- */
.grid { display: grid; gap: clamp(14px, 1.6vw, 20px); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 980px) {
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .grid-2, .grid-3 { grid-template-columns: 1fr; }
}

.card {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-m);
  background: var(--glass);
  padding: clamp(22px, 2.4vw, 32px);
  display: flex;
  flex-direction: column;
  gap: 14px;
  transition: border-color 0.24s var(--ease), background-color 0.24s var(--ease),
              transform 0.24s var(--ease), box-shadow 0.24s var(--ease);
}
a.card:hover {
  border-color: var(--line-firm);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px -22px rgba(18,18,17,0.55);
}
/* ---------- Work cards ---------- */
/* Full-width, grey, flat — a placeholder for imagery until each product has
   real screens. No border ornament on the media block: just a change of
   grey, the same restraint as the rest of the page. */
.work-card {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-m);
  background: var(--surface);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
/* 4:5 — the Instagram portrait frame. Work imagery is shot and cropped for
   social first, so the card mirrors that ratio rather than asking for a
   separate landscape crop of every piece. (TikTok is 9:16; 4:5 is the
   portrait format that survives both without a second export.) */
.work-card-media {
  /* Matches the ratio the campaign artwork ships at, so the empty card and
     the filled one line up. Cards that DO have an image ignore this and take
     the asset's own ratio (see below) — this value only has to keep the
     placeholder honest. */
  aspect-ratio: 970 / 908;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line-soft);
  overflow: hidden;
}
/* A card with a single image takes that image's own ratio, so nothing is
   ever cropped and a new piece can ship at whatever ratio it was made in.
   The :not() is load-bearing: :has(img) scores (0,1,1) — the argument's
   specificity counts — which would otherwise beat the two-state variant's
   (0,1,0) and collapse its box to zero height, since its images are
   absolutely positioned and contribute no height of their own. */
.work-card-media:has(img):not(.work-card-media--swap) { aspect-ratio: auto; }

/* Two-state media: product at rest, campaign on hover.
   Once two images share the box they can no longer each define its height,
   so this variant goes back to the fixed frame and both assets are cut to
   match it — see the ratio on .work-card-media above. */
.work-card-media--swap {
  position: relative;
  aspect-ratio: 970 / 908;
}
.work-card-media--swap img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.work-card-media--swap .wc-hover { opacity: 0; }

/* Only where hover is a real input. On touch there is no hover state to
   reveal, so the card simply stays on the product image rather than
   flickering the campaign in on tap. */
@media (hover: hover) {
  .work-card-media--swap .wc-rest,
  .work-card-media--swap .wc-hover {
    transition: opacity 0.45s var(--ease);
  }
  .work-card:hover .wc-rest  { opacity: 0; }
  .work-card:hover .wc-hover { opacity: 1; }
}
.work-card-media img {
  width: 100%;
  height: auto;
  display: block;
}
.work-card-body {
  padding: clamp(20px, 2.2vw, 28px);
  display: grid;
  gap: 6px;
}
.work-card-body h3 { font-size: var(--fs-h5); }
.work-card-body p { margin: 0; font-size: var(--fs-small); }
/* Nothing to show yet: the media block reads as empty, not just grey. */
.work-card--soon .work-card-media {
  background: repeating-linear-gradient(
    135deg,
    var(--surface-2) 0px, var(--surface-2) 10px,
    var(--surface) 10px, var(--surface) 20px
  );
}
.work-card--soon .work-card-body h3 { color: var(--ink-3); }

/* Linked cards. The image swap already signals that something is under the
   cursor, so the frame only has to confirm it — the same restrained lift the
   other cards use, nothing more. */
a.work-card {
  color: inherit;
  transition: border-color 0.24s var(--ease), transform 0.24s var(--ease),
              box-shadow 0.24s var(--ease);
}
a.work-card:hover {
  border-color: var(--line-firm);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px -22px rgba(18,18,17,0.55);
}
a.work-card .arrow-link { margin-top: 4px; align-self: flex-start; }
a.work-card:hover .arrow-link { border-color: var(--ink); }
a.work-card:hover .arrow-link .ar { transform: translateX(4px); }
.work-card-body { align-content: start; }

.card .card-index {
  font-size: var(--fs-caption);
  letter-spacing: 0.1em;
  color: var(--ink-4);
  font-weight: 500;
}
.card p { color: var(--ink-2); font-size: var(--fs-base-s); }
.card .arrow-link { margin-top: auto; align-self: flex-start; }

/* ---------- Operations ---------- */
.ops {
  list-style: none;
  margin: clamp(26px, 3vw, 40px) 0 clamp(28px, 3vw, 38px);
}
.op {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: clamp(12px, 3vw, 40px);
  align-items: baseline;
  padding: clamp(15px, 1.8vw, 21px) 0;
}
.op-name { font-size: var(--fs-h5); font-weight: 500; letter-spacing: -0.02em; }
.op-line { color: var(--ink-2); font-size: var(--fs-base-s); }
/* Numbered variant: the marker needs far less room than a word does. */
.ops--tight .op {
  grid-template-columns: 46px minmax(0, 1fr);
  gap: clamp(6px, 1.5vw, 20px);
  padding: clamp(7.5px, 0.9vw, 10.5px) 0;
}
.ops--tight .op-name { font-size: var(--fs-caption); color: var(--ink-4); letter-spacing: 0.08em; }
.ops--tight .op-line { color: var(--ink); font-size: var(--fs-base); }
@media (max-width: 620px) { .op { grid-template-columns: 1fr; gap: 3px; } }

/* ---------- Numbered / definition lists ---------- */

.ticklist { list-style: none; display: grid; gap: 12px; }
.ticklist li {
  display: grid;
  grid-template-columns: 14px minmax(0,1fr);
  gap: 14px;
  align-items: start;
  color: var(--ink-2);
  font-size: var(--fs-base-s);
  line-height: 1.5;
}
.ticklist li::before {
  content: "";
  width: 9px; height: 1px;
  background: var(--line-firm);
  margin-top: 12px;
}

/* ---------- Split layout ---------- */
.split {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(28px, 5vw, 76px);
  align-items: start;
}
@media (max-width: 900px) { .split { grid-template-columns: 1fr; gap: 26px; } }

.sticky-head { position: sticky; top: 108px; }
@media (max-width: 900px) { .sticky-head { position: static; } }

/* ---------- Page header ---------- */
.page-head { padding-block: clamp(46px, 6vw, 92px) clamp(30px, 4vw, 52px); }
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-caption);
  color: var(--ink-3);
  margin-bottom: 26px;
  list-style: none;
}
.crumbs li { display: flex; align-items: center; gap: 8px; }
.crumbs a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.crumbs .sep { color: var(--ink-4); }

/* ---------- Quote / pull ---------- */
.pull {
  border-left: 1px solid var(--line-firm);
  padding-left: clamp(18px, 2.5vw, 30px);
  font-size: var(--fs-quote);
  line-height: 1.42;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-weight: 500;
  margin: clamp(26px, 3vw, 40px) 0;
}

/* ---------- Empty state ---------- */
.empty {
  border: 1px dashed var(--line-firm);
  border-radius: var(--radius-m);
  padding: clamp(30px, 4vw, 52px);
  max-width: 62ch;
}
.empty h2 { margin-bottom: 12px; }

/* ---------- Form ---------- */
.form { display: grid; gap: 20px; }
.field { display: grid; gap: 8px; }
.field label { font-size: var(--fs-small); font-weight: 500; color: var(--ink); }
.field .hint { font-size: var(--fs-caption); color: var(--ink-3); }
.field input,
.field textarea,
.field select {
  font-family: inherit;
  font-size: var(--fs-base-s);
  color: var(--ink);
  /* Inputs keep a faint fill: it is the affordance that says "type here". */
  background: rgba(255,255,255,0.42);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 12px 14px;
  width: 100%;
  transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease);
}
.field textarea { min-height: 118px; resize: vertical; }
.field input:hover, .field textarea:hover, .field select:hover { border-color: var(--line-firm); }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: 2px solid var(--ink);
  outline-offset: 1px;
  border-color: transparent;
  background: rgba(255,255,255,0.72);
}
.field .opt { color: var(--ink-4); font-weight: 400; }
.form-note { font-size: var(--fs-caption); color: var(--ink-3); }

/* Validation. #8A2B18 on the input background is ~7.4:1 — AA at any size. */
.field-error {
  font-size: var(--fs-caption);
  line-height: 1.45;
  color: #8A2B18;
  margin: 0;
  display: flex;
  gap: 7px;
  align-items: baseline;
}
.field-error::before { content: "—"; color: #8A2B18; flex: none; }
.field input.has-error,
.field textarea.has-error,
.field select.has-error {
  border-color: #8A2B18;
  background: rgba(138, 43, 24, 0.035);
}
/* Off-screen rather than display:none, so naive bots still see and fill it. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- Article ---------- */
.article-body { max-width: 68ch; font-size: var(--fs-base); }
.article-body h2 { font-size: var(--fs-h2); margin: 2em 0 0.6em; }
.article-body h3 { margin: 1.7em 0 0.5em; }
.article-body p { color: var(--ink-2); }
.article-meta {
  display: flex; flex-wrap: wrap; gap: 8px 18px;
  font-size: var(--fs-caption); color: var(--ink-3);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding-block: 14px;
  margin-block: 28px;
}

.tmpl-flag {
  border: 1px solid var(--line-firm);
  border-radius: var(--radius-s);
  padding: 14px 18px;
  font-size: var(--fs-small);
  color: var(--ink-2);
  margin-bottom: 30px;
}
.tmpl-flag strong { color: var(--ink); font-weight: 500; }
.ph {
  color: var(--ink-4);
  font-style: normal;
  border-bottom: 1px dashed var(--line-firm);
}

/* ---------- CTA band ---------- */
.cta-band {
  border-top: 1px solid var(--line);
  background: rgba(18,18,17,0.025);
}
.cta-inner {
  display: grid;
  justify-items: start;
  gap: 18px;
  max-width: 54ch;
  padding-block: clamp(52px, 7vw, 88px);
}
.cta-inner .btn { margin-top: 8px; }

/* ---------- Footer ----------
   No separate footer block: this is the tail of the Contact section, its
   own tone and padding inherited from there. */
.foot-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 30px;
  width: 100%;
  margin-top: clamp(40px, 5vw, 64px);
}
.foot-nav { display: flex; flex-wrap: wrap; gap: 12px 22px; }
.foot-nav a { font-size: var(--fs-small); color: var(--ink-2); }
.foot-nav a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 4px; }
.foot-meta { font-size: var(--fs-caption); color: var(--ink-3); margin: 0; }

/* ---------- Reveal ---------- */
.reveal { opacity: 0; transform: translateY(14px); }
.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1 !important; transform: none !important; }
}

/* ---------- Print ---------- */
@media print {
  .site-head, .mobile-nav, .cta-band { display: none; }
  body { background: #fff; }
}
