:root {
  --bg: #ffffff;
  --fg: #111111;
  --blue: #b5c7d3;       /* PANTONE 13-4308 Baby Blue */
  --blue-text: #7691a3;  /* darkened for readable small text */
  --blue-soft: rgba(181, 199, 211, 0.3);
  --blue-softer: rgba(181, 199, 211, 0.16);
  --muted: #6c7d88;      /* blue-grey, derived from --blue */
  --line: #dbe3e9;       /* blue-tinted hairline */
  --maxw: 1100px;
  --font-body: "Helvetica Neue", "Noto Sans TC", "PingFang TC", Arial, sans-serif;
  --font-heading: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", "Helvetica Neue", Arial, sans-serif;
  /* Shared internal top/bottom padding for bordered grid-item content
     (currently the Services cells, and any future block of the same kind)
     — one variable so every bordered grid keeps the same amount of
     breathing room between its border and its content, at every screen
     size, without each page hard-coding its own number. */
  --grid-pad-block: clamp(36px, 5vw, 64px);
  /* Global Section Spacing System — the two gaps on either side of every
     main-content Section Divider (the <section> element's own border-top,
     used on About / Services / Works / Process / Contact to separate one
     content block from the next, plus the page-head → first-section gap).
     Deliberately asymmetric: more room is given AFTER the divider (before
     the next section's label/heading) than BEFORE it (after the previous
     section's last content), so a heading never reads as "resting on" the
     line above it. Scaled down for mobile inside the 760px media query
     below — same two names, smaller numbers, so every page updates
     together rather than each one hard-coding its own gap.
     This system is only for SECTION-level dividers. Component-level lines
     (Timeline lines, Card/Input borders, the nav's active-tab underline,
     list-row dividers like Contact's row separators) are intentionally
     untouched — they keep their own, much smaller, element-specific
     spacing and should never be confused with this. */
  --section-gap-before: clamp(48px, 5vw, 64px);  /* last content → divider */
  --section-gap-after: clamp(64px, 6vw, 72px);   /* divider → next label/heading */
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  overflow-x: hidden;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
}
h1, h2, .logo {
  font-family: var(--font-heading);
}
a { color: inherit; text-decoration: none; }
.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 32px; }

/* Nav */
header {
  position: sticky;
  top: 0;
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
  z-index: 10;
}
nav.wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 76px;
}
.logo {
  font-size: 15px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
}
.navlinks { display: flex; gap: 44px; }
.nav-item {
  position: relative;
  display: inline-block;
  overflow: hidden;
  height: 18px;
  line-height: 18px;
}
.nav-item .stack {
  display: flex;
  flex-direction: column;
  transition: transform 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
.nav-item .en,
.nav-item .zh {
  display: block;
  height: 18px;
  font-size: 13px;
  letter-spacing: 0.08em;
  white-space: nowrap;
}
.nav-item .en {
  text-transform: uppercase;
  color: var(--muted);
}
.nav-item .zh {
  color: var(--fg);
  letter-spacing: 0.04em;
}
/* Hover-reveal of the Chinese label — desktop/mouse only. Scoped so touch
   devices (which have no hover state) don't get a tap stuck mid-flip. */
@media (hover: hover) and (pointer: fine) {
  .nav-item:hover .stack { transform: translateY(-18px); }
  .nav-item:hover .en { color: var(--fg); }
}

/* Mobile menu toggle — hidden on desktop, shown only under the 760px
   breakpoint below, where .navlinks switches from an inline row to a
   dropdown panel. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: space-between;
  width: 26px;
  height: 16px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
}
.nav-toggle span {
  display: block;
  width: 100%;
  height: 1px;
  background: var(--fg);
  transition: transform 300ms ease-out, opacity 300ms ease-out;
}
header.nav-open .nav-toggle span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
header.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
header.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }

/* current page indicator */
.nav-item.active .en { color: var(--fg); }
.nav-item.active::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 1px;
  width: 100%;
  height: 2px;
  background: var(--blue);
}

/* Hero */
.hero {
  min-height: 82vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: 40px;
  /* Homepage has no <section> after the Hero, so the Hero itself is the
     "last section before the footer" — uses the same before-divider token
     as every other page's final section, so the footer's border-top
     doesn't end up sitting closer to the Hero's content than it does
     anywhere else on the site. padding-top is left as a flat 40px: there's
     no divider above the Hero to create breathing room from (no-border,
     it's the page's very first element). */
  padding-bottom: var(--section-gap-before);
  border-top: none;
}
.hero .eyebrow {
  font-size: 13px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 28px;
}
.hero h1 {
  font-size: clamp(40px, 7vw, 92px);
  font-weight: 200;
  line-height: 1.05;
  margin: 0 0 28px 0;
  letter-spacing: -0.01em;
}
.hero h1 em {
  font-style: normal;
  font-weight: 500;
  color: var(--fg);
}
.hero p {
  max-width: 520px;
  font-size: 17px;
  line-height: 1.8;
  color: var(--muted);
  margin: 0 0 40px 0;
}
.hero-links { display: flex; gap: 28px; }
.hero-links a {
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--fg);
}
.hero-links a:hover { color: var(--muted); border-color: var(--blue); }

/* Page header (for non-home pages) — shared by About / Services / Works /
   Process / Contact, so the vertical rhythm below (label → title → intro,
   each a visibly different-sized gap) and the breathing room before the
   divider are defined once here rather than per page.
   padding-bottom is the gap between the header's last line (intro, or the
   title itself on pages with no intro, or the cover image when one is set)
   and the <section> that follows — its border-top is the divider line, so
   this is what keeps text from reading as if it's resting on the line.
   Uses the same --section-gap-before token as every later section-to-
   section transition, so the FIRST divider on a page (page-head → first
   section) reads with the same breathing room as every one after it. */
