/* ============================================================
   MATÉRIA — Demo Design
   Structure & motion recreation. Placeholder content only.
   ============================================================ */

/* ---------- Design tokens ---------- */
:root {
  --base-vw: 1920;               /* PC comp base width */

  /* colours */
  --color-main: #000000;
  --color-sub: #ffffff;
  --color-gray: #e6e6e6;
  --color-gray-dark: #666666;
  --color-gradient-dark: #0f0a72;
  --color-gradient: #0166c9;
  --color-gradient-light: #a1e0e7;

  /* fonts */
  --font-jakarta: YakuhanJP, "Helvetica Neue", "Kumbh Sans", "Zen Kaku Gothic New", sans-serif;
  --font-zen: YakuhanJP, "Zen Kaku Gothic New", sans-serif;
  --font-serif: "Noto Serif Display", serif;

  /* type scale (unit = vw-linked rem = comp px) */
  /* readability floor: 16px minimum for all small text, site-wide */
  --fs-1: 16rem;  --fs-2: 16rem;  --fs-3: 16rem;  --fs-4: 16rem;
  --fs-5: 16rem;  --fs-6: 18rem;  --fs-7: 21rem;  --fs-8: 26rem;
  --fs-9: 32rem;  --fs-10: 43rem; --fs-11: 64rem; --fs-12: 85rem;
  --fs-13: 128rem; --fs-14: 256rem;

  /* motion */
  --duration: 0.8s;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
}

@media (width < 992px) { :root { --base-vw: 992; } }
@media (width < 768px) { :root { --base-vw: 430; } }

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

html {
  font-size: calc(100 / var(--base-vw) * 1vw);   /* vw-linked rem */
  font-family: var(--font-jakarta);
  font-weight: 300;
  font-feature-settings: "palt";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: var(--color-main);
  color: var(--color-sub);
  scroll-behavior: auto;
}
/* MOBILE / COMPACT-LANDSCAPE UNIT CAP
   Below 768px the vw-linked rem above keeps growing right up to the 768px
   boundary (e.g. 1.78px/rem at 767px width) then snaps down ~2.3x at the
   768px breakpoint — every rem-sized element (type, spacing, hero height)
   swells continuously as width grows, and lands on a jarring cliff at the
   boundary. Landscape phones (e.g. 667×375, 844×390) made this worse: a
   short/wide viewport that still matches "width<768" scales rem toward its
   width, not its (much smaller) height, so the whole hero could balloon to
   several screens tall. Capping at 1px/rem — exactly the multiplier the
   430-wide comp baseline reaches at 430px width — means mobile/compact
   layouts never scale ABOVE their authored (comp) size, only down for
   narrower phones. `(hover:none) and (orientation:landscape) and
   (max-height:500px)` pulls short-landscape touch devices into this same
   bracket regardless of their width, so an 844-wide landscape phone still
   gets the capped, 3-column mobile layout instead of the 6-column tablet
   grid meant for genuinely wide/tall surfaces. */
@media (width < 768px), (hover: none) and (orientation: landscape) and (max-height: 500px) {
  :root { --base-vw: 430; }
  html { font-size: min(calc(100 / 430 * 1vw), 1px); }
}
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body { overflow-x: hidden; position: relative; min-height: 100vh; }

img, picture, canvas, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
em { font-style: normal; }

/* Hide native cursor when custom cursor is active */
body.has-cursor, body.has-cursor a, body.has-cursor button { cursor: none; }

/* ---------- Base grid (6 columns: 135 | 412×4 | 135) ---------- */
.base-grid {
  display: grid;
  grid-template-columns: 135rem repeat(4, 412rem) 135rem;
  position: relative;
  --grid-line: rgba(255,255,255,0.12);
}
/* design-grid vertical rules as decoration */
.base-grid::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: repeating-linear-gradient(
    to right,
    transparent 0,
    transparent calc(135rem - 0.5px),
    var(--grid-line) calc(135rem - 0.5px),
    var(--grid-line) 135rem,
    transparent 135rem,
    transparent calc(135rem + 412rem - 0.5px),
    var(--grid-line) calc(135rem + 412rem - 0.5px),
    var(--grid-line) calc(135rem + 412rem),
    transparent calc(135rem + 412rem)
  );
  transition: background-image var(--duration) var(--ease);
}
.base-grid > * { position: relative; z-index: 1; }
/* Grid/flex items default to a content-based min-width (roughly the widest
   unbreakable run inside them), which can silently force a track — and the
   whole page — wider than the viewport when that content is an unbreakable
   phrase span (see .phrase below) or a long label. min-width:0 lets the
   track honor its authored size instead and the content wrap/clip within
   it, so a single long phrase can never push the page into horizontal
   scroll. */
.base-grid > *,
.partner__meta,
.service__meta,
.benefit__text,
.faq__q > span:first-child,
.contact__label { min-width: 0; }

/* ---------- Fixed UI (four corners) ---------- */
.fixed-ui {
  position: fixed;
  z-index: 500;
  font-size: var(--fs-4);
  font-weight: 300;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-sub);
  mix-blend-mode: difference;
}
.fixed-ui--logo { left: 0; top: 0; margin: 20rem; line-height: 0; }
.logo-img {
  display: block;
  height: 18rem;          /* wordmark height; width scales by ratio */
  width: auto;
  filter: invert(1);      /* black glyphs -> white, so difference blend stays adaptive */
}
.fixed-ui--menu { right: 0; top: 0; margin: 20rem; }
.fixed-ui--scroll { left: 0; bottom: 0; margin: 20rem; font-variant-numeric: tabular-nums; }
.fixed-ui--top { right: 0; bottom: 0; margin: 20rem; }

/* ---------- Custom cursor ---------- */
.cursor {
  position: fixed;
  top: 0; left: 0;
  width: 40rem; height: 40rem;
  margin: -20rem 0 0 -20rem;
  border-radius: 50%;
  background: rgba(230,230,230,0.15);
  border: 0.5px solid rgba(255,255,255,0.5);
  display: grid; place-items: center;
  pointer-events: none;
  z-index: 9999;
  transform: translate3d(0,0,0) scale(1);
  transition: width .3s var(--ease), height .3s var(--ease),
              background .3s var(--ease), opacity .3s var(--ease);
  will-change: transform;
  opacity: 0;
}
.cursor__plus { font-size: var(--fs-4); line-height: 1; opacity: .8; }
.cursor.is-active {
  width: 90rem; height: 90rem;
  margin: -45rem 0 0 -45rem;
  background: rgba(1,102,201,0.2);
}
.cursor.is-visible { opacity: 1; }
@media (hover: none) { .cursor { display: none; } }

/* ---------- Loader ---------- */
.loader {
  position: fixed; inset: 0; z-index: 9000;
  background: var(--color-main);
  display: grid; place-items: center;
}
.loader__inner { text-align: center; }
.loader__counter {
  display: flex; align-items: baseline; justify-content: center;
  font-size: var(--fs-12);
  font-weight: 300;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.digit { display: inline-block; height: 1em; overflow: hidden; }
.digit__reel { display: flex; flex-direction: column; will-change: transform; }
/* Each row is flex-centered rather than relying on line-height:1 to land the
   glyph exactly inside its 1em box. line-height:1 assumes the font's own
   ascent+descent sums to 1em, which isn't true for every font/renderer —
   on some mobile browsers (seen on iOS Safari) the digit's ink sat above
   the 1em box and got clipped by .digit's overflow:hidden, e.g. "100"
   rendering with only the bottom half of each "0" visible ("100" -> "l UU").
   Centering the glyph inside its own row keeps any leftover mismatch
   symmetric (and therefore far less visible) instead of clipping one side. */
.digit__reel > i {
  font-style: normal;
  height: 1em;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.digit__pct { font-size: var(--fs-8); margin-left: 8rem; opacity: .6; }
.loader__label {
  margin-top: 30rem; font-size: var(--fs-4);
  letter-spacing: 0.2em; opacity: .5; text-transform: uppercase;
}
.loader.is-done { pointer-events: none; }

/* ---------- Section label ---------- */
/* Noto Serif Display is loaded italic-only (ital,wght@1,300;1,400) for the
   hero/reel/cta em accents elsewhere on the page — added the upright
   0,400 face to the Google Fonts request specifically so these labels
   can render normal (non-italic) instead of falling back to a
   browser-synthesized or substituted face. */
.section-label {
  display: inline-block;
  font-family: var(--font-serif);
  font-style: normal;
  font-weight: 400;
  font-size: var(--fs-4);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-sub);
}

/* ---------- Link-plus style ---------- */
.link-plus {
  display: inline-flex; align-items: baseline; gap: 6rem;
  font-size: var(--fs-5);
  letter-spacing: 0.08em; text-transform: uppercase;
  padding-bottom: 4rem;
  border-bottom: 0.5px solid rgba(255,255,255,0.4);
  transition: gap .4s var(--ease), border-color .4s var(--ease);
}
.link-plus span { color: var(--color-gradient-light); }
.link-plus:hover { gap: 16rem; border-color: var(--color-sub); }

/* ---------- Japanese copy: line-breaking ---------- */
/* `line-break:strict` applies the stricter kinsoku set (e.g. a line may not
   start with 。 or ？), and `word-break:normal` keeps the standard CJK
   behaviour of wrapping between characters — appropriate for body text, but
   for short, meaning-carrying phrases (compound nouns, question endings,
   CTA labels) an arbitrary character-boundary break reads as a mistake.
   `word-break:auto-phrase` (Chrome/Edge) upgrades the same baseline with
   dictionary-aware phrase boundaries; where it isn't supported yet, the
   .phrase spans wrapped around specific must-not-split terms in the HTML
   (see index.html/contact.html) guarantee those never break regardless of
   browser support. Deliberately no overflow-wrap:anywhere here — that
   allows breaking anywhere (including inside a phrase span) and was masking
   a real overflow rather than fixing the wrap. */
/* .hello__ja was the one Japanese paragraph missing from these lists, so it
   fell back to the CJK default of "break anywhere" and split words mid-token
   (素材・技 / 術, 商品やブラ / ンド, 共同開 / 発型). Adding it here routes it
   through the same phrase-aware wrapping as every other Japanese block. */
.intro__ja, .intro__en,
.hello__ja,
.overview__text,
.partner__lead, .partner__name, .partner__desc,
.benefit__text,
.service__statement .line:last-child,
.founder__quote, .founder__text p,
.faq__q > span:first-child, .faq__a,
.contact__lead, .contact__label,
.contact-form__lead, .contact-form__note {
  line-break: strict;
  word-break: normal;
  overflow-wrap: normal;
}
/* text-wrap:pretty / word-break:auto-phrase are scoped to the blocks that
   are NOT flush-both-edges justified (see the 両端揃え rule further down).
   Phrase-aware wrapping and `pretty` both deliberately end lines EARLY —
   they stop at a phrase boundary and bias toward shorter lines. That is
   the right call for ragged-right text, but it is exactly wrong under
   inter-character justify: whatever room the phrase-aware breaker leaves
   at the end of a line gets redistributed as letter-spacing across only
   the characters on that line, so a line that stops 4-5 characters short
   renders as 「そ の 価 値 を 十 分 に」 with huge gaps. Justified CJK
   instead wants each line filled as completely as possible — which is
   the CJK default (word-break: normal breaks between characters), with
   line-break: strict supplying the kinsoku rules so 、。・ small kana
   and the like never land at a line head. The .phrase / .nb nowrap spans
   still protect the specific katakana/compound terms that must not split.
   REVERT: add the justified selectors back into both lists below. */
@supports (text-wrap: pretty) {
  .intro__ja, .intro__en,
  .partner__name,
  .service__statement .line:last-child,
  .founder__quote,
  .faq__q > span:first-child,
  .contact__label { text-wrap: pretty; }
}
@supports (word-break: auto-phrase) {
  .intro__ja, .intro__en,
  .partner__name,
  .service__statement .line:last-child,
  .founder__quote,
  .faq__q > span:first-child,
  .contact__label { word-break: auto-phrase; }
}
/* Hard guarantee for the specific short terms listed in the mobile-QA
   brief: a nowrap run never breaks internally, so these stay intact even
   in browsers without auto-phrase support.
   Was `display: inline-block` — under text-justify:inter-character
   (see the shared flush-both-edges rule further down) an inline-block is
   justified as a single atomic unit, so the justifier has fewer gaps to
   distribute a line's stretch across and dumps it all into whatever's
   left, producing grotesque gaps around/after the phrase (visible as
   "新しい事業　を　つ　く　る" — huge gaps between ordinary characters on
   a line containing a .phrase span). white-space:nowrap gives the same
   no-internal-break guarantee while staying a normal inline run, so
   inter-character justify can still distribute space through it char by
   char like any other text. REVERT: `display: inline-block;` to restore
   the pre-justify behavior. */
.phrase { white-space: nowrap; }

/* Same technique, used to pin the meaning units inside .hello__ja
   (素材・技術・ / 商品やブランド / 共同開発型 / 技術を保存する /
   更新する / つくる力 / 届ける力) and the equivalent compounds in the
   body paragraphs (商品・ブランド / 市場・世代 / ものづくり /
   という現状 / 「MATÉRIA」を). */
.nb { white-space: nowrap; }

/* Manually-authored line break, mobile only (see the @media (width < 768px)
   block for the paired `display: inline` override). Auto-wrap under the
   flush-both-edges justify rule below can leave short, unevenly-stretched
   lines (greedy wrap ignoring comma/phrase pauses, or wide .phrase/.nb
   atomic units eating a line's remaining space) — a <br class="mobile-break">
   forces the break at a chosen, sense-bearing point instead. Per the CSS
   justify spec, a line immediately before a forced break (or the last line
   of a block) is never stretched, so every manually-broken line reads at
   its natural width. Hidden by default so desktop/tablet — which have
   much more room per line — are unaffected. Used in: .partner__lead only.
   (Briefly also on .benefit__text / .founder__text p — removed once the
   any-char-wrap fix below made their auto-wrap set evenly on its own; a
   hard break there was papering over the wrapping rules, not fixing them.) */
.mobile-break { display: none; }

/* One word of a [data-decode] heading during its scramble reveal. main.js
   pins an inline width on each of these (measured from the real text), so
   the random glyphs can never re-wrap the heading onto an extra line.
   nowrap keeps a cell on one line; no overflow property is set on purpose —
   `overflow: hidden` on an inline-block moves its baseline to the bottom
   margin edge, which would drop the whole line mid-animation. */
.decode-cell {
  display: inline-block;
  white-space: nowrap;
}

/* text-wrap:balance is intentionally NOT used on the justified paragraphs.
   It equalises line lengths by making EVERY line shorter than it needs to
   be — under inter-character justify each of those shortened lines is then
   stretched back out to the full column width, so balancing actively
   manufactures the uneven letter-spacing it looks like it should fix. With
   the flush-both-edges rule below, all lines already end at the same right
   edge, which is what balance was reaching for in the first place.
   (Previously applied to .overview__text and .founder__text p.) */

/* ---------- Flush-both-edges Japanese body copy (両端揃え) ----------
   Right edge flush across every line, not just left-aligned ragged-right
   — inter-character justify redistributes letter-spacing per line to
   reach the edge (CJK has no word gaps to stretch instead), the same
   trade professional Japanese typesetting makes for justified text.
   Scoped to paragraphs that actually wrap to 2+ lines and read as prose;
   excluded: short single-line labels/headings (.partner__name, .faq__q,
   .contact__label) and pull quotes (.founder__quote) where justify would
   have nothing to do or would just look like a stray declaration, plus
   .intro__ja / .intro__en which are authored as manual pre-line
   statement breaks rather than a wrapped paragraph.
   REVERT: delete this rule to go back to ragged-right body copy
   (.hello__ja loses its own copy of this — see its rule above — so
   reverting there too needs text-align/text-justify added back). */
.hello__ja,
.overview__text,
.partner__lead, .partner__desc,
.benefit__text,
.founder__text p,
.faq__a p,
.contact__lead,
.contact-form__lead, .contact-form__note {
  text-align: justify;
  text-justify: inter-character;
}

/* ...and release the .nb / .phrase nowrap pins INSIDE those justified
   paragraphs. Those spans exist to stop phrase-aware wrapping from
   splitting a meaning unit across lines — a real problem when lines end
   at phrase boundaries. Under justify they invert into the cause of the
   defect: a pinned 5-7 character run that doesn't fit gets pushed whole
   to the next line, and the 5-7 characters of room it leaves behind are
   redistributed as letter-spacing across that line alone. Measured
   letter-spacing variance within a paragraph (px, lower = more even),
   at 390px:
                       before   any-char wrap   + pins released
     .hello__ja         11.8        12.8              3.3
     .partner__desc     14.8         1.2              1.2
     .benefit__text     10.6         0                0
     .founder__text p    5.6         0                0
   The pins are kept for the NON-justified blocks (.intro__ja,
   .partner__name, .contact__label, .service__statement, .founder__quote),
   which still wrap phrase-aware and still need them.
   REVERT: delete this rule to re-pin the spans everywhere. */
.hello__ja :is(.nb, .phrase),
.overview__text :is(.nb, .phrase),
.partner__lead :is(.nb, .phrase),
.partner__desc :is(.nb, .phrase),
.benefit__text :is(.nb, .phrase),
.founder__text p :is(.nb, .phrase),
.faq__a p :is(.nb, .phrase),
.contact__lead :is(.nb, .phrase),
.contact-form__lead :is(.nb, .phrase),
.contact-form__note :is(.nb, .phrase) {
  white-space: normal;
}

/* Anchor navigation (menu links, Contact category links, a #contact-form
   URL on load) must not land a section's label/heading under the fixed
   logo/menu. scroll-margin-top is honoured natively by scrollIntoView()
   and by the browser's own initial #hash scroll on page load; main.js's
   scrollTo() also reads this same value for the Lenis-driven path so both
   land at the same position. */
main section,
footer { scroll-margin-top: calc(72px + env(safe-area-inset-top)); }

/* ---------- Reveal primitives ---------- */
[data-reveal] .line,
[data-reveal].line { overflow: hidden; }
.line { display: block; }
.line > .line__inner, .word, .char { display: inline-block; }

/* ---------- Large multi-line heading reveal (mask rise) ---------- */
/* Outer mask: clips the line to its own box. Inner: the animated glyphs.
   Hidden-by-default here (not just via JS) so nothing flashes visible
   before GSAP attaches — prefers-reduced-motion below removes the hidden
   state entirely so screen readers / no-motion users see final text.
   Deliberately NO `transform` here: GSAP's yPercent/skewY treat a
   CSS-authored starting transform as a permanent additive baseline it
   can never fully clear (yPercent:0 still leaves the CSS translateY(110%)
   as a residual offset), so the very first transform this element ever
   gets must come from GSAP itself (see playHeadingLines/initHeroIntro),
   with opacity alone keeping it invisible until JS attaches. */
.heading-line { display: block; overflow: hidden; }
.heading-line-inner {
  display: block;
  opacity: 0;
  filter: blur(2px);
}
@media (prefers-reduced-motion: reduce) {
  .heading-line-inner { opacity: 1; filter: none; }
}

/* ============================================================
   SEC-1 HERO
   ============================================================ */
.hero {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  display: flex;
  align-items: center;
  /* first view inverted: white ground, black type */
  background-color: var(--color-sub);   /* white */
  color: var(--color-main);             /* black */
  /* same flip transition as the footer (see .footer / .footer.is-light) —
     smoothly fades to black as the section scrolls out of view instead of
     cutting hard into the black section below it */
  transition: background-color var(--duration) var(--ease),
              color var(--duration) var(--ease);
}
/* First-view background image — sits at the very back (behind the particle
   logo field and the design grid). Fades out with the section's flip-to-black. */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;                     /* behind .hero__particles (also 0, but later in DOM) and .hero__grid (1) */
  background: url("../img/hero-bg-2.webp") center center / cover no-repeat;
  pointer-events: none;
  transition: opacity var(--duration) var(--ease);
  /* Deepen the image itself rather than re-export it — "B" of a 3-step
     saturate/contrast/brightness comparison shown to the user (A 1.35/1.06/0.97,
     B 1.75/1.12/0.93, C 2.2/1.2/0.86). Chosen as the balance point: color reads
     clearly without eating into the black hero copy's contrast.
     REVERT: delete the filter line to go back to the flat source image. */
  filter: saturate(1.75) contrast(1.12) brightness(0.93);
}
.hero.is-dark .hero__bg { opacity: 0; }   /* clear as the hero flips to black on scroll-out */
.hero .base-grid { --grid-line: rgba(0,0,0,0.12); }   /* dark design-grid rules on white */
.hero.is-dark {
  background-color: var(--color-main);   /* back to black */
  color: var(--color-sub);
}
.hero.is-dark .base-grid { --grid-line: rgba(255,255,255,0.12); }
/* MATÉRIA logo particle field — sits above the white ground + design-grid
   rules, below the copy/labels (which live in .hero__grid, z-index 1). */
.hero__particles {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;   /* never blocks hover / custom cursor / copy */
}
.hero__grid {
  width: 100%;
  min-height: 100svh;
  align-content: center;
  padding: 120rem 0;
  z-index: 1;
}
.hero__lead {
  grid-column: 4 / 6;
  align-self: start;
  /* pulled up so the lead paragraph clears the centered MATÉRIA particle
     logo band instead of overlapping it */
  margin-top: -125rem;
  font-size: var(--fs-4);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.7;
  color: var(--color-gray-dark);
}
.hero__label {
  position: absolute;
  font-size: var(--fs-4);
  letter-spacing: 0.06em;
  color: var(--color-gray-dark);
}
.hero__label.q1 { top: 22%; left: 32%; }
.hero__label.q2 { top: 30%; right: 12%; }
.hero__label.q3 { bottom: 34%; left: 14%; }
.hero__label.q4 { bottom: 24%; right: 28%; }
.hero__copy {
  grid-column: 2 / 6;
  align-self: end;
  font-size: var(--fs-11);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.01em;
  /* drop the copy into the lower third so it clears the centered particle
     logo band; svh keeps the offset proportional to viewport height.
     Nudged down slightly further so the first line clears the {BRAND}
     quadrant label (q3) instead of overlapping it. */
  transform: translateY(30svh);
}
.hero__copy em { font-family: var(--font-serif); font-style: italic; font-weight: 400; }

/* ============================================================
   SEC-1.5 INTRO STATEMENT (sits above About)
   ============================================================ */
.intro { margin-top: 200rem; padding-bottom: 40rem; row-gap: 32rem; }
@media (width >= 992px) { .intro { margin-top: 280rem; } }
.intro__ja {
  grid-column: 2 / 6;
  font-family: var(--font-zen);
  font-weight: 700;
  font-size: 64rem;
  line-height: 1.7;
  letter-spacing: 0.01em;
  white-space: pre-line;   /* respect the literal line breaks authored in the HTML */
  /* word-break/line-break: see the shared Japanese-copy rules near the top
     of this file — word-break:keep-all was removed from here because it
     makes each authored (space-less) CJK line fully unbreakable, which
     overflowed narrow columns instead of wrapping; word-break:normal +
     line-break:strict (+ auto-phrase where supported) wraps normally while
     still avoiding the worst mid-phrase breaks. */
}
/* .intro__ja's font-size is expressed in comp-px (1920 basis) via the site's
   vw-linked rem, but the base-grid's column width is authored in DIFFERENT
   rem values per breakpoint (135/412 desktop, 60/218 mid, 40/1fr mobile) —
   so the same 64rem that fits one line at 1920 no longer fits the narrower
   mid/mobile columns and wraps mid-phrase. Scale it down at each bracket. */
@media (768px <= width < 992px) { .intro__ja { font-size: 40rem; } }
@media (width < 768px) { .intro__ja { font-size: 28rem; } }
.intro__en {
  grid-column: 2 / 6;
  font-family: var(--font-jakarta);
  font-weight: 300;
  font-size: 24rem;
  line-height: 1.8;
  letter-spacing: 0.02em;
  color: var(--color-sub);
  white-space: pre-line;
}

/* ============================================================
   INTERSTITIAL PHOTOS (drop into the runs between sections)
   ============================================================
   Reusable: add <figure class="interstitial base-grid"> anywhere between
   two sections. The image rides the shared .base-grid track, so its
   left/right edges always land on the same gutters as every section's
   text (2/6 desktop, 2/3 mobile — see the mobile block) and follow
   --sp-content-padding automatically if that value ever changes. */