.page-head {
  padding: 72px 0 var(--section-gap-before);
}
.page-head .eyebrow {
  font-size: 13px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: clamp(14px, 2vw, 20px);
}
.page-head h1 {
  font-size: clamp(32px, 5vw, 56px);
  font-weight: 200;
  margin: 0;
  letter-spacing: -0.01em;
}
.page-head-intro {
  font-size: 15px;
  line-height: 1.8;
  color: var(--muted);
  max-width: 560px;
  margin: clamp(20px, 3vw, 28px) 0 0 0;
}
/* Optional banner cover under the page title — stays within the same
   content width as the rest of the page (no full-bleed), so it reads as
   part of the same quiet, contained layout rather than a hero image.
   Hidden entirely until an image is set via admin (see .empty below). */
.page-head-cover {
  width: 100%;
  aspect-ratio: 21 / 9;
  background-color: var(--blue-softer);
  background-size: cover;
  background-position: center;
  border: 1px solid var(--line);
  margin-top: 40px;
}
.page-head-cover.empty { display: none; }

/* padding-top is the divider → content gap (the border-top is the divider
   line itself, drawn at the very top edge of the box, so this padding is
   what pushes this section's own label/heading down off the line).
   padding-bottom is the gap between THIS section's last content and the
   NEXT section's divider line (or the footer's, if this is the page's
   last section) — both read from the shared Global Section Spacing System
   tokens above, which also scale down together under the mobile query. */
section {
  padding-top: var(--section-gap-after);
  padding-bottom: var(--section-gap-before);
  border-top: 1px solid var(--line);
}
section.no-border { border-top: none; }

/* Section Divider — a standalone element (real document-flow <div>, never
   a border-bottom/border-top borrowed from a content block) used on the
   About page between Philosophy → Studio Development and Studio
   Development → Founder / Designer. It is the SINGLE source of the gap on
   both sides: margin-top is "last content → line", margin-bottom is
   "line → next heading". No transform/position hack and no negative
   margin anywhere in this component — just real padding/margin in normal
   flow, so the space it creates is actual layout space, not a visual
   illusion.
   The three sections it sits between (.about-philosophy-section,
   .studio-section, .founder-section, below) each have their adjoining
   padding explicitly zeroed out specifically so nothing else also tries
   to create a gap here — if both a section's padding AND this divider's
   margin were active at the same boundary, the two would stack into an
   accidentally-doubled gap instead of the precise, debuggable distance
   requested. Those sections also carry .no-border, since this div (not
   the next section's border-top) is now what draws the line. */
.section-divider {
  width: 100%;
  height: 1px;
  background-color: var(--line);
  margin: clamp(48px, 5vw, 56px) 0 clamp(40px, 5vw, 48px) 0;
}
/* Tablet: between the mobile breakpoint and ~1024px, slightly tighter than
   full desktop but still clearly more than mobile's compressed spacing. */
@media (max-width: 1024px) and (min-width: 761px) {
  .section-divider {
    margin: clamp(40px, 5vw, 48px) 0 clamp(36px, 5vw, 44px) 0;
  }
}
@media (max-width: 760px) {
  .section-divider {
    margin: clamp(32px, 6vw, 40px) 0 clamp(32px, 6vw, 40px) 0;
  }
}

/* About page: these three sections sit between (or after) the two
   .section-divider elements above, so their own padding on that side is
   zeroed — the divider's margin is the only thing creating the gap there.
   Unrelated padding (page-head → Philosophy above, Founder → footer below)
   is left on the normal Global Section Spacing tokens, since neither of
   those boundaries uses a .section-divider. */
.about-philosophy-section { padding-bottom: 0; }
.studio-section { padding-top: 0; padding-bottom: 0; }
.founder-section { padding-top: 0; }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 72px;
}
.section-head .num {
  font-size: 13px;
  color: var(--muted);
  letter-spacing: 0.1em;
}
.section-head h2 {
  font-size: 36px;
  font-weight: 300;
  margin: 0;
  letter-spacing: -0.01em;
}

/* Works */
.works-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 64px 48px;
}
.work-card { display: block; cursor: pointer; }
.work-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--blue-softer);
  border: 1px solid var(--line);
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transition: background 0.3s ease;
}
.work-card:hover .work-thumb { background: var(--blue-soft); }
.work-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.work-meta h3 { font-size: 19px; font-weight: 400; margin: 0; }
.work-meta span { font-size: 13px; color: var(--muted); }
.work-tag { font-size: 13px; color: var(--muted); margin-top: 8px; }

/* About */
.about-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 48px;
  max-width: 640px;
  margin: 0 auto;
}
.about-grid p {
  font-size: 17px;
  line-height: 1.9;
  color: var(--muted);
  margin: 0 0 20px 0;
}
.skills { margin-top: 48px; display: flex; flex-wrap: wrap; gap: 12px; }
.skills span {
  font-size: 13px;
  padding: 8px 16px;
  border: 1px solid var(--blue);
  letter-spacing: 0.04em;
  transition: background 0.2s ease;
}
.skills span:hover { background: var(--blue-softer); }

/* Studio Development — Chen Pin Design's capability expansion since 2022,
   shown as a plain 3-stage line rather than a corporate timeline. No
   card/background/gradient/shadow anywhere — same flat editorial language
   as the rest of the site. .studio-desktop (horizontal line) and
   .studio-mobile (vertical axis) hold the SAME content twice and are
   toggled purely via the media query below — same dual-markup approach the
   Process page already uses for its desktop detail panel vs. mobile inline
   expand, so no JS is needed to move content between layouts. */
.studio-label {
  font-size: 13px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: clamp(32px, 5vw, 56px);
}