/* overflow:hidden so the focus-settle reveal (data-reveal-img, see main.js)
   can scale its <img> in from 1.08x without spilling past the photo's own
   frame — the clip boundary doubles as the "frame" the image zooms into. */
.interstitial { margin: 0; overflow: hidden; }
.interstitial__img {
  /* Left edge stays flush with the first design-grid rule (135rem — the same
     flush-left as every section's text). The right edge is set by width, not
     by a grid line, because the DRAWN rules and the grid's column lines are
     not the same thing:
       .base-grid columns : 135 / 547 /  959 / 1371 / 1783rem
       .base-grid::before : 135 / 547 /  682 / 1094 / 1229rem
     the decoration is a repeating gradient whose unit is 135+412=547rem, so
     after the second rule it lays down pairs (547k+135, 547k+547) and stops
     lining up with the tracks. The 4th VISIBLE rule sits at 1094rem, so the
     photo runs 135 -> 1094rem = 959rem wide (719px at 1440).
     Mobile keeps grid-column 2/3 at width:100% (see the mobile block); the
     rules aren't drawn below 992px, so the fixed width is scoped to ≥992px.
     REVERT: drop the 959rem width rule below for a photo that fills the
     content column. */
  grid-column: 2 / 6;
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
@media (width >= 992px) {
  .interstitial__img { width: 959rem; }
}
/* 3:2 source (e.g. Photo5 at 5472x3648) — declared rather than letting the
   file's own ratio drive layout, so the box is reserved before it loads. */
.interstitial--wide .interstitial__img { aspect-ratio: 3 / 2; }

/* Vertical rhythm: 96rem of air above AND below every interstitial photo.
   The section gaps these photos sit in are deliberately large (200-320rem)
   because they separate *sections*; a photo dropped into the middle of one
   would otherwise float in ~180px of dead space on each side. These rules
   only fire when a photo is actually adjacent, so sections without one keep
   their original spacing untouched — on desktop too, since the sibling
   selectors outrank the plain `.overview` / `.partner` margin rules.
   The preceding section's own bottom padding is subtracted so the measured
   gap from the last line of text to the photo matches the gap below it:
     .intro ends with  40rem padding -> +56rem here  = 96rem
     .hello ends with 120rem padding -> trimmed to     96rem
   REVERT: delete this group to put the photos back in the middle of the
   full section gap. */
/* Desktop/tablet air around a photo: 72rem total on each side (54px at
   1440px). Scaled down from the previous 96rem alongside the image itself —
   the photo is now three quarters as wide, so keeping the old gap would have
   left it floating in noticeably more space than before.
   .intro contributes its own 40rem of bottom padding here, hence 32rem. */
.intro + .interstitial { margin-top: 32rem; }
.hello:has(+ .interstitial) { padding-bottom: 72rem; }
.interstitial + .overview,
.interstitial + .partner { margin-top: 72rem; }

/* ---- shared section rhythm (2/2): the photo interstitials ----
   Mobile only. These override the group above: a photo sitting inside a
   section boundary reads better with less air than a section-to-section
   break, and both sides of each photo must match. --sp-photo-gap is the
   single source for all four edges.
   .intro's bottom padding is zeroed by the rule above, so the full gap is
   carried by the figure's own margin here (rather than the 56rem that used
   to top up .intro's 40rem). */
@media (width < 768px), (hover: none) and (orientation: landscape) and (max-height: 500px) {
  .intro + .interstitial { margin-top: var(--sp-photo-gap); }
  .hello:has(+ .interstitial) { padding-bottom: var(--sp-photo-gap); }
  .interstitial + .overview,
  .interstitial + .partner { margin-top: var(--sp-photo-gap); }
}

/* ============================================================
   SEC-1.7 OVERVIEW (Who / What / How — sits above About)
   ============================================================ */
.overview { margin-top: 200rem; row-gap: 48rem; }
@media (width >= 992px) { .overview { margin-top: 280rem; } }
.overview .section-label { grid-column: 2 / 3; }
.overview__grid {
  grid-column: 2 / 6;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 72rem;
}
.overview__col {
  border-top: 0.5px solid rgba(255,255,255,0.2);
  padding-top: 40rem;
  display: flex;
  flex-direction: column;
}
.overview__col:not(:first-child) {
  border-left: 0.5px solid rgba(255,255,255,0.15);
  padding-left: 40rem;
}
.overview__title {
  font-size: 64rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--color-sub);
  margin-bottom: 32rem;
}
.overview__text {
  font-family: var(--font-zen);
  font-size: var(--fs-6);
  line-height: 2;
  color: var(--color-sub);
}
/* motion pictogram — pinned to the bottom of the column so all three
   bottom-align on the same line regardless of how many lines the text
   above wraps to; the column's border-top/border-left rules stretch to
   match automatically since grid rows stretch to the tallest column */
.overview__stage {
  width: 100%;
  height: 312rem; /* 240rem × 1.3 */
  margin-top: auto;
  padding-top: 52rem; /* 40rem × 1.3, scaled to keep balance with the taller pictogram */
  cursor: grab;
  touch-action: none;
}
.overview__stage:active { cursor: grabbing; }

/* ============================================================
   SEC-2 HELLO
   ============================================================ */
.hello { margin-top: 280rem; padding-bottom: 120rem; }
@media (width >= 992px) { .hello { margin-top: 400rem; } }
.hello .section-label { grid-column: 2 / 3; }
.hello__title {
  grid-column: 2 / 6;
  font-size: var(--fs-11);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  margin: 20rem 0 72rem;
}
/* fills the empty design-column to the left of the body copy (col 2, the
   same row as .hello__body — grid auto-placement slots it there since
   col 2 is otherwise unused on that row) */