/* shared dot used by both the desktop track and the mobile vertical line */
.studio-dot {
  display: block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--blue-text);
  background-color: var(--bg);
}
/* 03 — Integration is the studio's current primary focus; emphasised only
   through a solid node + slightly deeper text colour/weight, never size,
   colour saturation, or a background. */
.studio-dot.studio-node-primary {
  background-color: var(--fg);
  border-color: var(--fg);
}

.studio-mobile { display: none; }

.studio-years-row {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
}
.studio-year {
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--muted);
}
.studio-track {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 3px;
  margin-bottom: clamp(32px, 4vw, 48px);
}
.studio-track::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background-color: var(--line);
  transform: translateY(-50%);
  z-index: 0;
}
.studio-node {
  position: relative;
  z-index: 1;
  display: flex;
  background-color: var(--bg);
  padding: 0 2px;
}
.studio-stages {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 48px;
}
.studio-stage-idx {
  font-size: 13px;
  color: var(--blue-text);
  letter-spacing: 0.05em;
  margin-bottom: 10px;
}
.studio-stage-en {
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}
.studio-stage-zh {
  font-size: 17px;
  font-weight: 300;
  color: var(--fg);
  margin-bottom: 12px;
}
.studio-stage-desc {
  font-size: 14px;
  line-height: 1.8;
  color: var(--muted);
  margin: 0;
  word-break: keep-all;
  overflow-wrap: break-word;
}
/* Integration's extra weight — purely typographic (deeper colour on the
   English/Chinese labels, slightly heavier Chinese weight), no size jump,
   no accent colour, no badge. */
.studio-stage-primary .studio-stage-en,
.studio-stage-primary .studio-stage-zh {
  color: var(--fg);
}
.studio-stage-primary .studio-stage-zh { font-weight: 400; }

/* Editorial Statement — a small magazine-style "large keyword + small
   description" composition, replacing the old full-width body-copy
   paragraph. Deliberately NOT body text: right-aligned, only ~60% of the
   content width, large/light keyword on the left, a narrow (max 460px)
   muted description column on the right — reads as a quiet typographic
   accent under the 01/02/03 line rather than a continuation of it.
   margin-top is its own, larger gap (clamp 56-72px desktop) distinct from
   the shared --section-gap-* tokens: those govern divider-adjacent
   spacing, this one specifically separates "01/02/03 has finished" from
   "now a new, different kind of content begins" within the same section.
   No card/border/background/shadow anywhere — pure typography. */
.studio-statement {
  display: flex;
  align-items: flex-start;
  gap: 28px;
  width: 60%;
  margin: clamp(56px, 6vw, 72px) 0 0 auto;
}
.studio-statement-keyword {
  flex-shrink: 0;
  font-size: clamp(26px, 2.6vw, 34px);
  font-weight: 300;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--fg);
}
/* English support label — smaller, muted, secondary to the Chinese
   keyword by design (per "不要同時做得很搶眼"). Stacked below it on
   desktop; moves inline right after the Chinese on mobile instead (see
   the 900px query below), which is both more compact and easier to read
   once the layout goes single-column. */
.studio-statement-keyword-en {
  display: block;
  margin-top: 8px;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.studio-statement-desc {
  flex: 1;
  min-width: 0;
  max-width: 460px;
}
.studio-statement-desc p {
  margin: 0 0 20px 0;
  font-size: 14.5px;
  font-weight: 300;
  line-height: 1.85;
  color: var(--muted);
  word-break: keep-all;
  overflow-wrap: break-word;
}
.studio-statement-desc p:last-child { margin-bottom: 0; }

/* Below ~900px three text columns under a horizontal line get cramped
   before the site's usual 760px mobile line, so Studio Development gets
   its own, earlier switch-over — matches the brief's "tablet 可以提前切換
   成 Vertical Timeline" allowance. This is a full markup swap (not a
   transform/scale), reusing Process's "line only ever drawn between dots,
   never through one" technique for the connector. */
@media (max-width: 900px) {
  .studio-desktop { display: none; }
  .studio-mobile { display: block; }

  .studio-m-year {
    font-size: 12px;
    letter-spacing: 0.1em;
    color: var(--muted);
    margin-bottom: 20px;
  }
  .studio-m-year-end { margin-top: 20px; margin-bottom: 0; }
  .studio-m-item {
    position: relative;
    display: flex;
    gap: 20px;
    padding-bottom: clamp(32px, 6vw, 44px);
  }
  .studio-m-item:last-of-type { padding-bottom: 0; }
  .studio-m-line {
    position: relative;
    flex-shrink: 0;
    width: 9px;
    display: flex;
    justify-content: center;
  }
  .studio-m-line .studio-dot { margin-top: 7px; }
  /* the connecting line only ever runs from this dot's bottom edge to the
     NEXT dot's top edge — never drawn behind a dot — so there is no
     masking-dependent overlap to go wrong, same fix already proven on the
     Process page's circles. */
  .studio-m-item:not(:last-of-type) .studio-m-line::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 16px;
    bottom: calc(-1 * clamp(32px, 6vw, 44px));
    width: 1px;
    background-color: var(--line);
    transform: translateX(-50%);
  }
  .studio-m-body { flex: 1; min-width: 0; padding-top: 1px; }
  .studio-m-item .studio-stage-desc { max-width: 480px; }
  .studio-m-item-primary .studio-stage-en,
  .studio-m-item-primary .studio-stage-zh {
    color: var(--fg);
  }
  .studio-m-item-primary .studio-stage-zh { font-weight: 400; }

  /* Editorial Statement, mobile: single column instead of the desktop
     two-column keyword+description split (keeps the Chinese from being
     squeezed into a narrow left column), left-aligned, full width. The
     English label moves inline right after the Chinese keyword instead of
     stacking under it — more compact and easier to read at this width,
     per the annotated reference. */
  .studio-statement {
    flex-direction: column;
    width: 100%;
    gap: 16px;
    margin: clamp(40px, 7vw, 56px) 0 0 0;
  }
  .studio-statement-keyword {
    font-size: 24px;
  }
  .studio-statement-keyword-en {
    display: inline;
    margin-top: 0;
    margin-left: 10px;
  }
  .studio-statement-desc {
    max-width: none;
  }
}

/* Founder / Designer — second block on the About page, own <section> so it
   automatically inherits the site-wide divider (border-top) + generous
   section padding instead of needing a one-off custom divider. Plain
   photo + info split, no card/shadow/rounded container — same flat
   editorial language as everywhere else on the site. */
.founder-label {
  font-size: 13px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: clamp(32px, 5vw, 56px);
}
.founder-grid {
  display: grid;
  grid-template-columns: 43% 57%;
  gap: 64px;
  align-items: start;
}
/* Photo is deliberately smaller than its column (not a full-bleed fill) —
   this is an actual layout-size reduction (~80% of the column), not a
   transform: scale(), so the freed space reads as real negative space
   between the portrait and the info column rather than visual shrinkage.
   The founder-grid column split itself (43% / 57%) is untouched, so the
   info column's width/typography never shifts. */
.founder-photo-wrap {
  width: 80%;
  aspect-ratio: 4 / 5;
  background-color: var(--blue-softer);
  overflow: hidden;
}
.founder-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
/* hidden (not just transparent) until admin actually uploads a photo, so
   there's never a broken-image icon — the wrapper's pale placeholder
   background shows through on its own */
.founder-photo.empty { display: none; }

.founder-name {
  font-size: 22px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--fg);
  margin: 0 0 8px 0;
}
/* English title is the primary secondary-line (right under the name); the
   Chinese title directly beneath it is an auxiliary translation, so it sits
   closer to the English line (small gap) than the English line sits to the
   next block (medium gap to Professional Journey) — neither competes with
   CHEN PIN-YUAN above. */
.founder-title {
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 0 0 4px 0;
}
.founder-title-zh {
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin: 0 0 clamp(20px, 3vw, 28px) 0;
}

/* Professional Journey — a small standalone statement (not body text), so
   it gets its own, slightly larger typography tier between the title and
   the introduction paragraph: a small muted label, then the statement
   itself set larger/darker than the label but still well under the name's
   size. No card/background/border/icon/tag — purely typography + a plain
   "→" (drawn via ::after, not typed into the data, same technique as the
   Expertise "/" separators below). flex-wrap lets it fall back to wrapping
   rather than ever overflowing horizontally on narrow phones. */
.founder-journey {
  margin: 0 0 clamp(32px, 4vw, 44px) 0;
}
.founder-journey-label {
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--muted);
  margin: 0 0 8px 0;
}
.founder-journey-statement {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 6px;
  font-size: 18px;
  font-weight: 400;
  color: var(--fg);
  word-break: keep-all;
  overflow-wrap: break-word;
}
.founder-journey-statement span:not(:last-child)::after {
  content: "→";
  margin-left: 6px;
  color: var(--blue-text);
  font-size: 0.8em;
}

.founder-intro {
  font-size: 16px;
  line-height: 1.9;
  color: var(--muted);
  max-width: 480px;
  margin: 0 0 clamp(32px, 4vw, 44px) 0;
  white-space: pre-line;
  word-break: keep-all;
  overflow-wrap: break-word;
}
/* Typography only — no buttons/tags/borders. A plain "/" between items,
   drawn by CSS (not typed into the data) so it never leaks into contexts
   that read the raw list differently. Wraps freely on narrow screens. */
.founder-expertise {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  font-size: 13px;
  letter-spacing: 0.03em;
  color: var(--fg);
  margin: 0 0 clamp(22px, 3vw, 30px) 0;
}
.founder-expertise span { white-space: nowrap; }
.founder-expertise span:not(:last-child)::after {
  content: "/";
  margin-left: 10px;
  color: var(--blue-text);
}
.founder-location {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.founder-location-label {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue-text);
}
.founder-location-value {
  font-size: 14px;
  color: var(--fg);
}

/* Founder's personal Instagram — typography only, never a button: no
   background, border, circle fill, brand gradient or shadow. Icon and
   label share one colour (--muted) and transition together on hover.
   padding (offset by a matching negative margin so it doesn't push later
   content down) only grows the tap target — the icon's own rendered size
   is set inline from admin's icon-size field and never changes here. */
.founder-social {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: clamp(14px, 2vw, 22px) 0 -8px 0;
  padding: 8px 0;
  color: var(--muted);
  transition: color 0.2s ease, opacity 0.2s ease;
}
@media (hover: hover) and (pointer: fine) {
  .founder-social:hover { color: var(--fg); }
}
.founder-social-icon-wrap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.founder-social-icon-default,
.founder-social-icon-custom {
  width: 20px;
  height: 20px;
  display: block;
}
.founder-social-icon-custom { display: none; object-fit: contain; }
.founder-social-label {
  font-size: 13px;
  letter-spacing: 0.02em;
}

/* Tablet: switches to single column earlier than the site's usual 760px
   breakpoint specifically for this layout — a photo+text split gets
   cramped sooner than other two-column sections do. The photo keeps its
   ~80% layout-size reduction (width: 80%, inherited from the base rule
   above) and is additionally capped so it can't grow oversized once it's
   no longer sharing the row with text; the true mobile query below lifts
   this cap and sets its own phone-appropriate width instead. */
@media (max-width: 860px) {
  .founder-grid { grid-template-columns: 1fr; gap: 40px; }
  .founder-photo-wrap { max-width: 336px; }
}