.hello__pict {
  grid-column: 2 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.hello__stage { width: 100%; max-width: 320rem; aspect-ratio: 1 / 1; }
.hello__body { grid-column: 3 / 6; display: grid; gap: 48rem; max-width: 1400rem; }
.hello__en {
  font-size: var(--fs-8);
  line-height: 1.4; font-weight: 300;
}
.hello__ja {
  font-family: var(--font-zen);
  font-size: 36rem;
  font-weight: 700;
  line-height: 2; color: var(--color-sub);
  letter-spacing: 0.01em;
  /* text-align/text-justify: see the shared "flush-both-edges Japanese
     body copy" rule below the shared Japanese-copy wrap rules — this is
     the paragraph that rule was written for. */
}
.hello__body .link-plus { justify-self: start; }

/* ============================================================
   SEC-3 PLAY REEL
   ============================================================ */
.reel {
  position: relative;
  min-height: 100vh;
  display: grid;
  place-content: center;
  gap: 80rem;
  padding: 200rem 135rem;
  overflow: hidden;
}
.reel__copy {
  font-size: var(--fs-10);
  font-weight: 300;
  line-height: 1.15;
  text-align: center;
  max-width: 1400rem;
  z-index: 2;
}
.reel__copy em { font-family: var(--font-serif); font-style: italic; }
.reel__thumbs { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.reel__thumb {
  position: absolute;
  width: 160rem;
  height: calc(var(--h) * 1rem * 0.6);
  background: linear-gradient(140deg, var(--color-gradient-dark), var(--color-gradient));
  border-radius: 4rem;
  opacity: 0;
  will-change: transform, opacity;
}
.reel__player {
  z-index: 2;
  display: grid; gap: 24rem;
  justify-items: center;
  width: 1100rem; max-width: 90vw;
}
.reel__video {
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0.5px solid rgba(255,255,255,0.2);
  border-radius: 4rem;
  overflow: hidden;
}
.reel__placeholder {
  width: 100%; height: 100%;
  display: grid; place-content: center; gap: 12rem;
  text-align: center;
  background: radial-gradient(120% 120% at 30% 30%,
    rgba(1,102,201,0.35), rgba(15,10,114,0.5) 60%, #05030f 100%);
}
.reel__placeholder span { font-size: var(--fs-8); letter-spacing: 0.1em; }
.reel__placeholder small { font-size: var(--fs-4); opacity: .5; }

/* ============================================================
   SEC-4 SERVICE
   ============================================================ */
.service { margin-top: 280rem; padding-bottom: 160rem; row-gap: 40rem; }
@media (width >= 992px) { .service { margin-top: 430rem; } }
.service .section-label { grid-column: 2 / 3; }
.service__statement {
  grid-column: 2 / 6;
  font-size: var(--fs-9);
  line-height: 1.3; font-weight: 300;
  margin-bottom: 80rem;
}
.service__statement .line:last-child {
  font-family: var(--font-zen);
  font-size: 36rem;
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-gray);
  margin-top: 32rem;
}
.service__list {
  grid-column: 2 / 6;
  display: grid;
  position: relative;
  --rail-x: 26rem;        /* x-position of the flow rail */
  --flow: 0;              /* 0→1 draw progress, driven on scroll (see initServiceFlow) */
  padding-left: 78rem;    /* clear space to the left for the timeline rail */
}
.service__item {
  display: grid;
  /* num | heading+desc | pictogram | trailing space.
     Column widths are chosen so the pictogram track lands exactly in the
     base-grid decorative pattern's cell from 1229rem to 1641rem (a "wide"
     412rem gap between two of its rules — see .base-grid::before) — this
     naturally frames the pictogram between two existing vertical rules
     without needing to touch/redraw the shared decoration:
       content-start (213rem) + 120 (num) + 40 (gap) + 816 (heading) + 40 (gap)
       = 1229rem, then the pictogram track runs 1229 → 1641 (412rem).
     The trailing 1fr is intentionally small (~102rem) to avoid the wide
     empty margin the previous layout left after the pictogram. */
  grid-template-columns: 120rem 816rem 412rem 1fr;
  gap: 40rem;
  align-items: center;
  padding: 40rem 0;
  border-top: 0.5px solid rgba(255,255,255,0.2);
  position: relative;
}
.service__pict {
  width: 240rem;
  aspect-ratio: 1 / 1;
  justify-self: center;
  align-self: center;
  cursor: grab;
  touch-action: none;
  pointer-events: auto;
}
.service__pict:active { cursor: grabbing; }

/* ---- 01→06 flow timeline: a line that draws top→bottom on scroll,
   with a continuously flowing accent comet and step nodes that light
   up in sequence. Accent uses MATÉRIA's sphere gradient (cyan→blue→
   indigo) so it reads on-brand against the black section. ---- */
.service__rail {
  position: absolute;
  left: var(--rail-x);
  top: 0;
  bottom: 0;
  width: 3rem;
  pointer-events: none;
  z-index: 0;
}
.service__rail::before {          /* faint full-height track */
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  transform: translateX(-50%);
  background: rgba(255,255,255,0.16);
}
.service__rail-fill {             /* accent line, grows top→bottom with --flow */
  position: absolute;
  left: 50%;
  top: 0;
  width: 3rem;
  height: calc(var(--flow) * 100%);
  transform: translateX(-50%);
  border-radius: 3rem;
  overflow: hidden;
  background: linear-gradient(180deg,
    var(--color-gradient-light) 0%,
    var(--color-gradient) 48%,
    var(--color-gradient-dark) 100%);
  box-shadow: 0 0 14rem rgba(1,102,201,0.5);
}
.service__rail-fill::after {      /* the flowing comet, clipped to the drawn line */
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 56rem;
  background: linear-gradient(180deg,
    transparent 0%,
    rgba(255,255,255,0.85) 50%,
    transparent 100%);
  animation: serviceFlow 2.6s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes serviceFlow {
  0%   { top: -56rem; }
  100% { top: 100%; }
}
.service__rail-node {             /* glowing leading tip at the flow front */
  position: absolute;
  left: 50%;
  top: calc(var(--flow) * 100%);
  width: 9rem;
  height: 9rem;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: var(--color-gradient-light);
  box-shadow: 0 0 12rem 2rem rgba(161,224,231,0.6),
              0 0 30rem 8rem rgba(1,102,201,0.3);
  opacity: calc(var(--flow) * 999);   /* hidden only at exactly 0 */
}
.service__dot {                   /* per-step node on the rail */
  position: absolute;
  left: 50%;
  width: 7rem;
  height: 7rem;
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(1);
  background: var(--color-main);
  border: 1px solid rgba(255,255,255,0.35);
  transition: background var(--duration) var(--ease),
              border-color var(--duration) var(--ease),
              box-shadow var(--duration) var(--ease),
              transform var(--duration) var(--ease);
}
.service__dot.is-active {
  background: var(--color-gradient-light);
  border-color: var(--color-gradient-light);
  box-shadow: 0 0 10rem 1rem rgba(161,224,231,0.6);
  transform: translate(-50%, -50%) scale(1.3);
}
.service__num {
  transition: color var(--duration) var(--ease);
}
.service__item.is-active .service__num { color: var(--color-sub); }
.service__item:last-child { border-bottom: 0.5px solid rgba(255,255,255,0.2); }
.service__num { font-size: var(--fs-8); color: var(--color-sub); font-weight: 300; }
.service__name { font-size: 72rem; font-weight: 500; letter-spacing: -0.01em; white-space: nowrap; }
.service__desc {
  margin-top: 16rem; max-width: 800rem;
  font-size: var(--fs-6); font-weight: 500; line-height: 1.6; color: var(--color-gray);
}

/* ============================================================
   PARTNER (募集要項 / who we're looking for)
   ============================================================ */
.partner { margin-top: 240rem; row-gap: 40rem; }
@media (width >= 992px) { .partner { margin-top: 320rem; } }
.partner .section-label { grid-column: 2 / 3; }
.partner__title {
  grid-column: 2 / 6;
  font-size: var(--fs-11);
  font-weight: 500; line-height: 1; letter-spacing: -0.02em;
  color: var(--color-sub);
  margin: 20rem 0 24rem;
}
.partner__lead {
  grid-column: 2 / 6;
  font-family: var(--font-zen);
  font-size: 36rem; font-weight: 700; line-height: 2; color: var(--color-gray);
  max-width: 1600rem; margin-bottom: 40rem;
}
.partner__list {
  grid-column: 2 / 6;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 80rem;
}
.partner__item {
  display: grid;
  grid-template-columns: 64rem 1fr;
  gap: 24rem;
  align-items: start;
  padding: 40rem 0;
  border-top: 0.5px solid rgba(255,255,255,0.2);
}
.partner__num {
  font-family: var(--font-jakarta);
  font-size: var(--fs-7); font-weight: 300;
  color: var(--color-gradient-light);
  /* vertically center against the title's line box (title font-size
     var(--fs-8) at line-height 1.4), not the whole title+desc block */
  line-height: calc(var(--fs-8) * 1.4);
  display: block;
}
.partner__name {
  font-family: var(--font-zen);
  font-size: var(--fs-8); font-weight: 700; color: var(--color-sub);
  margin-bottom: 12rem; line-height: 1.4;
}
.partner__desc {
  font-family: var(--font-zen);
  font-size: var(--fs-6); line-height: 1.9; color: var(--color-sub);
}

/* ============================================================
   BENEFIT (協業のメリット + 役割分担)
   ============================================================ */
.benefit { margin-top: 240rem; row-gap: 40rem; }
@media (width >= 992px) { .benefit { margin-top: 320rem; } }
.benefit .section-label { grid-column: 2 / 3; }
.benefit__title {
  grid-column: 2 / 6;
  font-size: var(--fs-11);
  font-weight: 500; line-height: 1; letter-spacing: -0.02em;
  color: var(--color-sub);
  margin: 20rem 0 40rem;
}
.benefit__list-wrap {
  grid-column: 2 / 6;
  position: relative;
  display: grid;
  grid-template-columns: 1fr 412rem;
  column-gap: 64rem;
  align-items: stretch;
  margin-bottom: 88rem;
}
.benefit__list {
  grid-column: 1;
  display: grid;
}
.benefit__item {
  display: grid; grid-template-columns: 64rem 1fr; gap: 24rem;
  align-items: baseline;
  padding: 32rem 0;
  border-top: 0.5px solid rgba(255,255,255,0.2);
  /* extend the rule line across the pictogram column too — negative
     margin + matching padding widen the border-box without moving the
     text, so the line reaches the wrap's right edge and can run under
     the (transparent-background) pictogram canvas */
  margin-right: calc(-412rem - 64rem);
  padding-right: calc(412rem + 64rem);
}
.benefit__item:last-child { border-bottom: 0.5px solid rgba(255,255,255,0.2); }
.benefit__num {
  font-family: var(--font-jakarta); font-size: var(--fs-7);
  font-weight: 300; color: var(--color-gradient-light);
}
.benefit__text {
  font-family: var(--font-zen); font-size: 22rem; font-weight: 500; line-height: 1.9; color: var(--color-sub);
}
/* pictogram slot — fills the same vertical span as the rule-lined list
   (top border of item 01 to bottom border of item 03). Taken out of the
   grid column flow and pinned by absolute x so its center lands exactly
   on the midpoint of the picto's own 412rem column track (wrap-local
   1236 to 1648 → center 1442), i.e. the middle of the rule-lined slot
   to its right, not the sitewide decorative grid line. */
.benefit__gain-stage {
  position: absolute;
  /* centered on wrap-local x=1442rem: left edge = center - half of the
     338rem width below. Computed directly via `left` (not
     transform:translateX(-50%)) because this element carries
     [data-reveal], whose GSAP reveal animation drives `transform`
     inline and would otherwise clobber a CSS transform here.
     Shifted left by 2310px total (144.375rem), at the 1920px baseline. */
  left: 1128.625rem;
  top: 0;
  bottom: 0;
  width: 338rem;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.benefit__stage {
  width: 100%;
  aspect-ratio: 1 / 1;
  cursor: grab;
  touch-action: none;
  pointer-events: auto;
}
.benefit__stage:active { cursor: grabbing; }
.benefit__roles {
  grid-column: 2 / 6;
  display: grid; grid-template-columns: 1fr 1fr; gap: 80rem;
}
.benefit__role-k {
  display: block;
  font-size: 36rem; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgb(255,255,255);
  padding-bottom: 24rem; margin-bottom: 4rem;
  border-bottom: 0.5px solid rgba(255,255,255,0.25);
}
.benefit__role ul { display: grid; }
/* editorial spec-sheet rows: hairline dividers, a short cyan rule on the
   left that extends on hover while the label slides right */
.benefit__role li {
  position: relative;
  font-family: var(--font-zen); font-size: 22rem; font-weight: 500;
  line-height: 1.4; color: var(--color-sub);
  padding: 24rem 8rem 24rem 44rem;
  border-bottom: 0.5px solid rgba(255,255,255,0.12);
  transition: padding-left .45s var(--ease), color .45s var(--ease);
}
.benefit__role li::before {
  content: ""; position: absolute; left: 8rem; top: 50%;
  width: 22rem; height: 1px;
  background: var(--color-gradient-light);
  transform: translateY(-50%) scaleX(0.5); transform-origin: left center;
  transition: transform .45s var(--ease);
}
.benefit__role li:hover { padding-left: 56rem; }
.benefit__role li:hover::before { transform: translateY(-50%) scaleX(1); }

/* ============================================================
   FOUNDER (代表紹介)
   ============================================================ */
.founder { margin-top: 240rem; row-gap: 40rem; }
@media (width >= 992px) { .founder { margin-top: 320rem; } }
.founder .section-label { grid-column: 2 / 3; }
/* the sitewide decorative rule pattern repeats every 547rem and doesn't
   land on this section's photo+gap text offset — replaced below by a
   rule custom-positioned at the text's actual left edge instead. */
.founder.base-grid::before { display: none; }
.founder__body {
  grid-column: 2 / 6;
  position: relative;
  display: grid;
  /* portrait pinned to the left in a fixed 412rem (one design-grid column)
     track, so its left/right edges land exactly on the base-grid rules
     (135 / 547). Text takes the remaining space to the right. */
  grid-template-columns: 412rem 1fr;
  column-gap: 96rem;
  align-items: start;
  border-top: 0.5px solid rgba(255,255,255,0.25);
  padding-top: 64rem;   /* image top + quote top align on this line */
}
.founder__head { grid-column: 1; grid-row: 1; }
.founder__bio  { grid-column: 2; grid-row: 1; }
/* rule line pinned exactly to the text column's left edge (photo width +
   gap), spanning the full height of the body so it tracks whichever
   column (photo or bio) ends up taller */
.founder__body::after {
  content: "";
  position: absolute;
  left: 508rem;   /* 412rem photo + 96rem gap */
  top: 0; bottom: 0;
  width: 0.5px;
  background: var(--grid-line);
  pointer-events: none;
}
/* ----- portrait (grid-aligned) ----- */
.founder__photo {
  width: 100%;
  aspect-ratio: 5 / 4;          /* native 1000×800 — no crop */
  overflow: hidden;
  background: #0c0c0c;
  border: 0.5px solid rgba(255,255,255,0.14);
}
.founder__photo img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform 1.2s var(--ease);
}
.founder__photo:hover img { transform: scale(1.04); }
/* ----- identity block under the photo ----- */
.founder__id { margin-top: 32rem; }
.founder__role {
  font-size: var(--fs-4); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-gradient-light);
  margin-bottom: 16rem;
}
.founder__company {
  font-family: var(--font-zen);
  font-size: var(--fs-5); letter-spacing: 0.04em;
  color: var(--color-gray);
  margin-bottom: 12rem;
}
.founder__name {
  font-family: var(--font-zen);
  font-size: var(--fs-9); font-weight: 500; letter-spacing: 0.04em;
  color: var(--color-sub); margin-bottom: 8rem;
}
.founder__romaji {
  font-size: var(--fs-5); letter-spacing: 0.1em; color: var(--color-gray);
  margin-bottom: 28rem;
}
.founder__link {
  display: inline-flex; align-items: center; gap: 8rem;
  font-size: var(--fs-5); letter-spacing: 0.04em; color: var(--color-gray);
  padding-bottom: 6rem;
  border-bottom: 0.5px solid rgba(255,255,255,0.3);
  transition: color .4s var(--ease), border-color .4s var(--ease);
}
.founder__link i { font-style: normal; color: var(--color-gradient-light); }
.founder__link:hover { color: var(--color-sub); border-color: var(--color-sub); }
/* ----- quote + long-form bio ----- */
.founder__quote {
  font-family: var(--font-zen); font-size: 28rem; font-weight: 700; line-height: 1.6;
  color: var(--color-sub); margin-bottom: 44rem;
}
.founder__text { display: grid; gap: 28rem; }
.founder__text p {
  font-family: var(--font-zen); font-size: var(--fs-6); font-weight: 500; line-height: 2; color: var(--color-gray);
}
@media (width < 992px) {
  .founder__body { grid-template-columns: 1fr; column-gap: 0; row-gap: 48rem; padding-top: 48rem; }
  .founder__head { grid-column: 1; grid-row: 1; }
  .founder__bio  { grid-column: 1; grid-row: 2; }
  .founder__photo { max-width: 520rem; }
  .founder__body::after { display: none; }

  /* the fixed-width desktop process grid overflows this narrower baseline —
     drop back to the num | content layout and hide the per-step pictograms */
  .service__item { grid-template-columns: 120rem 1fr; }
  .service__pict { display: none; }
}

/* ============================================================
   FAQ (accordion)
   ============================================================ */
.faq { margin-top: 240rem; padding-bottom: 40rem; row-gap: 40rem; }
@media (width >= 992px) { .faq { margin-top: 320rem; } }
.faq .section-label { grid-column: 2 / 3; }
.faq__title {
  grid-column: 2 / 6;
  font-size: var(--fs-11);
  font-weight: 500; line-height: 1; letter-spacing: -0.02em;
  color: var(--color-sub);
  margin: 20rem 0 40rem;
}
.faq__list { grid-column: 2 / 6; display: grid; }
.faq__item {
  border-top: 0.5px solid rgba(255,255,255,0.2);
  transition: box-shadow .5s var(--ease);
  /* off state: zero-alpha accent so the glow-in is a pure fade */
  box-shadow:
    inset 0 0.5px 0 0 rgba(161,224,231,0),
    inset 0 -0.5px 0 0 rgba(161,224,231,0),
    0 0 0 0 rgba(161,224,231,0);
}
.faq__item:last-child { border-bottom: 0.5px solid rgba(255,255,255,0.2); }
/* same soft cyan glow as the Contact list: only the hovered/focused row
   lights up along its top/bottom hairlines plus a faint ambient bloom */
.faq__item:hover,
.faq__item:has(.faq__q:focus-visible) {
  box-shadow:
    inset 0 0.5px 0 0 rgba(161,224,231,0.9),
    inset 0 -0.5px 0 0 rgba(161,224,231,0.9),
    0 0 32rem 2rem rgba(161,224,231,0.2);
}
.faq__q {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 24rem;
  padding: 36rem 8rem;
  font-family: var(--font-zen); font-size: var(--fs-7); font-weight: 500;
  color: var(--color-sub);
  outline: none;   /* replaced by the cyan glow above (see :focus-visible) */
  transition: color .4s var(--ease);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__mark { position: relative; flex: none; width: 24rem; height: 24rem; color: inherit; }
.faq__mark::before, .faq__mark::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  background: currentColor;
  transition: transform .4s var(--ease), opacity .4s var(--ease);
}
.faq__mark::before { width: 24rem; height: 1.5px; transform: translate(-50%, -50%); }
.faq__mark::after  { width: 1.5px; height: 24rem; transform: translate(-50%, -50%); }
.faq__item[open] .faq__mark::after { opacity: 0; transform: translate(-50%, -50%) rotate(90deg); }
.faq__a { padding: 0 8rem 40rem; max-width: 1300rem; display: grid; gap: 20rem; }
.faq__a p {
  font-family: var(--font-zen); font-size: var(--fs-6); line-height: 1.9; color: var(--color-sub);
}
.faq__item[open] .faq__a { animation: faqIn .5s var(--ease); }
@keyframes faqIn {
  from { opacity: 0; transform: translateY(-6rem); }
  to   { opacity: 1; transform: none; }
}

/* ============================================================
   SEC-5 WORKS
   ============================================================ */
.works { margin-top: 280rem; padding-bottom: 200rem; overflow: hidden; row-gap: 60rem; }
@media (width >= 768px) { .works { margin-top: 240rem; } }
.works .section-label { grid-column: 2 / 3; }
.works__grid {
  grid-column: 2 / 6;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 80rem 60rem;
  align-items: start;
}
.work { display: block; }
.work__thumb {
  width: 100%;
  aspect-ratio: var(--w) / var(--h);
  border-radius: 4rem;
  overflow: hidden;
  transition: transform .8s var(--ease);
  transform: scale(1);
}
.work__thumb[data-tone="1"] { background: linear-gradient(135deg,#0f0a72,#0166c9); }
.work__thumb[data-tone="2"] { background: linear-gradient(135deg,#0166c9,#a1e0e7); }
.work__thumb[data-tone="3"] { background: linear-gradient(135deg,#12122b,#0166c9); }
.work__thumb[data-tone="4"] { background: linear-gradient(135deg,#0f0a72,#a1e0e7); }
.work__thumb[data-tone="5"] { background: linear-gradient(135deg,#05030f,#0166c9); }
.work__thumb[data-tone="6"] { background: linear-gradient(135deg,#0166c9,#0f0a72); }
.work:hover .work__thumb { transform: scale(1.06); }
.work--tall { align-self: stretch; }
.work__meta {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 20rem;
  gap: 20rem;
}
.work__title {
  position: relative;
  font-size: var(--fs-7);
  font-weight: 300;
  height: 1.2em; overflow: hidden;
}
.work__title span {
  display: block;
  transition: transform .5s var(--ease);
}
.work__title span:last-child { position: absolute; top: 100%; left: 0; }
.work:hover .work__title span { transform: translateY(-100%); }
.work__cat { font-size: var(--fs-4); letter-spacing: 0.1em; color: var(--color-sub); }
.works__all { grid-column: 2 / 3; justify-self: start; margin-top: 40rem; }

/* Works hover preview (outline marquee) */
.work-preview {
  position: fixed; top: 0; left: 0;
  z-index: 400;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%);
  will-change: transform, opacity;
  transition: opacity .3s var(--ease);
}
.work-preview.is-visible { opacity: 1; }
.work-preview__row { overflow: hidden; white-space: nowrap; }
.work-preview__row span {
  display: inline-block;
  font-size: var(--fs-9);
  font-weight: 300;
  letter-spacing: 0.04em;
  -webkit-text-stroke: 0.5px var(--color-sub);
  color: transparent;
  text-transform: uppercase;
  animation: marquee 8s linear infinite;
}
.work-preview__row:nth-child(2) span { animation-direction: reverse; animation-duration: 10s; }
@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ============================================================
   SEC-5.5 CONTACT
   ============================================================ */
.contact { margin-top: 240rem; padding-bottom: 40rem; row-gap: 32rem; }
@media (width >= 992px) { .contact { margin-top: 320rem; } }
.contact .section-label { grid-column: 2 / 3; }
.contact__title {
  grid-column: 2 / 6;
  font-size: var(--fs-11);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  margin: 20rem 0 8rem;
}
.contact__lead {
  grid-column: 2 / 6;
  font-family: var(--font-zen);
  font-size: var(--fs-6);
  line-height: 1.9;
  color: var(--color-sub);
  margin-bottom: 40rem;
}
.contact__list { grid-column: 2 / 6; display: grid; }
.contact__item {
  border-top: 0.5px solid rgba(255,255,255,0.2);
  transition: box-shadow .5s var(--ease);
  /* off state: same hairline positions, zero-alpha accent so the glow-in
     is a pure fade, not a color swap */
  box-shadow:
    inset 0 0.5px 0 0 rgba(161,224,231,0),
    inset 0 -0.5px 0 0 rgba(161,224,231,0),
    0 0 0 0 rgba(161,224,231,0);
}
.contact__item:last-child { border-bottom: 0.5px solid rgba(255,255,255,0.2); }
/* Only the hovered row's frame lights up — a soft cyan glow along its
   top/bottom hairlines plus a faint ambient bloom, nothing else moves. */
.contact__item:has(.contact__link:hover),
.contact__item:has(.contact__link:focus-visible) {
  box-shadow:
    inset 0 0.5px 0 0 rgba(161,224,231,0.9),
    inset 0 -0.5px 0 0 rgba(161,224,231,0.9),
    0 0 32rem 2rem rgba(161,224,231,0.2);
}
.contact__link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24rem;
  padding: 40rem 24rem;
  overflow: hidden;
}

.contact__label,
.contact__arrow {
  position: relative;
  z-index: 1;
}
.contact__label {
  font-family: var(--font-zen);
  font-size: var(--fs-7);
  font-weight: 500;
  color: var(--color-sub);
  transition: transform .55s var(--ease);
}
.contact__arrow {
  font-size: var(--fs-7);
  color: var(--color-sub);
  opacity: 0;
  transform: translateX(-12rem);
  transition: opacity .45s var(--ease), transform .55s var(--ease);
}
.contact__link:hover .contact__label,
.contact__link:focus-visible .contact__label { transform: translateX(16rem); }
.contact__link:hover .contact__arrow,
.contact__link:focus-visible .contact__arrow { opacity: 1; transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
  .contact__link::before,
  .contact__label,
  .contact__arrow { transition-duration: 1ms; }
}

/* ----- Page-top variant: same section, but it opens the page (no hero
   above it), so it only needs to clear the fixed corner UI, not a whole
   first view. ----- */
.contact--page { margin-top: 160rem; padding-bottom: 0; }
@media (width >= 992px) { .contact--page { margin-top: 200rem; } }

/* ============================================================
   CONTACT — FORM (contact.html)
   ============================================================ */
.contact-form { margin-top: 120rem; padding-bottom: 240rem; row-gap: 32rem; }
@media (width >= 992px) { .contact-form { margin-top: 160rem; } }
.contact-form .section-label { grid-column: 2 / 3; }
/* readability: min 16px + white for the labels on the contact page */
.contact--page .section-label,
.contact-form .section-label { font-size: 16rem; color: var(--color-sub); }
.contact-form__lead {
  grid-column: 2 / 6;
  font-family: var(--font-zen);
  font-size: var(--fs-6);
  line-height: 1.9;
  color: var(--color-gray);
  margin-bottom: 16rem;
}

#contactForm { grid-column: 2 / 6; }
.contact-form__fields {
  display: grid;
  gap: 40rem;
  max-width: 820rem;
  margin-top: 24rem;
}

/* ----- Category picker (radio pills) ----- */
.field-group { display: grid; gap: 16rem; }
.field-group__label {
  font-size: 16rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-sub);
}
.field-group__options { display: flex; flex-wrap: wrap; gap: 12rem; }
.radio-pill { position: relative; display: inline-flex; }
.radio-pill input {
  position: absolute; inset: 0;
  opacity: 0; margin: 0; cursor: pointer;
}
.radio-pill span {
  display: inline-flex; align-items: center;
  padding: 12rem 22rem;
  border-radius: 999rem;
  border: 0.5px solid rgba(255,255,255,0.3);
  font-family: var(--font-zen);
  font-size: var(--fs-5);
  color: var(--color-gray);
  transition: border-color .4s var(--ease), color .4s var(--ease), background-color .4s var(--ease);
}
.radio-pill:hover span { border-color: rgba(255,255,255,0.6); }
.radio-pill input:checked + span {
  border-color: var(--color-gradient-light);
  background-color: rgba(1,102,201,0.15);
  color: var(--color-sub);
}
.radio-pill input:focus-visible + span { border-color: var(--color-sub); }

/* ----- Text fields ----- */
.field { display: grid; gap: 10rem; }
.field label {
  font-size: 16rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-sub);
}
.field__req {
  margin-left: 10rem;
  font-size: 16rem;
  letter-spacing: 0.06em;
  color: var(--color-gradient-light);
  text-transform: none;
}
.field input,
.field textarea {
  width: 100%;
  background: transparent;
  border: none;
  border-bottom: 0.5px solid rgba(255,255,255,0.3);
  padding: 12rem 2rem;
  font-family: var(--font-jakarta);
  font-size: var(--fs-6);
  font-weight: 300;
  color: var(--color-sub);
  transition: border-color .4s var(--ease);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--color-sub); }
.field input:focus,
.field textarea:focus { outline: none; border-color: var(--color-gradient-light); }
.field textarea { resize: vertical; min-height: 140rem; line-height: 1.7; }

/* ----- Submit ----- */
.contact-form__submit-row {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 24rem; margin-top: 8rem;
}
.contact-form__submit { background: none; border: 0; cursor: pointer; }
.contact-form__note { font-size: 16rem; color: var(--color-sub); }

/* ----- Sent state ----- */
.contact-form__sent {
  display: none;
  max-width: 820rem;
  padding: 56rem 0;
  border-top: 0.5px solid rgba(255,255,255,0.2);
}
.contact-form__sent-title {
  font-size: var(--fs-9);
  font-weight: 300;
  line-height: 1.4;
  margin-bottom: 16rem;
}
.contact-form__sent-body {
  font-family: var(--font-zen);
  font-size: var(--fs-6);
  line-height: 1.9;
  color: var(--color-gray);
}
#contactForm.is-sent .contact-form__fields { display: none; }
#contactForm.is-sent .contact-form__sent { display: block; }

/* ============================================================
   SEC-6 CTA
   ============================================================ */
.cta {
  position: relative;
  /* extra scroll length drives the scroll-triggered photo pop-out; the inner
     stage stays pinned (sticky) and centered while this scrolls past */
  min-height: 240vh;
}
.cta__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid; place-content: center;
  overflow: hidden;
}
.cta__canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; }
.cta__link { position: relative; z-index: 3; }

/* scattered photos that pop in on scroll (kaitonote-style) */
.cta__scatter { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.cta__photo {
  position: absolute;
  /* --x/--y/--w come from the inline style in index.html; the mobile block
     below swaps in --mx/--my/--mw. Authored as custom properties rather
     than inline left/top/width so a media query can override them. */
  left: var(--x);
  top: var(--y);
  width: var(--w);
  transform: translate(-50%, -50%);   /* position wrapper by its center */
  margin: 0;
  line-height: 0;
}
.cta__photo img {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  opacity: 0;                          /* revealed by GSAP on scroll */
  box-shadow: 0 24rem 80rem rgba(0,0,0,0.5);
  will-change: transform, opacity, filter;
}
@media (prefers-reduced-motion: reduce) {
  .cta { min-height: 100vh; }
  .cta__photo img { opacity: 0.9; }
}
@media (width < 768px) {
  /* The scatter runs on phones too, using the mobile layout authored as
     --mx/--my/--mw on each figure (see index.html). It was previously
     display:none here because the desktop vw sizes/positions crowded the
     headline; the mobile set instead keeps the middle third clear for the
     text and scales the photos up (1vw is ~4px on a phone vs ~19px on a
     desktop, so the desktop widths would render as thumbnails).
     .cta keeps its tall scroll length so the sticky stage has runway for
     the scrubbed pop-out — without it there is no scroll distance to
     drive the animation.
     REVERT: `.cta { min-height: 100vh; } .cta__scatter { display: none; }`
     to go back to a photo-less mobile CTA. */
  .cta { min-height: 220vh; }
  .cta__photo {
    left: var(--mx, var(--x));
    top: var(--my, var(--y));
    /* --cta-photo-scale multiplies every mobile photo at once, so the
       overall presence of the scatter can be tuned from one place instead
       of rewriting ten --mw values. Overlap between photos is intentional
       at scales above 1 — the positions are not re-spaced to compensate. */
    width: calc(var(--mw, var(--w)) * var(--cta-photo-scale, 1));
  }
  .cta__scatter { --cta-photo-scale: 1.15; }
  /* Lighter than the desktop 0 24rem 80rem: at phone scale that spread
     reads as a grey haze around every photo rather than a cast shadow. */
  .cta__photo img { box-shadow: 0 8rem 32rem rgba(0,0,0,0.55); }
  /* Stack the headline as three left-aligned lines (Let's / Build /
     Together) instead of one centered run. place-content's second value
     switches the sticky stage's inline alignment from center to start,
     and the padding puts the text's left edge on the same shared gutter
     as every other section (--sp-content-padding).
     REVERT: drop this block — .cta__word returns to inline and the stage
     to `place-content: center` — for the centered one-line headline. */
  .cta__stage {
    place-content: center start;
    padding-inline: var(--sp-content-padding);
  }
  .cta__link { display: block; width: 100%; }
  .cta__title { text-align: left; }
  .cta__word { display: block; }
}
.cta__title {
  font-size: var(--fs-12);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  transition: opacity .4s var(--ease);
}
.cta__title-em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
}
.cta__link:hover .cta__title { opacity: .7; }

/* ============================================================
   SEC-7 FOOTER
   ============================================================ */
.footer {
  min-height: 100svh;
  align-content: center;
  padding: 120rem 0;
  row-gap: 80rem;
  background-color: var(--color-main);   /* black by default */
  color: var(--color-sub);
  position: relative;
  /* footer-transition: theme flip to light on scroll-in */
  transition: background-color var(--duration) var(--ease),
              color var(--duration) var(--ease);
}
.footer::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 60%;
  background: radial-gradient(120% 100% at 50% 120%,
    rgba(1,102,201,0.4), rgba(15,10,114,0.25) 50%, transparent 75%);
  pointer-events: none; z-index: 0;
  transition: opacity var(--duration) var(--ease);
}

/* ----- Inverted (light) theme ----- */
.footer.is-light {
  background-color: var(--color-sub);    /* white */
  color: var(--color-main);              /* black */
  --grid-line: rgba(0,0,0,0.12);
}
.footer.is-light::after { opacity: 0; }
.footer.is-light .footer__k,
.footer.is-light .footer__copy { color: var(--color-gray-dark); }
.footer__col--nav { grid-column: 2 / 3; }
.footer__col--nav ul { display: grid; gap: 20rem; }
.footer__col--nav a {
  font-size: var(--fs-10);
  font-weight: 400;
  display: inline-block;
  transition: color var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.footer__col--nav a:hover {
  color: var(--color-gradient-light);
  transform: translateX(12rem);
}
/* decorative globe pictogram — sits in the same grid cell as meta (behind
   it, per DOM order) so it fills the otherwise-empty space to the right
   of that short text block without disturbing its layout. align-self/
   justify-self:center (overriding the grid default of stretch) means its
   size comes purely from width/aspect-ratio below, so it can never be
   squeezed/clipped by the row's content-driven height. */
.footer__col--pict {
  grid-column: 3 / 6;
  grid-row: 1;
  align-self: center;
  justify-self: center;
  width: 680rem;
  aspect-ratio: 1 / 1;
  pointer-events: none;
}
.footer__stage { display: block; width: 100%; height: 100%; }
.footer__col--meta { grid-column: 3 / 6; grid-row: 1; display: grid; gap: 40rem; align-content: start; }
.footer__block { display: grid; gap: 8rem; }
.footer__k { font-size: var(--fs-4); letter-spacing: 0.14em; color: var(--color-sub); text-transform: uppercase; }
.footer__block a, .footer__block span, .footer__clock { font-size: var(--fs-6); }
.footer__clock { font-variant-numeric: tabular-nums; letter-spacing: 0.08em; }
.footer__sns-list { display: grid; gap: 12rem; }
.footer__sns-list a { font-size: var(--fs-5); display: inline-flex; gap: 8rem; }
.menu__sns i, .footer__block i { font-style: normal; opacity: .6; }
.footer__copy {
  grid-column: 2 / 6;
  margin-top: 40rem;
  font-size: var(--fs-4);
  color: var(--color-sub);
  letter-spacing: 0.04em;
}

/* ============================================================
   MENU (fullscreen overlay)
   ============================================================ */
.menu {
  position: fixed; inset: 0;
  z-index: 800;
  visibility: hidden;
  overflow: hidden;   /* keep oversized nav text from adding page scroll width */
  clip-path: circle(0 at calc(100% - 40rem) 40rem);
  transition: clip-path var(--duration) var(--ease), visibility 0s linear var(--duration);
}
.menu.is-open {
  visibility: visible;
  clip-path: circle(150% at calc(100% - 40rem) 40rem);
  transition: clip-path var(--duration) var(--ease), visibility 0s;
}
.menu__bg {
  position: absolute; inset: 0;
  background: var(--color-main);
}
.menu__bg::before {
  content: "";
  position: absolute; left: -20%; top: 50%;
  width: 70vh; height: 70vh; transform: translateY(-50%);
  border-radius: 50%;
  background: radial-gradient(circle at 40% 40%,
    var(--color-gradient-light), var(--color-gradient) 45%, var(--color-gradient-dark) 80%);
  filter: blur(40px);
  opacity: .55;
}

/* ---- Mobile drawer orb motion: breathing + smooth hue drift ----
   Two independent animations layered on the same pseudo-element:
   orbBreathe is a continuous 6s scale/opacity pulse ("呼吸") — pattern B
   ("ダイナミック") from the 3-option comparison, a stronger swing than the
   original 8s/0.94-1.07 version. orbHueShift eases the orb's hue back and
   forth between its original blue and a cyan-shifted version (-30deg,
   ~210deg blue -> ~180deg cyan) with ease-in-out timing so the color
   drifts continuously with no visible steps. Desktop keeps the original
   static orb; this only applies inside the mobile media query below.
   REVERT: delete the two @keyframes blocks and the .menu__bg::before
   animation rule inside the mobile block to restore the static orb. */
@keyframes orbBreathe {
  0%, 100% { transform: translateY(-50%) scale(0.80); opacity: .28; }
  50%      { transform: translateY(-50%) scale(1.22); opacity: .88; }
}
@keyframes orbHueShift {
  0%, 100% { filter: blur(40px) hue-rotate(0deg); }
  50%      { filter: blur(40px) hue-rotate(-30deg); }
}
.menu__grid {
  position: relative;
  height: 100%;
  display: grid;
  align-content: center;
  justify-content: end;
  gap: 60rem;
  padding: 0 135rem;
}
.menu__nav { display: grid; gap: 10rem; text-align: right; }
/* The link is a clipping window that the inner span slides up into.
   `overflow: clip` rather than `hidden`: `hidden` makes the element a
   scroll container, and on iOS Safari the boundary between that scroll
   container and the composited (transformed) span inside it renders as a
   1px seam — a dark hairline under every nav word, exactly the width of the
   link box. `clip` does the same visual job without the scroll container,
   and the 2px bottom padding moves the clip edge clear of the glyph ink so
   the seam has nothing to land on even where `clip` is unsupported.
   backface-visibility pins the span to whole pixels on the same GPU layer.
   The span still starts fully out of view: translateY(110%) is 110% of the
   span's own height (55.4px at 48px type) against a 52.4px window.
   REVERT: `overflow: hidden` with no padding, and drop the span's
   backface-visibility, to go back to the previous behaviour. */
.menu__nav a {
  display: inline-block;
  font-size: var(--fs-12);
  font-weight: 300;
  line-height: 1.05;
  overflow: clip;
  padding-bottom: 2px;
}
.menu__nav a span {
  display: inline-block;
  transform: translateY(110%);
  transition: transform .6s var(--ease);
  backface-visibility: hidden;
}
.menu.is-open .menu__nav a span { transform: translateY(0); }
.menu.is-open .menu__nav li:nth-child(1) a span { transition-delay: .15s; }
.menu.is-open .menu__nav li:nth-child(2) a span { transition-delay: .22s; }
.menu.is-open .menu__nav li:nth-child(3) a span { transition-delay: .29s; }
.menu.is-open .menu__nav li:nth-child(4) a span { transition-delay: .36s; }
.menu.is-open .menu__nav li:nth-child(5) a span { transition-delay: .43s; }
.menu__sns {
  display: grid; grid-template-columns: repeat(2, auto); gap: 16rem 60rem;
  justify-content: end; text-align: right;
}
.menu__sns a { font-size: var(--fs-6); display: inline-flex; gap: 8rem; }

/* ----- Close (×) button: lives above the overlay (the fixed {MENU} button
   sits below the menu's z-index, so it can't serve as the close control).
   Two thin rules crossed — matches the site's hairline-grid aesthetic. ----- */
.menu__close {
  position: absolute;
  top: 0; right: 0;
  margin: 20rem;                 /* same corner offset as .fixed-ui--menu */
  width: 34rem; height: 34rem;
  z-index: 3;                    /* above .menu__bg + .menu__grid */
  display: grid; place-items: center;
  opacity: 0;
  transform: scale(0.9);
  transition: opacity .4s var(--ease) .15s,
              transform .4s var(--ease) .15s;
}
.menu.is-open .menu__close { opacity: 1; transform: scale(1); }
.menu__close span {
  position: absolute;
  width: 34rem; height: 1px;
  background: var(--color-sub);
  transition: background .4s var(--ease);
}
.menu__close span:first-child { transform: rotate(45deg); }
.menu__close span:last-child { transform: rotate(-45deg); }
.menu__close:hover span { background: var(--color-gradient-light); }

/* ============================================================
   Responsive tweaks
   ============================================================ */
/* Mid range (768–992px): base-vw is 992, so the grid must total ~992rem
   to fill the viewport instead of the 1920-comp columns (which overflow). */
@media (768px <= width < 992px) {
  .base-grid { grid-template-columns: 60rem repeat(4, 218rem) 60rem; }
  .base-grid::before { display: none; }

  /* .benefit__gain-stage's desktop rules (left/width in rem) are pinned to
     the 1920px-baseline grid math and don't translate to this narrower
     992-baseline grid — fall back to the same simple stacked layout used
     below 768px instead of rendering a wildly mis-scaled/off-position box. */
  .benefit__list-wrap { grid-template-columns: 1fr; row-gap: 40rem; }
  .benefit__gain-stage {
    position: static;
    grid-column: 1;
    width: 100%;
    max-width: 260rem;
    margin: 0 auto;
  }
  .benefit__item { margin-right: 0; padding-right: 0; }
}

@media (width < 768px), (hover: none) and (orientation: landscape) and (max-height: 500px) {
  /* Single source of truth for the mobile left/right content margin. Every
     section's text container is aligned to this same value (via
     .base-grid's outer columns below, plus a couple of non-grid sections —
     .reel, .menu__grid — that opt into it directly) instead of each
     section carrying its own hand-tuned side padding, which is what let
     them drift out of alignment with each other in the first place. */
  :root {
    --sp-content-padding: clamp(24px, 7vw, 32px);
    /* Vertical rhythm tokens. The section margins were authored against the
       1920px desktop comp (200-430rem) and carried straight over to phones,
       where they measured 181-363px between sections — an uneven ladder that
       read as dead space. One token now drives every section boundary; the
       photo interstitials keep their own tighter value.
         --sp-section-gap: 165rem -> 150px @390 (137 @320, 165 @430)
         --sp-photo-gap:    96rem ->  87px @390
       REVERT: delete the two tokens and the two rule groups that consume
       them (marked "shared section rhythm" below) to restore the per-section
       desktop-derived values. */
    --sp-section-gap: 165rem;
    --sp-photo-gap: 96rem;
  }

  /* ---- shared section rhythm (1/2): every section boundary ---- */
  .intro, .overview, .hello, .service,
  .partner, .benefit, .founder, .faq, .contact {
    margin-top: var(--sp-section-gap);
  }
  /* Trailing padding on these sections was a second helping of section gap —
     it stacked on top of the next section's margin (e.g. .service's 160rem
     plus .founder's 240rem measured 363px). Zeroed so the token alone
     decides the boundary. .contact keeps its 40rem: the CTA that follows is
     a full-bleed sticky stage, not a normal section, and butting it flush
     against the contact list looks like a rendering error. */
  .intro, .hello, .service, .faq { padding-bottom: 0; }

  /* Fluid gutters decoupled from the vw-linked rem: a fixed px/vw clamp
     instead of `40rem` means the side margins track the viewport smoothly
     instead of jumping with the rem unit, and minmax(0,1fr) lets the
     center column shrink below its content's natural min-width instead
     of forcing the page wider (pairs with the .base-grid > * min-width:0
     rule declared alongside .base-grid above). This is THE shared
     container: every .base-grid section's text lives in grid-column 2/3,
     so they all inherit the exact same left/right edge from here. */
  .base-grid {
    grid-template-columns:
      var(--sp-content-padding)
      minmax(0, 1fr)
      var(--sp-content-padding);
  }
  .base-grid::before { display: none; }

  /* Mobile type floor: body/label/form text must stay readable (≥16px) at
     every phone width. The vw-linked rem above 16rem already reaches 16px
     at ≥430px width, but shrinks below it on narrower phones (320px ≈
     11.9px) — these override the small utility sizes with an explicit
     floor instead of continuing to scale down. */
  :root {
    --fs-1: 16px; --fs-2: 16px; --fs-3: 16px; --fs-4: 16px; --fs-5: 16px;
    --fs-6: clamp(16px, 4.2vw, 18px);
    --fs-7: clamp(18px, 4.8vw, 21px);
    --fs-8: clamp(22px, 5.8vw, 26px);
    --fs-9: clamp(28px, 7vw, 32px);
    --fs-11: clamp(52px, 15vw, 68px);
  }
  /* A few form/label sizes are authored as a literal 16rem rather than
     var(--fs-*) — floor those the same way so they can't dip under 16px
     on narrow phones either. */
  .contact--page .section-label,
  .contact-form .section-label,
  .field-group__label,
  .field label,
  .field__req,
  .contact-form__note { font-size: 16px; }

  /* Large display headings: left-aligned (never centered/justified — see
     the shared rule below) and, where supported, text-wrap:pretty so a
     wrapped 2-3 word heading can't leave a single orphaned word on its own
     line. Scoped to mobile only per the "don't touch desktop" requirement,
     even though pretty-wrapping is a cosmetic no-op almost everywhere. */
  .hero__copy,
  .overview__title,
  .hello__title,
  .partner__title,
  .benefit__title,
  .faq__title,
  .contact__title,
  .founder__name {
    text-align: left;
    text-align-last: auto;
  }
  @supports (text-wrap: pretty) {
    .hero__copy,
    .overview__title,
    .hello__title,
    .partner__title,
    .benefit__title,
    .faq__title,
    .contact__title,
    .founder__name { text-wrap: pretty; }
  }

  /* {LABEL} -> heading gap unification. Every *__title rule authors the
     same intent — margin-top: 20rem between the bracket label and the
     English heading (see .hello__title, .partner__title, .benefit__title,
     .faq__title, .contact__title above) — and on desktop that's exactly
     what renders, because .hello has no row-gap so the label and title
     stack with nothing but that 20rem margin between them.
     On mobile, though, .section-label and these titles get forced into
     the same grid-column (2/3, see the per-section rules below) so they
     become adjacent grid ROWS — and each section's own row-gap (40rem for
     partner/benefit/faq, 32rem for contact) then stacks ON TOP of the
     20rem margin, since grid row-gap and margin-top are independent and
     both apply. .hello never had a row-gap, so it alone rendered the
     intended 20rem gap; every other section silently doubled it
     (measured ~54rem instead of ~20rem for benefit). Cancelling each
     section's row-gap here restores the single 20rem gap everywhere —
     same value the CSS already declares as the intent, just no longer
     paid twice. REVERT: delete this rule; each title's own margin-top
     reappears as authored, but the row-gap doubling returns with it. */
  .partner__title,
  .benefit__title,
  .faq__title { margin-top: -20rem; }
  .contact__title { margin-top: -12rem; }

  /* The intro statement was authored spanning grid-column 2/6. On the
     3-column mobile grid (lines 1–4) that reaches a non-existent line 6,
     creating two implicit columns that widened the whole page. */
  .intro__ja, .intro__en { grid-column: 2 / 3; }

  /* Interstitial photo: same 2/3 track as every other mobile section, so its
     left/right edges sit on the shared --sp-content-padding gutters. */
  .interstitial__img { grid-column: 2 / 3; }

  /* ----- Hero -----
     Lead paragraph dropped on mobile so the particle-formed MATÉRIA
     wordmark can take the center of the first view uncontested, the same
     role the -125rem lead margin trick protects on desktop.
     REVERT: replace this with
       .hero__lead { grid-column: 2 / 3; margin-top: max(72px, calc(56px + env(safe-area-inset-top))); }
     to restore the lead paragraph on mobile. */
  .hero__lead { display: none; }
  .hero__label { display: none; }
  /* Particle wordmark removed on mobile — the first view is now the
     heading alone at full size (see .hero__copy below). This hides the
     canvas; main.js also early-exits initParticleHero() on compact
     viewports so no pool/raf work happens at all.
     REVERT: comment this rule out AND remove the matching
     `if (isCompact()) return;` early-exit at the top of
     initParticleHero() in main.js to bring the particle logo back. */
  .hero__particles { display: none; }
  .hero__grid {
    padding: max(96px, calc(64px + env(safe-area-inset-top))) 0 max(40px, env(safe-area-inset-bottom));
    /* With both the lead and the particle field gone, .hero__copy is the
       sole occupant of the first view, so centering it puts the enlarged
       heading in the optical middle of the screen.
       REVERT: `end` was the value used while the particle logo occupied
       the upper/middle band. */
    align-content: center;
  }
  .hero__copy {
    grid-column: 2 / 3;
    transform: none;
    /* Sized to fill the first view now that nothing else competes for it.
       16vw is the largest size that still yields a clean 6-line
       composition — measured identical at 320 / 375 / 390 / 430px:
         From / Material, / We Build / Business, / Craft for / the Market.
       (the line break was originally an em dash after "Business" — now a
       comma, see the heading-line markup in index.html — but the comma is
       narrower, so it wraps the same way if not more safely). Above 16vw
       the wrap degrades: at 17vw "the Market." splits, and by 18-20vw
       "the" gets stranded on its own line (and at 21vw+ "Material," /
       "Business," exceed the content column and get clipped by
       .heading-line's overflow:hidden). The 70px cap holds that
       composition on 430px+ phones; the 30px floor protects very narrow
       screens.
       line-height 1.2 keeps the block reading as a full first-view
       headline (6 lines still span well over half the viewport at every
       tested width) while sitting noticeably tighter than 1.3/1.55.
       Tracking stays slightly tight (-.02em) to keep the large type
       cohesive.
       REVERT: previous values were
         font-size: clamp(38px, 13vw, 56px); line-height: .98;
         letter-spacing: -.025em;
       (and briefly line-height: 1.55, then 1.3, before settling on 1.2). */
    font-size: clamp(30px, 16vw, 70px);
    line-height: 1.2;
    letter-spacing: -.02em;
  }
  /* Each of the 3 heading-line spans wraps naturally now instead of being
     manually split with a <br class="sp-only"> — text-wrap:pretty (applied
     below, alongside the rest of the site's headings) balances the last
     line so this doesn't orphan a single short word the way plain wrapping
     sometimes does. REVERT: to go back to the manual <br>-controlled
     5-line composition, restore
       .hero__copy .heading-line-inner { white-space: nowrap; }
       br.sp-only { display: initial; }
     here and the <br class="sp-only"> tags + the "for&nbsp;the" glue in
     index.html's .hero__copy markup. */

  /* Corner wordmark at half the previous mobile size (45rem -> 23rem).
     Only the height changes — .fixed-ui--logo's left/top offsets below are
     untouched, so the logo's left edge stays exactly where it was and the
     wordmark simply shrinks toward that fixed left origin (the <img> is
     `width:auto`, so the width follows the height by aspect ratio).
     History: 18rem was the original small fixed-UI size; 45rem was an
     enlarged size chosen to approach the hero heading's first-line width.
     REVERT: set back to `height: 45rem;` for the large version, or
     `height: 18rem;` for the original small corner logo. */
  .logo-img { height: 23rem; }

  /* ----- Fixed UI: keep only the logo/menu. SCROLL% and BACK TO TOP were
     overlapping Founder body copy, FAQ, the contact form and Footer meta
     once those sections' natural top offsets shrank on phones. ----- */
  .fixed-ui--scroll,
  .fixed-ui--top { display: none; }
  .fixed-ui--logo {
    top: calc(16px + env(safe-area-inset-top));
    left: calc(16px + env(safe-area-inset-left));
    margin: 0;
  }
  .fixed-ui--menu {
    top: calc(16px + env(safe-area-inset-top));
    right: calc(16px + env(safe-area-inset-right));
    margin: 0;
  }

  .overview .section-label,
  .overview__grid { grid-column: 2 / 3; }
  .overview__grid { grid-template-columns: 1fr; gap: 48rem; }
  .overview__col:not(:first-child) { border-left: none; padding-left: 0; }
  .overview__stage { height: 260rem; }
  /* Let a finger-swipe that starts on a pictogram scroll the page.
     `touch-action: none` (the desktop rule) tells the browser "this
     element handles all touch gestures itself", which is what made the
     3D models spin instead of the page scrolling. pictograms.js also
     skips its touch drag listeners on coarse-pointer devices, so the
     models keep their idle auto-rotation but never capture the gesture.
     REVERT: delete this rule and the isCoarsePointer guard in
     pictograms.js to restore drag-to-rotate on touch. */
  .overview__stage,
  .benefit__stage,
  .service__pict {
    touch-action: auto;
    cursor: default;
  }

  /* "Who We Are" / "What We Do" / "How We Work" each on a single line.
     The binding case is the longest, "How We Work", which renders at
     ~6.43x the font size in this weight — so the largest size that fits
     is (content column width / 6.43). The content column is
     `100vw - 2 * --sp-content-padding`, and 13vw sits just under that
     ceiling at every tested width with a little slack for font-metric
     rounding:
       320px -> 41.6px (fits 42.3px)   375px -> 48.8px (fits 50.2px)
       390px -> 50.7px (fits 52.2px)   430px -> 55.9px (fits 57.6px)
     The 58px cap holds the size steady on wider phones (where the column
     keeps growing, so no overflow); the 38px floor keeps it a display
     heading on very narrow screens.
     REVERT: the previous value was the shared desktop `font-size: 64rem`
     (inherited, no mobile override) — delete this rule to go back. */
  .overview__title {
    font-size: clamp(38px, 13vw, 58px);
    line-height: 1.05;
  }

  .hello .section-label,
  .hello__title,
  .hello__pict,
  .hello__body { grid-column: 2 / 3; }
  .hello__pict { margin-bottom: 32rem; }
  .hello__title { font-size: 72rem; }
  /* the 36rem bold statement is desktop-comp sized; scaled down for the
     430-base phone comp. Previously this also cancelled inter-character
     justify on mobile (text-align:left/text-justify:none) because at
     ~34px it stretched letter-spacing awkwardly — the shared
     flush-both-edges rule now applies here too (client preference: flush
     right edge over perfectly uniform spacing), so that cancellation is
     removed and this only overrides font-size.
     26rem -> 22rem: at 26rem the paragraph sets 9-10 lines whose last line
     collapses to a 2-word orphan and whose right edge steps by ~190px.
     22rem is the size at which the phrase-aware wrapping (auto-phrase plus
     the .nb no-break units in index.html) resolves to a balanced 8-line
     composition at 320 / 375 / 390 / 430px:
       MATÉRIAは、優れた素材・技術・ / 製造力を持つ地域の事業者や職人と /
       協業し、その価値を現代の市場に / 適した商品やブランドへ再編集する /
       共同開発型の事業です。 / 技術を保存するのではなく、 /
       更新する——つくる力と、 / 届ける力をつなぎます。
     REVERT: set back to 26rem for the previous (larger) setting; add back
     text-align:left; text-justify:none; to cancel justify on mobile. */
  .hello__ja { font-size: 22rem; }

  .service .section-label,
  .service__statement,
  .service__list { grid-column: 2 / 3; }
  /* Tighten the flow rail on phones. padding-left is trimmed from the
     desktop value (78rem) down to just enough to clear the rail
     (--rail-x + line width + a small gap) — previously 44rem, which
     combined with the item grid's own 70rem num-column + 20rem gap put
     the heading/desc text ~134rem from the gutter, well past where
     .partner__item / .benefit__item's number-list text lands (~88rem:
     64rem num + 24rem gap). 20rem here brings it to ~110rem — closer to
     the other numbered lists, without crowding the rail against the
     "0" in "01". */
  .service__list { --rail-x: 8rem; padding-left: 20rem; }
  .service__item { grid-template-columns: 70rem 1fr; gap: 20rem; }
  /* long service names can't fit one line on phones: shrink + allow wrap */
  .service__name { font-size: 44rem; white-space: normal; }
  /* the JA process statement is a 36rem desktop-comp size — too large on phones */
  .service__statement .line:last-child { font-size: 26rem; }
  /* the per-step pictograms need the wide desktop grid; hide on phones */
  .service__pict { display: none; }

  .partner .section-label,
  .partner__title,
  .partner__lead,
  .partner__list { grid-column: 2 / 3; }
  .partner__list { grid-template-columns: 1fr; column-gap: 0; }
  .partner__title { font-size: 72rem; }
  .partner__lead { font-size: 26rem; }
  /* Enable every manually-authored break (see .mobile-break above) so the
     affected paragraphs read as natural, sense-based lines instead of
     greedy-wrapping into short, over-stretched justified fragments. */
  .mobile-break { display: inline; }

  .benefit .section-label,
  .benefit__title,
  .benefit__list-wrap,
  .benefit__roles { grid-column: 2 / 3; }
  .benefit__list-wrap { grid-template-columns: 1fr; row-gap: 40rem; }
  /* undo the desktop absolute-positioning trick; stack it back in-flow below the list */
  .benefit__gain-stage {
    position: static;
    transform: none;
    grid-column: 1;
    width: 100%;
    max-width: 260rem;
    margin: 0 auto;
  }
  .benefit__item { margin-right: 0; padding-right: 0; }
  .benefit__roles { grid-template-columns: 1fr; gap: 56rem; }
  .benefit__title { font-size: 72rem; }
  /* Japanese role headings ("MATÉRIAが担うこと" / "パートナーに期待すること")
     on a single line each. The longer of the two runs ~13.7x the font
     size including the 0.14em tracking, so the ceiling is
     (content column / 13.7) — 6vw stays just under it everywhere:
       320px -> 19.2px (fits 19.9px)   375px -> 22.5px (fits 23.5px)
       390px -> 23.4px (fits 24.5px)   430px -> 25.8px (fits 27.0px)
     The wide 0.14em tracking is kept as-is (it's the label's intended
     look) rather than tightened to buy a larger size.
     REVERT: the previous value was the shared desktop `font-size: 36rem`
     (inherited, no mobile override) — delete this rule to go back. */
  .benefit__role-k {
    font-size: clamp(17px, 6vw, 26px);
    white-space: nowrap;
  }

  .founder .section-label,
  .founder__body { grid-column: 2 / 3; }
  .founder__body { grid-template-columns: 1fr; column-gap: 0; row-gap: 40rem; }
  .founder__head { grid-column: 1; grid-row: 1; }
  .founder__bio  { grid-column: 1; grid-row: 2; }
  .founder__body::after { display: none; }

  .faq .section-label,
  .faq__title,
  .faq__list { grid-column: 2 / 3; }
  .faq__title { font-size: 72rem; }
  /* .faq__q / .faq__a carry their own 8rem horizontal padding on desktop
     (room to breathe around the +/- mark) — on mobile that put the
     question text ~8rem right of every other section's flush-left text
     and the answer paragraph another 8rem in from that. Zeroed here so
     both align to the shared gutter like everything else; the mark icon
     lands flush with the right edge instead, which also gives it the same
     right-hand baseline as the section's other content. */
  .faq__q { padding: 24px 0; }
  .faq__a { padding: 0 0 32px; }

  .contact .section-label,
  .contact__title,
  .contact__lead,
  .contact__list { grid-column: 2 / 3; }
  .contact__label { font-size: var(--fs-7); }
  /* Same reasoning as .faq__q above: .contact__link's 24rem horizontal
     padding (desktop) offset the label text from the shared gutter more
     than any other section's text — zeroed so the label's left edge and
     the arrow icon's right edge both land on the common baseline. */
  .contact__link { padding: 24px 0; }

  .contact-form .section-label,
  .contact-form__lead,
  #contactForm { grid-column: 2 / 3; }
  .contact-form__fields { max-width: none; }
  .contact-form__sent { max-width: none; }

  /* SP fallback: titles always visible, no hover swap */
  .work__title { height: auto; }
  .work__title span:last-child { display: none; }

  .footer__col--nav,
  .footer__col--meta,
  .footer__copy { grid-column: 2 / 3; }
  .footer__col--pict { display: none; }   /* stacked single-column footer has no room for a background pictogram */
  /* undo the desktop grid-row:1 pin so the stacked order follows the DOM
     (nav → meta → copy) instead of showing address/contact above the nav */
  .footer__col--meta { grid-row: auto; }
  /* Matched to the section headings' 48px-at-390px size (see the shared
     rule at the end of this block) so the footer nav reads at the same
     scale as About / Partner With Us / Questions rather than the larger
     64rem (~58px) it used before.
     REVERT: set back to `font-size: 64rem;`. */
  .footer__col--nav a { font-size: clamp(36px, 12.3vw, 55px); }

  /* ---- Footer height: 1144px (136svh) -> one screen ----
     The stacked mobile footer inherited the desktop's generous spacing
     (120rem padding, 40rem meta gap) on top of a 6-item nav, so it ran
     ~300px past the viewport and always needed a second scroll to finish.
     Every value below is spacing only — no font sizes, no content — and
     together they bring the natural content height to ~825px, i.e. under
     the 100svh floor, so `min-height: 100svh` now sets the height and the
     block sits vertically centred in exactly one screen:
       390x844 -> 844px (was 1144)   375x812 -> 812px   430x932 -> 932px
       320x700 -> 712px (12px over; the narrowest phone is the one case
       that still needs a small scroll)
     REVERT: previous values were
       .footer { row-gap: 60rem; padding: 120rem 0; }   (padding inherited)
       .footer__col--nav ul { gap: 20rem; }             (inherited)
       .footer__col--meta { row-gap: 40rem; }           (inherited)
       .footer__block { gap: 8rem; }                    (inherited) */
  .footer { row-gap: 28rem; padding: 40rem 0; }
  .footer__col--nav ul { gap: 8rem; }
  .footer__col--meta { row-gap: 20rem; }
  .footer__block { gap: 6rem; }

  /* Not a .base-grid section, so it opts into the same shared margin
     directly rather than via grid-column. */
  .menu__grid { padding: 0 var(--sp-content-padding); justify-content: stretch; }
  .menu__nav { text-align: left; }
  /* Same 48px-at-390px size as the footer nav and the section headings, so
     the overlay menu matches the rest of the mobile type scale.
     REVERT: set back to `font-size: 64rem;`. */
  .menu__nav a { font-size: clamp(36px, 12.3vw, 55px); }
  .menu__sns { justify-content: start; text-align: left; }

  /* Smoother drawer-open reveal (mobile only). The reveal itself is a
     clip-path circle grown to 150% of the viewport — every frame of that
     0.8s transition forces the browser to re-rasterize the whole overlay
     subtree, including the blurred, actively-animating orb underneath.
     will-change promotes .menu to its own compositor layer up front
     (avoids a first-frame layer-creation hitch), and delaying the orb's
     own animations by --duration keeps them idle until the clip-path
     transition has settled, so the two aren't competing for paint budget
     in the same frames — the open motion reads as one continuous
     movement instead of a stutter.
     REVERT: remove will-change/backface-visibility from .menu, and drop
     the two `var(--duration)` delays from the animation shorthand below. */
  .menu {
    will-change: clip-path;
    backface-visibility: hidden;
  }

  /* Drawer orb motion (mobile only) — see @keyframes orbBreathe /
     orbHueShift near .menu__bg::before's base rule. */
  .menu__bg::before {
    animation: orbBreathe 6s ease-in-out var(--duration) infinite,
               orbHueShift 11s ease-in-out var(--duration) infinite;
  }

  /* Also not a .base-grid section (a centered full-bleed CTA-style
     statement, kept centered intentionally rather than left-aligned like
     the document sections) — its outer margin still matches the shared
     value so the section's overall bounds line up with the rest of the
     page even though the copy itself is centered within them. */
  .reel { padding: 160rem var(--sp-content-padding); }
  .reel__copy { font-size: 72rem; }

  .work-preview { display: none; }

  /* ---- Shared size for every English section heading ----
     Who We Are / What We Do / How We Work / About / Partner With Us /
     What You Gain / Questions / Contact previously carried three different
     sizes (overview 13vw≈50.7px, the 72rem group ≈65.3px, contact via
     --fs-11 ≈58.5px at 390px). Unified here to one 48px-at-390px value.
     12.3vw is that ratio (390 x 0.123 = 47.97px), so the headings keep
     scaling with the viewport instead of freezing at one pixel size:
       320px -> 39.4px   375px -> 46.1px   390px -> 48.0px   430px -> 52.9px
     The 55px cap holds them steady on wider phones and the 36px floor keeps
     them display-scale on very narrow ones. The longest string,
     "How We Work", renders ~309px at 48px against a 335px column, so every
     heading still sets on one line (Partner With Us / What You Gain keep
     their intentional two-line break).
     Declared last in this block so it wins on source order over the
     per-section rules above without needing !important.
     REVERT: delete this rule to restore the three original sizes. */
  .overview__title,
  .hello__title,
  .partner__title,
  .benefit__title,
  .faq__title,
  .contact__title {
    font-size: clamp(36px, 12.3vw, 55px);
  }
}

/* Landscape-compact only (short-height touch devices, e.g. 667×375,
   844×390): the portrait hero treatment above is still too tall for
   ~375–390px of height, so the lead/labels/particles stay hidden
   (redundant with the block above, kept explicit here for clarity) and
   the heading gets its own shorter clamp + tighter vertical padding so
   all 5 lines fit on one screen without the section growing past 100svh. */
@media (hover: none) and (orientation: landscape) and (max-height: 500px) {
  .hero__lead,
  .hero__label,
  .hero__particles { display: none; }

  /* Landscape counterpart to the portrait 23rem corner logo. Kept a touch
     smaller than portrait because vertical room is far tighter here.
     REVERT: delete this rule to inherit the portrait value. */
  .logo-img { height: 19rem; }

  .hero__grid {
    min-height: 100svh;
    padding: max(40px, env(safe-area-inset-top)) 0 max(28px, env(safe-area-inset-bottom));
    align-content: center;
  }

  .hero__copy {
    transform: none;
    font-size: clamp(30px, 7vw, 46px);
    line-height: .96;
  }
}

/* ============================================================
   Reduced motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .loader { display: none; }
  [data-reveal] * { transform: none !important; opacity: 1 !important; }
  .reel__thumb { display: none; }
  .work-preview, .cursor { display: none; }
  * { transition: none !important; animation: none !important; }
}