/* Services — bento-style grid: 01 full width, 02/03 side by side (equal
   height via grid's default row stretch), 04 (Integration) full width.
   All content is shown by default — no accordion / expand-on-hover.
   Flat, editorial / Swiss-grid language throughout: white background never
   changes, no shadow, no blur, no gradient, no scale, no lift. Interaction
   reads entirely through typography, line weight/colour and spacing. */
.services-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  margin-top: 8px;
}
.service-cell.cell-01,
.service-cell.cell-04 {
  grid-column: 1 / -1;
}
.service-cell {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 0;
  background-color: var(--bg);
  /* internal top/bottom padding via the shared --grid-pad-block variable,
     so content doesn't sit flush against the card's own border; horizontal
     padding is kept separate and unchanged */
  padding-block: var(--grid-pad-block);
  padding-inline: 40px;
  transition: border-color 400ms ease-out;
}
.service-cell .idx {
  font-size: 13px;
  color: var(--blue-text);
  letter-spacing: 0.05em;
  margin-bottom: 20px;
}
/* Tiny arrow beside the number — the main hover cue at the top of each
   cell. Same typography as the number itself, fades in only; it implies
   "more detail below", not a link, so the cursor never changes. */
.service-cell .idx-arrow {
  display: inline-block;
  margin-left: 6px;
  opacity: 0;
  transition: opacity 350ms ease-out;
}
.service-cell h3 {
  font-size: 21px;
  font-weight: 400;
  letter-spacing: -0.01em;
  margin: 0 0 10px 0;
  transition: transform 400ms ease-out, letter-spacing 400ms ease-out;
}
.service-cell .en-label {
  display: block;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 22px;
  transition: color 400ms ease-out;
}
.service-cell p.desc {
  font-size: 14.5px;
  line-height: 1.8;
  color: var(--muted);
  max-width: 560px;
  margin: 0;
  opacity: 0.78;
  /* respects manual line breaks typed into the content (e.g. 01's
     description), and keeps mixed CJK/Latin runs like "Logo、色彩" from
     being split mid-phrase by the browser's default line-break points.
     overflow-wrap is a safety net: keep-all refuses to break *inside* a
     run of CJK characters, so on narrow phone widths a long unbroken
     segment (a sentence with no comma/space for a while) can run past the
     edge of the card instead of wrapping — overflow-wrap lets the browser
     break it as a last resort rather than overflow the container. */
  white-space: pre-line;
  word-break: keep-all;
  overflow-wrap: break-word;
  word-wrap: break-word;
  transition: opacity 400ms ease-out;
}
.service-cell.cell-01 p.desc {
  max-width: 680px;
}

/* 01 is the studio's most central service. No special material — just a
   larger original footprint (more room, bigger title) so it carries more
   visual weight before anyone even hovers. Its vertical padding stays a
   bit more generous than the other cells (consistent with that), scaling
   alongside --grid-pad-block rather than as a fixed number. */
.service-cell.cell-01 {
  padding-block: calc(var(--grid-pad-block) + 6px);
  padding-inline: 44px;
}
.service-cell.cell-01 h3 { font-size: 23px; }

/* Hover / active — border is the primary cue: pale grey-blue deepens to
   near-black, 1px the whole time (never thicker). Everything else is
   typography: arrow fades in, title nudges right, English label deepens,
   intro copy comes to full opacity. Scoped to real hover (mouse) only —
   touch uses .is-open instead (toggled in JS), so a tap never leaves a
   card "stuck" looking hovered. */
@media (hover: hover) and (pointer: fine) {
  .service-cell:hover { border-color: var(--fg); }
  .service-cell:hover .idx-arrow { opacity: 1; }
  .service-cell:hover h3 { transform: translateX(4px); }
  .service-cell.cell-01:hover h3 { letter-spacing: 0.01em; }
  .service-cell:hover .en-label { color: #45515a; }
  .service-cell:hover p.desc { opacity: 1; }
}
.service-cell.is-open { border-color: var(--fg); }
.service-cell.is-open .idx-arrow { opacity: 1; }
.service-cell.is-open h3 { transform: translateX(4px); }
.service-cell.cell-01.is-open h3 { letter-spacing: 0.01em; }
.service-cell.is-open .en-label { color: #45515a; }
.service-cell.is-open p.desc { opacity: 1; }

/* 01 / 02 / 03 / 04 hover/tap-expand — reveals extra columns beneath the
   existing title/description without replacing them. Built on the
   grid-template-rows 0fr→1fr technique so the height animates smoothly
   with no JS measuring. 01 is full-width in its own row so expanding it
   never shifts 02/03; 02 and 03 sit side by side and, being an equal-height
   grid row by default, may stretch to match whichever one is open — kept
   in check by giving both a similarly-sized two-column breakdown. */
.cell-expand {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 450ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.cell-expand-inner {
  overflow: hidden;
  min-height: 0;
}
/* Divider draws itself in left→right (scaleX, not width, so it never
   triggers layout) as the panel opens, then deepens from pale line-blue to
   a readable grey-blue once open — a quiet two-step rather than everything
   happening at once. */
.cell-divider {
  height: 1px;
  background-color: var(--line);
  margin-top: 32px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 550ms ease-out, background-color 300ms ease-out 400ms;
}
.service-cell:hover .cell-divider,
.service-cell.is-open .cell-divider {
  transform: scaleX(1);
  background-color: var(--muted);
}
.cell-cols {
  display: grid;
  gap: 32px;
  padding-top: 32px;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 400ms ease-out 50ms, transform 400ms ease-out 50ms;
}
/* 01 covers three categories (brand core / visual system / application);
   02 and 03 each cover two; 04 (Integration) covers five — column count
   follows the content. 04's five columns run tighter on gap so
   "COMMERCIAL PHOTOGRAPHY" has room to wrap onto a second line without
   ever widening its column relative to the other four.
   Scoped to min-width: these selectors (three classes deep) are more
   specific than the mobile ".cell-cols { grid-template-columns: 1fr }"
   override below, so without this media guard they'd win on phones too
   and cram 3/2/5 columns into a narrow screen regardless of viewport. */
@media (min-width: 761px) {
  .service-cell.cell-01 .cell-cols { grid-template-columns: repeat(3, 1fr); }
  .service-cell.cell-02 .cell-cols,
  .service-cell.cell-03 .cell-cols { grid-template-columns: repeat(2, 1fr); }
  .service-cell.cell-04 .cell-cols { grid-template-columns: repeat(5, 1fr); gap: 24px; }
}

.cell-col { min-width: 0; }
.cell-col .col-idx {
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.05em;
  margin-bottom: 12px;
}
.cell-col .col-en {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}
.cell-col .col-zh {
  font-size: 15px;
  font-weight: 500;
  color: var(--fg);
  margin-bottom: 18px;
}
.cell-col .col-list { display: flex; flex-direction: column; gap: 8px; }
.cell-col .col-list span {
  display: block;
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
}

/* Expand on hover — desktop/mouse only, so a touchscreen never gets stuck
   in a "hover" state after a tap. */
@media (hover: hover) and (pointer: fine) {
  .service-cell.has-expand:hover .cell-expand { grid-template-rows: 1fr; }
  .service-cell.has-expand:hover .cell-cols {
    opacity: 1;
    transform: translateY(0);
  }
}
/* Expand on tap — toggled via JS (see services.html), covers touch devices
   and also works as a keyboard/click fallback on desktop. Only one cell is
   ever marked is-open at a time (handled in JS). */
.service-cell.has-expand.is-open .cell-expand { grid-template-rows: 1fr; }
.service-cell.has-expand.is-open .cell-cols {
  opacity: 1;
  transform: translateY(0);
}

/* Process / How We Work — vertical timeline (left) + cross-fading detail
   panel (right). No cards, no per-item borders: structure reads entirely
   through the thin connecting line, the circular step markers, and
   typography weight/colour, consistent with the rest of the site's flat
   editorial language. */
.process-timeline {
  display: grid;
  grid-template-columns: 45% 55%;
  gap: 64px;
  align-items: start;
  /* Pulls both columns in from the section's full width toward the center
     (desktop only — reset to 0 under the mobile query below), rather than
     the two groups sitting at the far left/right edges with an oversized
     gap of nothing in between. */
  padding-inline: 64px;
  /* Extra breathing room above AND below the timeline, on top of the
     section's own padding — a circle sitting right at a horizontal line
     reads as "touching" it even at a gap plain text would be fine with,
     so this needs to be clearly more generous on both ends. Scales from
     ~44px on phones up to 80px on desktop. */
  padding-top: clamp(44px, 6vw, 80px);
  padding-bottom: clamp(44px, 6vw, 80px);
}

.process-nodes {
  position: relative;
  /* shared by the node spacing and the line-segment math below, so they
     can never drift out of sync */
  --process-gap: clamp(32px, 5vw, 56px);
}
.process-node {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 20px;
  margin-bottom: var(--process-gap);
}
.process-node:last-child { margin-bottom: 0; }
/* The connecting line only ever runs BETWEEN dots — from this node's own
   dot's bottom edge, down through the gap, to the very top edge of the
   next node (which is exactly where the next dot's top edge is). It never
   passes behind/through a dot's circular body, so there's no masking to
   get wrong and nothing can show through the circle. 04 has no line below
   it at all — it's the end of the timeline. */
.process-node::before {
  content: "";
  position: absolute;
  left: 19px;
  width: 1px;
  top: 40px;
  bottom: calc(-1 * var(--process-gap));
  background-color: var(--line);
  z-index: 0;
  transition: background-color 350ms ease-out;
}
.process-node:last-child::before { display: none; }

.node-dot {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background-color: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 350ms ease-out, background-color 350ms ease-out;
}
.node-idx {
  font-size: 13px;
  color: var(--blue-text);
  letter-spacing: 0.05em;
  transition: color 350ms ease-out;
}
.node-body {
  flex: 1;
  min-width: 0;
  padding-top: 9px;
}
.node-title-row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 14px;
}
.node-title {
  font-size: 17px;
  font-weight: 400;
  color: var(--muted);
  margin: 0;
  transition: color 350ms ease-out, transform 350ms ease-out;
}
/* Secondary to the Chinese title: smaller, uppercase, the site's existing
   blue-grey, start a little faded and come up slightly on hover/active —
   never competing with the Chinese for attention. */
.node-en {
  font-size: 11px;
  font-weight: 300;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--blue-text);
  opacity: 0.65;
  transition: opacity 350ms ease-out;
}
/* +/- expand indicator — mobile/touch only (see the 760px query below for
   its actual shape); on desktop there's nothing to tap-expand, so it's
   never rendered at all. margin-left:auto keeps it pinned to the row's far
   right regardless of how long the zh/en labels next to it are. */
.node-toggle { display: none; margin-left: auto; }
/* mobile-only inline expand — see .node-detail-mobile under the 760px
   query below; stays fully collapsed/hidden on desktop, where the detail
   lives in the right-hand panel instead */
.node-detail-mobile { display: none; }

/* Active state shared by desktop hover and the mobile tap toggle below —
   border/number/title deepen, title nudges right. No shadow, scale,
   gradient or background change. */
@media (hover: hover) and (pointer: fine) {
  .process-node:hover .node-dot { border-color: var(--fg); background-color: var(--blue-softer); }
  .process-node:hover .node-idx { color: var(--fg); }
  .process-node:hover .node-title { color: var(--fg); transform: translateX(4px); }
  .process-node:hover .node-en { opacity: 0.9; }
  .process-node:hover::before { background-color: var(--muted); }
  /* hovering any one node dims the others so it reads as the focused item —
     pure CSS sibling trick: the broader rule dims everyone, the more
     specific :hover-on-self rule (declared after) brings that one back */
  .process-nodes:hover .process-node { opacity: 0.55; }
  .process-nodes .process-node:hover { opacity: 1; }
}
.process-node.is-open .node-dot { border-color: var(--fg); background-color: var(--blue-softer); }
.process-node.is-open .node-idx { color: var(--fg); }
.process-node.is-open .node-title { color: var(--fg); transform: translateX(4px); }
.process-node.is-open .node-en { opacity: 0.9; }

/* Right-side detail — all four panels stacked in the same grid cell so
   switching between them never changes the area's height (sized to the
   tallest panel) and the crossfade has nothing to reflow against. */
.process-detail {
  position: relative;
  display: grid;
}
.process-detail-panel {
  grid-row: 1;
  grid-column: 1;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 400ms ease-out, transform 400ms ease-out;
  pointer-events: none;
}
.process-detail-panel.is-active {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.detail-idx {
  font-size: 13px;
  color: var(--blue-text);
  letter-spacing: 0.1em;
  margin-bottom: 16px;
}
.detail-title {
  font-size: 22px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--fg);
  margin: 0 0 16px 0;
}
.detail-desc {
  font-size: 15px;
  line-height: 1.8;
  color: var(--muted);
  max-width: 480px;
  margin: 0;
  white-space: pre-line;
  word-break: keep-all;
  overflow-wrap: break-word;
}

/* Work detail */
.back-link {
  display: inline-block;
  font-size: 13px;
  color: var(--muted);
  margin: 40px 0 8px 0;
}
.back-link:hover { color: var(--fg); }
.work-detail-cover {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--blue-softer);
  background-size: cover;
  background-position: center;
  border: 1px solid var(--line);
  margin: 24px 0 40px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.work-detail-meta {
  display: flex;
  gap: 24px;
  font-size: 13px;
  color: var(--muted);
  letter-spacing: 0.05em;
  margin-bottom: 12px;
}
.work-detail-title {
  font-size: clamp(28px, 4.5vw, 48px);
  font-weight: 300;
  letter-spacing: -0.01em;
  margin: 0 0 40px 0;
}
.work-detail-desc {
  font-size: 16px;
  line-height: 1.9;
  color: var(--muted);
  max-width: 720px;
  text-align: center;
  white-space: pre-line;
  margin: 0 auto var(--section-gap-before) auto;
}
/* Desktop: capped at 1280px and centered. Mobile (see media query below):
   breaks out full-bleed edge-to-edge instead. Images stack with no gap
   and no border between them either way.
   margin-bottom matters here specifically: this page has no <section>
   border-top of its own, but it still ends at the footer's border-top —
   same role as a Section Divider everywhere else on the site — and
   without this, a case study WITH a gallery would end with its last
   image sitting flush against that line (the gallery, not the already-
   spaced-out .work-detail-desc above, is the final element before
   </main> whenever it's populated). Uses the same before-divider token as
   every other page so this page's footer transition matches the rest of
   the site rather than needing its own one-off number. */
.work-gallery {
  max-width: 1280px;
  margin: 0 auto var(--section-gap-before) auto;
}
.work-gallery img {
  width: 100%;
  display: block;
  border: none;
  margin: 0;
}
.work-gallery.empty { display: none; }

/* Contact */
.contact {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 64px;
  align-items: start;
}
.contact h2 {
  font-size: clamp(32px, 5vw, 56px);
  font-weight: 200;
  line-height: 1.2;
  margin: 0 0 32px 0;
}
.contact-list { font-size: 15px; }
.contact-list .row {
  display: flex;
  justify-content: space-between;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}
.contact-list .label { color: var(--muted); letter-spacing: 0.08em; text-transform: uppercase; font-size: 12px; }
.contact-list .value a:hover { color: var(--muted); }

/* Social icons row — last row of the same Contact Information list, same
   border/padding/height as the rows above it. No label (there's nothing to
   left-align), so icons sit flush to the row's right edge instead — the
   same edge the other rows' Value column already aligns to. Rendered by
   contact.html's own script from admin-configured data; hidden via JS
   (display:none, not removed here) if nothing is enabled/configured yet. */
.contact-list .row.row-social {
  justify-content: flex-end;
  gap: 16px;
}
.social-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* invisible — purely a bigger, easier tap target than the icon itself,
     per "40-44px touch area, no visible background" */
  min-width: 44px;
  min-height: 44px;
  opacity: 0.55;
  cursor: pointer;
  transition: opacity 300ms ease-out, transform 300ms ease-out;
}
.social-icon img {
  display: block;
  object-fit: contain;
}
@media (hover: hover) and (pointer: fine) {
  .social-icon:hover {
    opacity: 1;
    transform: translateY(-2px);
  }
}

footer {
  border-top: 1px solid var(--line);
  padding: 32px 0;
}
footer .wrap {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--muted);
}

@media (max-width: 760px) {
  /* Global Section Spacing System, mobile values — same two tokens used by
     `section` and `.page-head` above, just smaller, so every page's
     section-to-section gaps shrink together rather than any one page
     needing its own override. */
  :root {
    --section-gap-before: clamp(32px, 6vw, 40px);
    --section-gap-after: clamp(40px, 7vw, 48px);
  }

  /* Nav: the inline row (logo + 5 items) doesn't fit on phone widths, so it
     collapses into a hamburger-triggered dropdown panel instead. The panel
     sits right under the sticky header, full-bleed, solid white (not the
     translucent header background, so text underneath never shows through).
     Each item shows both languages stacked and static — there's no hover on
     touch to reveal the Chinese label, so it's just shown directly, with 中文
     as the primary line, matching the eyebrow-label pattern used elsewhere
     on the site (small caps EN beneath a larger line). */
  .nav-toggle { display: flex; }
  .navlinks {
    position: fixed;
    top: 76px;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    max-height: 0;
    overflow: hidden;
    transition: max-height 350ms ease-out;
    z-index: 20;
  }
  header.nav-open .navlinks { max-height: 420px; }
  .nav-item {
    height: auto;
    overflow: visible;
    padding: 16px 32px;
    border-top: 1px solid var(--line);
  }
  .nav-item .stack { flex-direction: column-reverse; transform: none !important; }
  .nav-item .en { height: auto; font-size: 11px; margin-top: 3px; }
  .nav-item .zh { height: auto; font-size: 16px; }

  .works-grid { grid-template-columns: 1fr; }
  .about-grid { grid-template-columns: 1fr; }
  .contact { grid-template-columns: 1fr; }
  .hero { min-height: 70vh; }

  /* Phone: no longer the 80%-of-column desktop rule (there's no column to
     be a fraction of once it's full-width) — instead a phone-appropriate
     ~82% of the content width, left-aligned, with real negative space to
     its right rather than a scaled-down image. */
  .founder-photo-wrap { width: 82%; max-width: none; }

  /* How We Work: no hover on touch, so the right-hand detail panel drops
     out — each step's full description instead expands inline, directly
     under its own node, tap-to-open/collapse like the Services cells. */
  /* the desktop-only inward centering isn't needed once it's a single
     column — reset to full width so phones get all the room they have */
  .process-timeline { grid-template-columns: 1fr; gap: 0; padding-inline: 0; }
  .process-detail { display: none; }
  .node-toggle {
    display: block;
    position: relative;
    width: 12px;
    height: 12px;
    flex-shrink: 0;
  }
  .node-toggle::before,
  .node-toggle::after {
    content: "";
    position: absolute;
    background-color: var(--muted);
    transition: transform 300ms ease-out, background-color 350ms ease-out;
  }
  .node-toggle::before { left: 0; top: 50%; width: 100%; height: 1px; transform: translateY(-50%); }
  .node-toggle::after { left: 50%; top: 0; width: 1px; height: 100%; transform: translateX(-50%); }
  .process-node.is-open .node-toggle::after { transform: translateX(-50%) rotate(90deg); }
  .process-node.is-open .node-toggle::before,
  .process-node.is-open .node-toggle::after {
    background-color: var(--fg);
  }
  .node-detail-mobile {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 450ms cubic-bezier(0.22, 0.61, 0.36, 1);
  }
  .node-detail-mobile-inner { overflow: hidden; min-height: 0; }
  .node-detail-mobile-desc {
    font-size: 14px;
    line-height: 1.75;
    color: var(--muted);
    margin: 0;
    padding-top: 12px;
    white-space: pre-line;
    word-break: keep-all;
    overflow-wrap: break-word;
  }
  .process-node.is-open .node-detail-mobile { grid-template-rows: 1fr; }

  /* Services grid collapses to a single column: 01 / 02 / 03 / 04, content
     stays fully visible (no accordion either way) and hover-only motion is
     switched off since touch has no hover state to simulate. */
  .services-grid { grid-template-columns: 1fr; gap: 20px; }
  /* Vertical padding is left alone here — --grid-pad-block's own clamp()
     already settles at its ~36px floor at this width, which is where the
     old fixed 32px came from anyway. Only horizontal padding needs its own
     mobile value. */
  .service-cell { padding-inline: 28px; }
  /* 01 / 02 / 03 / 04's expand panels: single column, categories stacked in
     order, tap-to-open only (no hover on touch — handled by the
     (hover:hover) media rule already not matching here). Single column
     plus box-sizing: border-box (set globally) keeps this from ever
     overflowing horizontally. */
  .service-cell.cell-01 { padding-inline: 28px; }
  .service-cell.cell-01 h3 { font-size: 21px; }
  .cell-cols { grid-template-columns: 1fr; gap: 28px; padding-top: 28px; }

  /* gallery goes full-bleed edge-to-edge on mobile */
  .work-gallery {
    max-width: 100vw;
    width: 100vw;
    position: relative;
    left: 50%;
    margin-left: -50vw;
  }

  /* page-head banner: 21:9 reads as a thin sliver on narrow screens, so
     switch to a taller ratio instead of shrinking proportionally */
  .page-head-cover {
    aspect-ratio: 4 / 3;
    margin-top: 28px;
  }
}

/* Very small phones: 82% of content width can start feeling narrow once the
   content width itself is already small, so nudge the Founder photo up to
   ~90% rather than leaving it cramped — reading/layout balance takes
   priority over keeping the exact same ratio on every phone size. */
@media (max-width: 420px) {
  .founder-photo-wrap { width: 90%; }
}

@media (prefers-reduced-motion: reduce) {
  .service-cell,
  .service-cell .idx-arrow,
  .service-cell h3,
  .service-cell .en-label,
  .service-cell p.desc,
  .cell-divider,
  .cell-expand,
  .cell-cols {
    transition: none !important;
  }
  .service-cell:hover h3,
  .service-cell.is-open h3 {
    transform: none;
  }
  .service-cell.cell-01:hover h3,
  .service-cell.cell-01.is-open h3 {
    letter-spacing: -0.01em;
  }

  .process-node::before,
  .node-dot,
  .node-idx,
  .node-title,
  .node-en,
  .node-toggle::before,
  .node-toggle::after,
  .process-detail-panel,
  .node-detail-mobile {
    transition: none !important;
  }
  .process-node:hover .node-title,
  .process-node.is-open .node-title {
    transform: none;
  }
}
