/* ═══════════════════════════════════════════════════════════════
   데모 공용 골격 — skeleton.css

   ★ 이 파일에 색·폰트·여백 리터럴을 넣지 말 것. ★
     전부 var(--c-*, --t-*, --s-*)로만 참조한다. 그래야 데모마다
     tokens.css만 갈아끼워 다른 무드를 만들 수 있다.
     (하드코딩을 하나라도 넣는 순간 그 데모에서만 맞는 CSS가 된다)

   섹션 구성:
     sticky header + desktop-nav/mobile menu → hero → features
     → preview 교차 배치 → CTA split → footer
   ═══════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

/* ─── 타이포 역할 (v3 §1) ──────────────────────────────────────
   임의 font-size 를 쓰지 않는다. 아래 여덟 개 중 하나를 고른다.

   요소 기본값이 곧 역할이라, 평범한 <h2> 를 쓰면 §1 의 h2 가 나온다.
   역할과 문서 구조가 어긋나는 자리 — 대표적으로 섹션 대제목이 목차상
   <h2> 인데 크기는 h1 이어야 하는 자리 — 에서만 .t-* 를 덧붙인다.

     <h2 class="t-h1">일하는 방식</h2>

   ★ 굵기를 h1,h2,h3,h4 에 한 값으로 주지 않는다. v3 역할표가
     display 800 / h1 800 / h2 500 / h3 700 / h4 600 으로 갈라 두었고,
     인용구(h2)가 카드 제목(h3)보다 가늘어야 하는 이유가 거기 있다.
   ──────────────────────────────────────────────────────────── */
h1, h2, h3, h4 { margin: 0; font-family: var(--font-head); }

h1, .t-h1 { font-size: var(--t-h1); line-height: var(--lh-tight); letter-spacing: var(--ls-tight); font-weight: var(--fw-h1); }
h2, .t-h2 { font-size: var(--t-h2); line-height: var(--lh-snug);  letter-spacing: var(--ls-tight); font-weight: var(--fw-h2); }
h3, .t-h3 { font-size: var(--t-h3); line-height: var(--lh-snug);  letter-spacing: var(--ls-tight); font-weight: var(--fw-h3); }
h4, .t-h4 { font-size: var(--t-h4); line-height: var(--lh-snug);  letter-spacing: var(--ls-tight); font-weight: var(--fw-h4); }

.t-display { font-size: var(--t-display); line-height: var(--lh-tight); letter-spacing: var(--ls-tight); font-weight: var(--fw-display); }
.t-body    { font-size: var(--t-body);    line-height: var(--lh-body);  font-weight: var(--fw-body); }
.t-caption { font-size: var(--t-caption); line-height: var(--lh-body); }
.t-eyebrow { font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; }

/* 하위 호환 별칭 — 기존 마크업의 .h2 / .h3 */
.h2 { font-size: var(--t-h2); }
.h3 { font-size: var(--t-h3); }

/* ─── 레이아웃 ─────────────────────────────────────────────── */
.wrap { width: 100%; max-width: var(--w-base); margin: 0 auto; padding-inline: var(--gutter); }
.wrap-wide { max-width: var(--w-wide); }
.wrap-copy { max-width: var(--w-read); }
/* ★ 세로 여백을 변수로도 알린다. 블리드 이미지가 섹션 위아래 변까지
   닿으려면 그 여백만큼 되돌려야 하는데, 되돌릴 값을 이미지 쪽에서
   알 방법이 이것뿐이다. 상수로 적으면 section 과 section-sm 이 갈린다. */
.section { --sec-py: var(--section-py); padding-block: var(--sec-py); }
.section-sm { --sec-py: var(--section-py-sm); padding-block: var(--sec-py); }
.section + .section { border-top: var(--bw) solid var(--c-line); }
.bg-alt { background: var(--c-bg-alt); }


.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--t-eyebrow);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-accent);
  margin-bottom: var(--sp-2);
}
.lead { font-size: var(--t-lead); color: var(--c-muted); }
.muted { color: var(--c-muted); }
.faint { color: var(--c-faint); }
.mono { font-family: var(--font-mono); }
.stack > * + * { margin-top: var(--sp-2); }
.stack-lg > * + * { margin-top: var(--sp-4); }

/* ─── 헤더 ─────────────────────────────────────────────────── */
.hd {
  position: sticky; top: 0; z-index: 60;
  height: var(--header-h);
  background: color-mix(in srgb, var(--c-bg) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: var(--bw) solid var(--c-line);
}
.hd-in { height: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
/* 로고는 한 줄이다. 두 줄로 접힌 이름은 다른 이름처럼 보인다.
   ★ flex 자식은 기본으로 줄어들 수 있어서(min-width:auto 가 아닌 축소),
   폭이 2px 모자라도 글자가 먼저 접혔다. 줄바꿈을 막고, 대신 좁은
   화면에서 글자가 줄어들게 한다 — 접히는 것보다 작아지는 편이 낫다. */
.brand { display: flex; align-items: baseline; gap: var(--sp-1); flex-wrap: nowrap; flex-shrink: 0; white-space: nowrap; font-family: var(--font-head); font-weight: var(--fw-head); letter-spacing: var(--ls-head); }
.brand .b-mark, .brand .b-sub { white-space: nowrap; }
.brand .b-mark { font-size: var(--t-h4); }
.brand .b-sub { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); color: var(--c-faint); text-transform: uppercase; }
.nav { display: flex; gap: var(--sp-4); font-size: var(--t-caption); }
.nav a { color: var(--c-muted); padding-block: var(--sp-1); border-bottom: var(--bw) solid transparent; transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease); }
.nav a:hover { color: var(--c-text); }
.nav a[aria-current="page"] { color: var(--c-text); border-bottom-color: var(--c-accent); }
.hd-cta { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; border: var(--bw) solid var(--c-line-2); padding: 9px var(--sp-2); color: var(--c-text); background: none; transition: background var(--dur-fast) var(--ease); }
.hd-cta:hover { background: var(--c-surface); }
.burger { display: none; background: none; border: var(--bw) solid var(--c-line-2); width: 40px; height: 36px; color: var(--c-text); }

/* ─── 버튼 ─────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  font-family: var(--font-mono); font-size: var(--t-caption);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  padding: 15px var(--sp-4); border-radius: var(--radius);
  background: var(--c-btn-bg); color: var(--c-btn-fg);
  /* 테두리를 채움과 따로 둔다 (2.9차 I-1). --c-btn-line 을 정의하지 않은
     데모(리넨·얼로이)는 폴백으로 채움색을 그대로 써서 이전과 같다. */
  border: var(--bw) solid var(--c-btn-line, var(--c-btn-bg));
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.btn:hover { opacity: .88; transform: translateY(-1px); }
.btn-ghost { background: none; color: var(--c-text); border-color: var(--c-line-2); }
.btn-ghost:hover { background: var(--c-surface); opacity: 1; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

/* ─── 히어로 ───────────────────────────────────────────────── */
.hero { position: relative; background: var(--c-bg-alt); border-bottom: var(--bw) solid var(--c-line); }
/* 텍스트 쪽에 조금 더 준다 — 1:1이면 긴 한글 제목이 끊긴다 */
/* ═══ v2 §5-3 히어로 높이 규격 ═══════════════════════════════════
   ★ 순서를 뒤집었다. 지금까지는 사진 비율(.ph 의 aspect-ratio)이 히어로
     높이를 정했다 — 얼로이 소개의 3:4 사진 한 장이 히어로를 1031px 로
     만들었고, 그래서 페이지마다 520~1031px 로 흩어졌다(편차 511px).

     이제 화면이 높이를 정하고 사진이 그 높이를 채운다. 고객이 낼 자료
     기준(ph-size · demos.json)도 이 높이에서 역산한다. 명세가 레이아웃을
     정하는 게 아니라 레이아웃이 명세를 정한다.

   하한을 max() 로 함께 건다 — 뷰포트가 낮으면 52svh 가 380px 밑으로
   내려가 히어로가 납작해진다. */
.hero-in {
  display: grid; grid-template-columns: 1.08fr .92fr; gap: var(--sp-6);
  align-items: center;
  --sec-py: var(--section-py-sm);
  padding-block: var(--sec-py);
}

/* ★ 높이 규격과 비율 해제는 2열 히어로가 살아 있는 폭에서만 건다.
     아래 반응형 블록이 900px 에서 히어로를 한 열로 쌓는다. 거기서
     aspect-ratio 를 풀면 사진 칸의 높이를 정하는 것이 아무것도 없어
     50~129px 로 주저앉는다. 세로로 쌓인 히어로는 콘텐츠가 높이를
     정하는 게 맞다 — 그래서 901px 부터다(브레이크포인트 4단이 아니라
     레이아웃이 갈라지는 지점을 따라간다). */
@media (min-width: 901px) {
  /* ★ 히어로 여백은 섹션 리듬(--section-py-sm)을 따르지 않는다. 1920 에서
     96px 이라 위아래 192px 인데, 뷰포트가 낮으면 히어로가 하한 380px 까지
     내려가 미디어가 188px 짜리 띠가 된다(실측 비율 2.14까지 벌어졌다).
     높이를 규격으로 잡은 이상 여백은 고정값이어야 미디어 비율이 안정된다. */
  /* ★ --sec-py 도 함께 바꾼다. 블리드 이미지가 이 값만큼 여백을 되돌려
     섹션 위아래 변에 닿는데, 여백만 고정값으로 바꾸고 변수를 두면
     16px 씩 어긋난 채 섹션 밖으로 삐져나온다(실측 위 -16 · 아래 -15). */
  .hero-in { --sec-py: var(--sp-5); padding-block: var(--sec-py); min-height: max(var(--hero-h-sub-min), var(--hero-h-sub)); }
  .hero-home .hero-in { min-height: max(var(--hero-h-home-min), var(--hero-h-home)); }

  /* 미디어가 칸을 채운다. align-self 는 위 align-items:center 를 이
     칸에서만 뒤집는다. */
  .hero-in > .ph { aspect-ratio: auto; align-self: stretch; min-height: 0; }
}
/* ─── 문의 히어로 (2.6차 F-6 · §9-4) ────────────────────────────
   ★ 2.5차의 .hero-media-3x2 를 걷어냈다. 그때는 §5-3(높이 규격)과
     §8(비율 3:2)을 함께 지키려고 이미지 칸의 폭을 높이×1.5 로 역산했다.
     비율은 정확히 맞았는데, 세 번째 지적이 돌아왔다 — 문제는 비율이
     아니라 **사방에 여백을 두른 사각형이 떠 있다는 것** 자체였다.

     §9-4 가 그 답이다: 히어로 이미지는 최소 두 변이 가장자리에 닿아야
     한다. 닿고 나면 "어떤 비율로 잘렸나"는 질문 자체가 사라진다 —
     보이는 변이 없기 때문이다.

   구현은 새로 만들지 않았다. 홈·스튜디오 히어로가 쓰는 .safe-media 가
   이미 그것이다(미디어를 우측 62%로 몰고 좌측을 비운 뒤 마스크로 잇는다).
   문의 히어로도 같은 부품을 쓴다 — 세 히어로가 한 규칙 아래 들어왔다.
   ──────────────────────────────────────────────────────────── */

.hero h1 { font-size: var(--t-h1); line-height: var(--lh-tight); }
.hero h1 em { font-style: normal; color: var(--c-accent); }
.hero .lead { margin-top: var(--sp-3); max-width: var(--measure-tight); }
.hero .btn-row { margin-top: var(--sp-5); }
.hero-meta { display: flex; gap: var(--sp-5); margin-top: var(--sp-6); padding-top: var(--sp-3); border-top: var(--bw) solid var(--c-line); }
.hero-meta .hm-n { font-family: var(--font-mono); font-size: var(--t-h3); color: var(--c-text); }
.hero-meta .hm-l { font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); margin-top: var(--sp-0); }

/* ─── 자리표시자 ────────────────────────────────────────────
   회색 네모가 아니라 실제 비율·크기가 보이는 형태로.
   교체할 때 뭘 찾아야 하는지 화면에서 바로 읽히게 한다. */
.ph {
  position: relative;
  background: var(--ph-bg);
  border: var(--bw) dashed var(--ph-line);
  border-radius: var(--radius);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-1); text-align: center; padding: var(--sp-3);
  overflow: hidden;
}
/* 대각선 — 이미지 자리임을 한눈에 */
.ph::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to top right, transparent calc(50% - .5px), var(--ph-line) 50%, transparent calc(50% + .5px)),
    linear-gradient(to bottom right, transparent calc(50% - .5px), var(--ph-line) 50%, transparent calc(50% + .5px));
  opacity: .22;
}
.ph > * { position: relative; }
.ph .ph-code { font-family: var(--font-code);
  font-family: var(--font-mono); font-size: var(--t-caption);
  letter-spacing: var(--ls-wide); color: var(--c-accent);
  border: var(--bw) solid var(--ph-line); padding: 3px 9px;
}
.ph .ph-label { font-size: var(--t-caption); color: var(--ph-text); max-width: 22em; line-height: 1.5; }
.ph .ph-size { font-family: var(--font-code); font-size: var(--t-eyebrow); letter-spacing: .06em; color: var(--ph-text); opacity: .8; }
.ph .ph-req { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); }

.ph-16x9 { aspect-ratio: 16 / 9; }
.ph-3x2  { aspect-ratio: 3 / 2; }
.ph-4x3  { aspect-ratio: 4 / 3; }
.ph-1x1  { aspect-ratio: 1 / 1; }
.ph-3x4  { aspect-ratio: 3 / 4; }
.ph-21x9 { aspect-ratio: 21 / 9; }

/* ─── 자리표시자에 실물 이미지 채우기 ─────────────────────────
   .ph 안에 <img>를 넣고 .ph 에 .filled 를 더한다. 그러면 안내
   글자·점선·대각선이 사라지고 이미지만 남는다.

     <div class="ph ph-4x3 filled">
       <img src="img/img-01-1600.webp"
            srcset="img/img-01-400.webp 400w, …, img/img-01-1600.webp 1600w"
            sizes="(min-width:1920px) 640px, …, 84vw"
            width="1600" height="1200" alt="…">
       <span class="ph-code">[IMG-01]</span>   ← 그대로 둔다
       …
     </div>

   ★ 안내 span 들을 지우지 말 것. 명세가 마크업에 남아 있어야
     나중에 어느 슬롯인지, 어떤 이미지를 다시 받아야 하는지 안다.
     .filled 만 떼면 언제든 자리표시자로 되돌아간다.

   ★ src/srcset/sizes 를 손으로 쓰지 말 것. 원본을
     img/_src/img-<코드>.jpg 로 두고 `node tools/build-images.js` 를
     돌리면 변형과 img-<코드>.json 이 생긴다. sizes 문자열은 그 json 에
     들어 있고, 슬롯 실측(tools/qa/slots.html)에서 나온 값이다.
     레이아웃을 고치면 실측을 다시 돌려 프로필 표를 갱신한다.

   히어로처럼 첫 화면에 보이는 자리는 fetchpriority="high" 를 준다.
   접힘 아래 자리는 loading="lazy" 를 준다.

   비율·테두리·풀블리드 정렬은 위에서 이미 잡아둔 것을 그대로 쓴다. */
.ph > img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.ph.filled { border-style: solid; border-color: transparent; background: var(--c-bg-2, var(--ph-bg)); }
.ph.filled::before { display: none; }   /* 대각선 */
.ph.filled > span  { display: none; }   /* 코드·설명·사양·필수 */
/* ★ 자리표시자 장식을 새로 만들면 여기서도 끄는 것을 잊지 말 것.
     풀블리드 히어로의 ::after 테두리가 그래서 한동안 남아 있었다
     (더 구체적인 선택자라 아래 .hero-full 쪽에서 따로 꺼야 했다). */

/* ─── 카드 ─────────────────────────────────────────────────── */
.grid { display: grid; gap: var(--sp-3); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(2, 1fr); }  /* 1280+에서 4열 */

.card {
  background: var(--c-surface);
  border: var(--bw) solid var(--c-line);
  border-radius: var(--radius);
  padding: var(--sp-4);
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.card:hover { background: var(--c-surface-h); border-color: var(--c-line-2); }
.card .c-no { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); color: var(--c-accent); }
.card h3 { font-size: var(--t-h3); margin-top: var(--sp-2); }
.card p { color: var(--c-muted); margin-top: var(--sp-2); font-size: var(--t-body); }

/* 이미지가 위에 붙는 카드 */
.card-media { background: var(--c-surface); border: var(--bw) solid var(--c-line); border-radius: var(--radius); overflow: hidden; }
.card-media .cm-body { padding: var(--sp-3) var(--sp-3) var(--sp-4); }
.card-media h3 { font-size: var(--t-h3); }
.card-media p { color: var(--c-muted); margin-top: var(--sp-2); }
.card-media .cm-tag { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); margin-bottom: var(--sp-2); }

/* ─── 교차 배치 프리뷰 ─────────────────────────────────────── */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(32px, 5vw, 72px); align-items: center; }

/* ─── 두 컬럼의 하단까지 맞춘다 (§9-2 · E-3 전파) ────────────────
   .split 은 align-items:center 라 짧은 컬럼이 가운데에 뜬다. 문의
   페이지처럼 좌우 길이가 다른 자리에서는 상단도 하단도 안 맞았다
   (얼로이 실측 상단 105px · 하단 120px 어긋남).

   ★ 높이를 계산해 맞추지 않는다. 남는 높이를 먹을 요소를 하나 정한다 —
     .grow 가 그것이고, 그 안의 자리표시자·지도가 차이를 흡수한다.
     계산으로 맞추면 폼이 한 줄 늘 때마다 다시 어긋난다.
   ★ 한 열로 쌓이는 폭에서는 늘릴 상대가 없으므로 되돌린다. */
.split.split-stretch { align-items: stretch; }
.split.split-stretch > * { display: flex; flex-direction: column; }
.split.split-stretch .grow { flex: 1 1 auto; display: flex; flex-direction: column; }
/* 차이를 흡수하는 것은 이 칸이다 — 자리표시자든 지도든.
   ★ .map-embed 를 함께 잡는다. 3.2차 M-7 에서 자리표시자를 지도로
     바꿨더니 .ph 만 걸려 있던 이 규칙이 비껴갔고, .map-embed 의
     aspect-ratio 4:3 이 살아나 우측 컬럼을 428px 로 늘렸다. 좌측 폼은
     "보내기"에서 끝나는데 상자만 따라 늘어 아래가 311px 비었다.
     ★ 부품을 갈아끼울 때는 그 부품을 이름으로 지목하던 규칙도 함께 본다. */
.split.split-stretch .grow > .ph,
.split.split-stretch .grow > .map-embed {
  flex: 1 1 auto;
  aspect-ratio: auto;
  min-height: clamp(180px, 15vw, 320px);
}
@media (max-width: 900px) {
  .split.split-stretch > *,
  .split.split-stretch .grow { display: block; }
  /* ★ min-height 를 반드시 함께 되돌린다. 비율이 다시 켜진 상태에서
     하한 높이가 남으면 브라우저가 그 높이에서 **폭을 되계산한다** —
     342px 칸에 356px(200×16/9)이 들어가 390 화면에서 56px 이 넘쳤다.
     블리드 이미지의 aspect-ratio 와 정확히 같은 함정이고, 방향만 반대다. */
  .split.split-stretch .grow > .ph { aspect-ratio: 16 / 9; min-height: 0; }
  /* 지도는 한 열에서 제 비율(§8 4:3)로 돌아간다 — 프레임과 같다 */
  .split.split-stretch .grow > .map-embed { aspect-ratio: 4 / 3; min-height: 0; }
}
.split.reverse > :first-child { order: 2; }

/* ─── 이미지가 옆 칸 높이를 따라간다 (3.2차 M-2) ─────────────────
   2단에서 한쪽이 이미지, 한쪽이 글일 때 이미지에 비율(.ph-4x3)이 걸려
   있으면 높이가 칸 폭에서 정해진다. 옆 글이 몇 줄이냐와 무관하므로
   폭이 바뀔 때마다 하단선이 맞았다 어긋났다 한다 — 실측 1024 에서
   21px, 1280~1680 에서 0, 1920 에서 0. 맞는 폭이 있다고 맞는 것이 아니다.
   ★ 비율을 풀고 stretch 로 넘긴다. 높이를 옆 칸이 정하고 사진은
     cover 로 채우므로, 어느 폭에서도 두 하단선이 만난다.
   ★ 한 열로 접히면 맞출 상대가 없다. 비율을 돌려준다. */
.pair-stretch > .ph { align-self: stretch; aspect-ratio: auto; min-height: 0; }
@media (max-width: 900px) {
  /* 한 열로 접히면 맞출 상대가 없다. 비율을 돌려주고, 좌우는 화면 끝까지
     내보낸다 — 여기서도 §9-4 다. 이 칸의 사진은 썸네일이 아니라 섹션을
     대표하는 한 장이라, 거터 안에 갇히면 "떠 있는 사각형"이 된다.
     (3.2차 M-6 전수 점검에서 뒤늦게 걸린 자리다 — 처음 훑을 때 히어로와
      분할 섹션만 세고 2단 격자 안의 단독 사진을 빼먹었다.) */
  .pair-stretch > .ph {
    align-self: auto;
    aspect-ratio: 4 / 3;
    margin-inline: calc(-1 * var(--gutter));
  }
}

/* ═══════════════════════════════════════════════════════════════
   분할 섹션 — .split-media (v3 §5)

   텍스트가 한쪽에 몰린 섹션의 반대편을 이미지로 채운다. 스튜디오
   "일하는 방식"이 좌측 절반에 글자, 우측 절반이 통째로 빈 검정이었던
   것을 고치려고 만든 부품이다.

     <div class="split-media">
       <div class="sm-copy"> 아이브로우 · 제목 · 본문 </div>
       <div class="sm-img"><img …></div>
     </div>

   ★ 이미지 경계를 선으로 끊지 않고 텍스트 쪽으로 서서히 지운다.
     칸이 둘로 갈린 것처럼 보이면 "빈 우측"을 이미지로 메웠다는 인상이
     그대로 남는다. 마스크가 하는 일은 두 칸을 한 장면으로 만드는 것이다.

   ★ 비율 5:7 — 이미지가 넓다. 텍스트는 --measure 에서 어차피 멈추므로
     남는 폭을 이미지가 가져가는 편이 채움률에 이롭다.

   ★ 이미지 칸에 하한 높이를 준다. 글이 짧은 날 이미지가 함께 납작해지면
     띠 한 줄이 되어 안 넣느니만 못하다 (v2 히어로 높이에서 배운 것과
     같은 이야기다 — 콘텐츠가 아니라 규격이 높이를 정한다).
   ═══════════════════════════════════════════════════════════════ */

.split-media {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}
.split-media .sm-copy { max-width: var(--measure); }

.split-media .sm-img {
  position: relative;
  align-self: stretch;
  min-height: clamp(340px, 32vw, 520px);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to left, #000 55%, transparent 100%);
          mask-image: linear-gradient(to left, #000 55%, transparent 100%);
}
.split-media .sm-img > img { width: 100%; height: 100%; object-fit: cover; }

/* ─── 우측 블리드 (§9-4 확장) ──────────────────────────────────
   §9-4 는 히어로 이미지를 두고 쓴 규칙인데, 분할 섹션 이미지에도 같은
   말이 성립한다. 컨테이너 우측 라인에서 멈추면 이미지 오른쪽에 검은 띠가
   한 뼘 남고, 왼쪽은 마스크로 녹아 있으니 "오른쪽만 잘린 사각형"으로
   읽힌다. 오른쪽 변까지 화면 밖으로 내보내면 보이는 변이 위아래 둘뿐이고,
   그 둘은 섹션 여백과 만나 경계로 읽히지 않는다.

   ★ 부모 섹션에 .clip-x 를 함께 준다. 스크롤바 때문에 100vw 가 문서 폭보다
     넓어 몇 px 이 삐져나오는데, clip 은 그것을 잘라내면서 스크롤 컨테이너를
     만들지 않는다 (overflow:hidden 과 다른 점이고, sticky 헤더가 사는 이유).
   ★ 한 열로 쌓이는 폭에서는 걸지 않는다. 양쪽 거터 안에 얌전히 들어간
     이미지의 오른쪽만 내보내면 그게 더 어긋나 보인다. */
@media (min-width: 901px) {
  .split-media .sm-img.bleed-r {
    margin-right: calc(-1 * ((100vw - min(100vw, var(--w-wide))) / 2 + var(--gutter)));
  }
}
.clip-x { overflow-x: clip; }

/* ─── 블리드 유틸 — 골격 A 용 (E-3 전파) ───────────────────────
   위 규칙은 .split-media(골격 B) 안에서만 걸린다. 리넨·얼로이는 골격 A 라
   히어로와 분할 섹션이 .hero-in / .split 격자 위에 있고, 그 안의 이미지가
   사방에 여백을 두른 채 앉아 있었다 — §9-4 가 말하는 "떠 있는 사각형"이다.

   ★ 컨테이너 폭이 데모마다 다르므로(--w-base 리넨 1240 · 얼로이 1480)
     화면과 컨테이너의 차이를 그때그때 계산한다. 상수로 적으면 한 데모에서만 맞는다.
   ★ 한 열로 쌓이는 폭(≤900)에서는 **한쪽만** 내보내지 않는다. 대신
     양쪽을 함께 내보낸다 — 아래 블록이 그것이다. 3.2차 M-6·M-9·M-10.
     "한쪽만 내보내면 어긋나 보인다"까지는 맞았는데, 거기서 멈춰
     아무것도 안 하면 §9-4 가 금지한 '떠 있는 사각형'이 그대로 남는다.
     실측 두 데모 모바일 히어로·분할 이미지 **15개 전부** 좌우 24px
     여백을 두르고 앉아 있었다. */
@media (min-width: 901px) {
  .bleed-r { margin-right: calc(-1 * ((100vw - min(100vw, var(--w-base))) / 2 + var(--gutter))); }
  .bleed-l { margin-left:  calc(-1 * ((100vw - min(100vw, var(--w-base))) / 2 + var(--gutter))); }

  /* 한 변만 내보내면 여전히 사각형이다 — §9-4 는 **두 변 이상**을 요구한다.
     좌우 한 변을 화면 밖으로 보낸 뒤 위아래를 섹션 변까지 늘려, 보이는
     변이 안쪽 하나만 남게 한다. 그 한 변은 글자와 만나는 쪽이고
     마스크가 녹인다(아래).
     ★ aspect-ratio 를 반드시 푼다. .ph-4x3 을 남겨 두면 stretch 로 늘어난
       **높이에서 폭을 되계산해** 칸을 넘어간다 — 실측 667×4/3 = 889px 이
       584px 칸 위로 흘러 옆 글자를 덮었다. 비율은 칸 안에 있을 때 쓰는
       규격이고, 여기서는 칸이 아니라 섹션이 크기를 정한다.
       안쪽 <img> 는 cover 라 구도는 유지된다. */
  .hero-in > .bleed-r, .hero-in > .bleed-l,
  .split > .bleed-r,   .split > .bleed-l {
    align-self: stretch;
    aspect-ratio: auto;
    margin-block: calc(-1 * var(--sec-py, 0px));
  }
  /* 글자와 만나는 변만 녹인다 — 잘린 변이 아니라 이어지는 변으로 읽힌다 */
  .bleed-r {
    -webkit-mask-image: linear-gradient(to right, var(--hero-fade-stops));
            mask-image: linear-gradient(to right, var(--hero-fade-stops));
  }
  .bleed-l {
    -webkit-mask-image: linear-gradient(to left, var(--hero-fade-stops));
            mask-image: linear-gradient(to left, var(--hero-fade-stops));
  }
}

/* ─── 한 열로 쌓이는 폭 : 양쪽을 함께 내보낸다 (3.2차 M-6·M-9·M-10) ──
   세로로 쌓이면 이미지 좌우가 거터 안에 갇혀 사방에 여백을 두른
   사각형이 된다. 위아래는 이미 섹션 여백과 만나 경계로 읽히지 않으므로,
   좌우만 내보내면 남는 변이 없다.
   ★ 데스크톱처럼 화면-컨테이너 차이를 계산할 필요가 없다. 이 폭에서는
     wrap 이 이미 화면을 가득 채우고 있어 거터만 되돌리면 된다.
     계산식을 그대로 쓰면 min(100vw, --w-base) 가 100vw 로 접혀 같은
     값이 나오지만, 읽는 사람이 한 번 더 따져 봐야 한다.
   ★ 마스크는 뗀다. 좌우가 다 화면 밖으로 나가면 녹일 안쪽 변이 없고,
     남겨 두면 이미지 한쪽이 이유 없이 흐려진다.
   ★ 세로 stretch 도 뗀다. 위에서 --sec-py 만큼 되돌리는 규칙은 2단일
     때 옆 칸과 높이를 맞추려는 것이고, 한 열에서는 맞출 상대가 없다. */
@media (max-width: 900px) {
  .bleed-l, .bleed-r {
    margin-inline: calc(-1 * var(--gutter));
    -webkit-mask-image: none;
            mask-image: none;
  }
  .hero-in > .bleed-r, .hero-in > .bleed-l,
  .split > .bleed-r,   .split > .bleed-l {
    align-self: auto;
    margin-block: 0;
  }
}
.split-media .sm-img > .ph { width: 100%; height: 100%; border-radius: 0; }

/* 텍스트가 우측이면 방향을 뒤집는다 — 마스크도 함께 */
.split-media.reverse { grid-template-columns: 7fr 5fr; }
.split-media.reverse .sm-copy { order: 2; }
.split-media.reverse .sm-img {
  -webkit-mask-image: linear-gradient(to right, #000 55%, transparent 100%);
          mask-image: linear-gradient(to right, #000 55%, transparent 100%);
}

/* 한 열로 쌓이면 마스크 방향이 아래쪽이어야 한다. 좌우로 지우면
   화면 폭 전체를 쓴 이미지의 한쪽 끝만 사라져 어색하다. */
@media (max-width: 900px) {
  .split-media, .split-media.reverse { grid-template-columns: 1fr; }
  .split-media.reverse .sm-copy { order: 0; }
  .split-media .sm-img,
  .split-media.reverse .sm-img {
    min-height: clamp(240px, 52vw, 360px);
    -webkit-mask-image: linear-gradient(to top, #000 60%, transparent 100%);
            mask-image: linear-gradient(to top, #000 60%, transparent 100%);
    /* ★ 한 열로 쌓이면 좌우 양쪽으로 블리드한다 (2.9차 I-3 · §9-4).
       데스크톱에서는 우측 한 변만 내보내면 됐지만(좌측은 마스크로 녹는다),
       세로로 쌓이면 좌우가 거터 안에 갇혀 "사방 여백의 떠 있는 사각형"이
       된다 — 2.6차에 데스크톱에서 고친 바로 그 모양이다.
       위쪽 변은 마스크가 지우므로 남는 변은 아래 하나뿐이고, 그 변은
       섹션 여백과 만나 경계로 읽히지 않는다. */
    margin-inline: calc(-1 * var(--gutter));
  }
}


/* ─── 스펙 표 ──────────────────────────────────────────────── */
.spec { width: 100%; border-collapse: collapse; }
.spec th, .spec td { text-align: left; padding: var(--sp-2) var(--sp-2) var(--sp-2) 0; border-bottom: var(--bw) solid var(--c-line); vertical-align: top; }
.spec th { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); font-weight: 400; white-space: nowrap; width: 34%; }
.spec td { color: var(--c-muted); }

/* ─── 좁은 폭 : 표를 세로로 쌓는다 (3.2차 M-5) ──────────────────
   .spec th 는 nowrap 이고 폭이 34% 다. 390 화면에서 라벨이 148px 을
   가져가고 남은 칸에 "Butylene Glycol — 보습·용해" 가 들어가니 값이
   두세 줄로 접혔다. 행마다 접히는 줄 수가 달라 라벨과 값의 첫 줄이
   서로 어긋나 보였다.
   ★ 라벨 폭을 고정하는 길도 있었지만, 그러면 값 칸이 더 좁아져 줄 수만
     늘어난다. 좁은 화면에서 두 칸을 나란히 두는 것 자체가 무리다.
   ★ 560 에서 끊는다. .grid-2 가 한 열로 접히는 지점과 같다 — M-1 의
     2열 성분표가 한 열이 되는 순간과 표 형태가 바뀌는 순간을 맞춘다. */
@media (max-width: 560px) {
  .spec, .spec tbody, .spec tr, .spec th, .spec td { display: block; width: auto; }
  .spec tr { border-bottom: var(--bw) solid var(--c-line); padding-block: var(--sp-2); }
  .spec th, .spec td { border-bottom: 0; padding: 0; white-space: normal; }
  .spec td { margin-top: var(--sp-1); }
}

/* ─── CTA 밴드 ─────────────────────────────────────────────── */
.cta { background: var(--c-bg-alt); border-top: var(--bw) solid var(--c-line); }
/* ★ CTA 는 --section-py-sm 보다 더 조인다. 1920 에서 sm 은 96px 이라
   위아래 192px + 콘텐츠 109px = 302px 인데, 실측 채움률이 36% 로
   아홉 페이지 전부 같았다 (v2 §2 R5 가 목표를 180~200px 로 잡은 근거).
   닫는 띠는 여백이 아니라 밀도로 읽혀야 한다. */
.cta-in { display: grid; grid-template-columns: 1.3fr .7fr; gap: var(--sp-5); align-items: center; padding-block: var(--sp-5); }
.cta h2 { font-size: var(--t-h2); }
.cta p { color: var(--c-muted); margin-top: var(--sp-2); }

/* ═══════════════════════════════════════════════════════════════
   타임라인 — .tl (v3 §6 · 2.6차 F-5 로 배치 변경)

   듬성한 표를 폐지하고 박스형 리스트로 바꾼 것이 §6 이다. 2.5차는 그
   박스를 세로로 쌓고 폭을 --w-read(800px)로 묶었는데, 1744px 컨테이너에서
   그러면 우측 940px 이 통째로 빈다 — 표를 고치면서 §9-2(반쪽 비움 금지)를
   새로 만든 셈이 됐다.

   ★ 그래서 가로 1행이다. 박스 다섯 개가 컨테이너 전폭을 균등 분할한다.
     세로 쌓기는 열이 하나가 되는 좁은 폭에서만 나온다.
   ★ 박스 안은 가운데 정렬 2줄 — 연도(위) / 내용(아래). 좌우 2단은
     칸 폭이 300px 안팎으로 좁아져 성립하지 않는다.

   박스 규격(배경·보더·호버)은 §6 그대로다.
   ═══════════════════════════════════════════════════════════════ */

.tl {
  display: grid;
  grid-template-columns: repeat(var(--tl-cols, 5), 1fr);
  gap: var(--sp-2);
}

/* ─── 박스 밀도 — 한 값으로 통일 (2.7차 G-3) ────────────────────
   한 페이지에 박스 부품이 둘인데 안쪽 여백과 줄 간격이 다르면, 그 차이가
   의미 없는 위계로 읽힌다. 스튜디오의 통계 박스가 타임라인 박스보다
   26px 높았고(179 대 153) 내용은 오히려 적어 "속이 비어" 보였다.

   ★ 바탕색도 하나로 뒀다. 타임라인은 bg-0 섹션 위, 통계는 bg-1 섹션 위에
     앉는데 --c-surface 는 둘 다보다 밝아 어느 쪽에서도 박스로 읽힌다.
     배경마다 다른 색을 고르면 그 자체가 또 하나의 규칙이 된다. */
.box {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-4) var(--sp-3);
  background: var(--c-surface);
  border: var(--bw) solid var(--c-line);
  transition: border-color var(--dur-1) var(--ease-out),
              background var(--dur-1) var(--ease-out);
}
.box:hover {
  border-color: var(--accent-dim, var(--c-accent));
  background: var(--c-surface-h);
}

/* 타임라인 박스 — .box 위에 가운데 정렬만 더한다.
   마크업은 class="box tl-item" 이다. */
.tl-item {
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* 통계 박스 — 라벨 / 수치 / 보조설명 3줄 (§9 · G-3)
   ★ .card 를 쓰던 자리다. .card 는 줄 사이를 --sp-2 로 벌려 세 줄이
     32px 을 더 먹었다. 여기서는 .box 의 --sp-1 리듬을 그대로 쓴다. */
.stat-l { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-accent); }
.stat-n { font-size: var(--t-h3); font-weight: var(--fw-h3); letter-spacing: var(--ls-tight); color: var(--c-text); }
.stat-c { font-size: var(--t-caption); line-height: var(--lh-snug); color: var(--c-muted); }

.tl-y {
  font-family: var(--font-head);
  font-size: var(--t-h3);
  font-weight: var(--fw-h3);
  letter-spacing: var(--ls-tight);
  color: var(--c-accent);
}
.tl-b {
  font-size: var(--t-body);
  line-height: var(--lh-snug);   /* 칸이 좁아 1.7 은 세 줄을 네 줄로 만든다 */
  color: var(--c-muted);
  max-width: 18em;
}

/* 열 수만 줄인다. 어떤 폭에서도 행은 컨테이너 전폭을 쓴다 (F-5).
   ★ 5 → 3 → 2 → 1. 4를 건너뛰는 것은 항목이 5개라서다 — 4열이면
     둘째 줄에 한 칸만 남아 그 줄의 4분의 3이 빈다. 3열이면 3+2 로
     둘째 줄이 절반 넘게 찬다. */
@media (max-width: 1279px) { .tl { --tl-cols: 3; } }
@media (max-width: 767px)  { .tl { --tl-cols: 2; } }
@media (max-width: 479px)  { .tl { --tl-cols: 1; } }

/* ═══════════════════════════════════════════════════════════════
   문의 섹션 — .inquiry (v3 §8 · 지시서 D-3)

   ★ 머리말(아이브로우·제목·인트로)을 격자 밖으로 뺐다. 예전에는 좌측
     컬럼 안에 있어서, 우측 정보 박스가 "폼 시작점"이 아니라 "제목 꼭대기"에
     맞춰 떴다 — 그 어긋남이 정보 박스를 어중간한 높이에 떠 있게 만든 원인이다.
     머리말이 폭 전체를 쓰고 그 아래에서 두 컬럼이 함께 시작한다.

     <div class="wrap wrap-wide">
       아이브로우 · 제목 · 인트로
       <div class="inquiry">
         <form class="inq-form"> … </form>
         <aside class="inq-side"> 정보 → 지도 → 유료 안내 </aside>
       </div>
     </div>

   ★ 7:5 · 상단 정렬. 폼이 주인공이고 우측은 보조다.
   ═══════════════════════════════════════════════════════════════ */

.inquiry {
  display: grid;
  grid-template-columns: 7fr 5fr;
  gap: clamp(32px, 4vw, 64px);
  align-items: stretch;   /* 두 컬럼의 상단과 하단이 모두 만난다 (§9-2) */
}

/* 우측 컬럼 — 정보 박스는 제 높이만, 지도가 남는 높이를 흡수한다.
   ★ 이것이 "지도 하단 라인 = 보내기 버튼 하단 라인"의 구현이다.
     지도에 높이를 지정해 맞추려 하면 폼이 한 줄 늘 때마다 어긋난다.
     남는 높이를 먹는 요소를 하나 정해 두면 계산이 필요 없다. */
.inq-side {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.inq-side > .map-embed {
  flex: 1 1 auto;
  aspect-ratio: auto;          /* 높이는 좌측 컬럼이 정한다 */
  min-height: clamp(220px, 22vw, 340px);
}

@media (max-width: 900px) {
  /* 한 열로 쌓이면 맞출 상대가 없다. 지도는 제 비율(§8 4:3)로 돌아간다. */
  .inquiry { grid-template-columns: 1fr; }
  .inq-side > .map-embed { flex: none; aspect-ratio: 4 / 3; }
}

/* 스튜디오 정보 — 내부 2단으로 세로를 압축한다 (F-7).
   한 단으로 쌓으면 박스만 300px 가까이 되어 지도가 먹을 높이를 뺏는다. */
.info-2col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2) var(--sp-4); }
@media (max-width: 1279px) { .info-2col { grid-template-columns: 1fr; } }
@media (max-width: 900px)  { .info-2col { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px)  { .info-2col { grid-template-columns: 1fr; } }

/* ─── 정보 목록 — 행간 압축 (§8 ①) ────────────────────────────
   공용 .spec 표를 쓰면 th 가 34%를 먹고 행 높이가 --sp-2 두 겹으로
   벌어진다. 정보 박스는 읽는 것이 아니라 확인하는 것이라 조밀해야 한다.
   §1 의 h4(라벨) + body(값) 조합을 그대로 쓴다. */
.info-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-1) var(--sp-3);
  margin: 0;
  align-content: start;
}
/* 2단 안에 들어가면 칸이 좁다. 라벨을 위, 값을 아래로 쌓는다. */
.info-2col .info-list { grid-template-columns: 1fr; gap: var(--sp-0) 0; }
.info-2col .info-list dd + dt { margin-top: var(--sp-2); }
.info-list dt {
  font-family: var(--font-mono);
  font-size: var(--t-h4); font-weight: var(--fw-h4);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  color: var(--c-faint);
  white-space: nowrap;
}
.info-list dd {
  margin: 0;
  font-size: var(--t-body);
  line-height: var(--lh-snug);   /* 본문 1.7 은 확인용 정보에 과하다 */
  color: var(--c-muted);
}

/* ─── 지도 (§8 ②) ─────────────────────────────────────────────
   비율 4:3, 테두리 1px. 안쪽 iframe 은 _shared/map-embed.js 가 넣는다 —
   실주소가 정해지면 카카오맵 JS SDK 로 갈아끼울 자리가 그 파일 하나다. */
.map-embed {
  position: relative;
  aspect-ratio: 4 / 3;
  border: var(--bw) solid var(--c-line-2);
  background: var(--c-surface);
  overflow: hidden;
}
.map-embed > iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.map-embed > .map-note {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-3); text-align: center;
  font-size: var(--t-caption); color: var(--c-faint);
}

/* ─── 폼 ───────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: var(--sp-1); }
.field label { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); }
.field input, .field textarea, .field select {
  font: inherit; font-size: var(--t-body);
  background: var(--c-bg); color: var(--c-text);
  border: var(--bw) solid var(--c-line-2); border-radius: var(--radius);
  padding: 13px var(--sp-2);
}
.field input:focus, .field textarea:focus, .field select:focus { outline: none; border-color: var(--c-accent); }
.field textarea { min-height: 140px; resize: vertical; }
.field.full { grid-column: 1 / -1; }

/* 제출 줄 — 버튼이 인라인 요소라 그 아래로 줄상자의 디센더 공간(9px)이
   남는다. 폼 바닥이 버튼 바닥보다 9px 아래에 생기고, 그만큼 우측 지도가
   버튼선 아래로 내려간다 (F-7-4 는 두 선이 만나기를 요구한다).
   그리드 아이템으로 만들면 줄상자가 생기지 않는다. */
/* ★ :not(.field) — 제출 줄만이다. .field.full(분야·내용)까지 걸면
     justify-items:start 가 입력칸을 내용 폭으로 줄여 버린다. */
.inq-form > .full:not(.field) { display: grid; justify-items: start; }

/* ─── 푸터 ─────────────────────────────────────────────────── */
.ft { background: var(--c-bg-alt); border-top: var(--bw) solid var(--c-line); padding-block: var(--sp-6) var(--sp-4); }
.ft-top { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: var(--sp-5); }
.ft h4 { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); font-weight: 400; margin-bottom: var(--sp-2); }
.ft li + li { margin-top: var(--sp-1); }
.ft a { color: var(--c-muted); }
.ft a:hover { color: var(--c-text); }
.ft-bot { margin-top: var(--sp-6); padding-top: var(--sp-3); border-top: var(--bw) solid var(--c-line); display: flex; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap; font-size: var(--t-caption); color: var(--c-faint); }
/* 푸터 주소·연락처. 인라인 style="font-size:…" 로 흩어져 있던 것을
   한 자리로 모았다 — §1 은 마크업에 크기를 적는 것을 금지한다. */
.ft-addr { font-size: var(--t-caption); line-height: var(--lh-body); }

/* ─── 데모 배너 — 실제 회사로 오인하지 않도록 ───────────────── */
.demo-bar {
  background: var(--c-accent-2); color: var(--c-text);
  font-family: var(--font-mono); font-size: var(--t-eyebrow);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  text-align: center; padding: 9px var(--sp-2);
}
.demo-bar a { text-decoration: underline; text-underline-offset: 3px; }

/* ─── 반응형 ───────────────────────────────────────────────── */
@media (max-width: 900px) {
  .hero-in, .split, .cta-in { grid-template-columns: 1fr; }
  .split.reverse > :first-child { order: 0; }
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .ft-top { grid-template-columns: 1fr 1fr; }
  .nav, .hd-cta { display: none; }
  .burger { display: block; }
}
@media (max-width: 560px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .ft-top { grid-template-columns: 1fr; }
  .hero-meta { flex-wrap: wrap; gap: var(--sp-3); }

  /* 모바일에서도 2열을 지키는 격자 (3.0차 J-4).
     칸 안이 한 줄짜리 수치라 한 열로 쌓으면 세로만 길어진다 —
     스튜디오 통계 네 칸이 그랬다. 문장이 든 카드에는 쓰지 않는다.
     ★ .grid-4 뒤에 온다. 같은 특이도라 순서로 이긴다. */
  .grid-keep-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 모바일 메뉴 — burger로 여닫는다 */
.m-menu { display: none; border-bottom: var(--bw) solid var(--c-line); background: var(--c-bg-alt); }
.m-menu.open { display: block; }
.m-menu a { display: block; padding: var(--sp-2) 0; border-bottom: var(--bw) solid var(--c-line); color: var(--c-muted); }
.m-menu a:last-child { border-bottom: none; }

/* ═══════════════════════════════════════════════════════════════
   모션 (v3 §7) — 페이지 진입 · 리빌 · 패럴랙스 · 안전지대 · 인트로
   값은 전부 base.css 토큰. 여기에 리터럴을 넣지 않는다.
   ═══════════════════════════════════════════════════════════════ */

/* ─── 페이지 진입 (§7-1) ────────────────────────────────────────
   짧은 페이드 + 상승. 페이지를 옮길 때마다 같은 길이·같은 이징이라
   "넘어갔다"는 감각이 생긴다.

   ★ body 에 건다. 헤더(.hd)와 데모 배너까지 함께 올라와야 페이지가
     한 장으로 넘어간 것처럼 읽힌다. 본문만 올리면 헤더가 제자리에
     붙박여 있어 두 겹으로 보인다.
   ★ transform 이 걸린 동안 position:fixed 자손은 body 를 기준으로
     잡힌다. 프레임에는 fixed 요소가 .intro 뿐이고 이 데모는 쓰지
     않는다. 새로 붙일 때 이 줄을 기억할 것.
   ★ fill-mode 는 backwards 다. forwards 를 함께 걸면(both) 끝난 뒤에도
     마지막 키프레임이 얹혀 있어, 백그라운드 탭처럼 애니메이션이 멈춘
     상태에서 opacity:0 이 그대로 굳는다. backwards 는 "시작 전 상태만"
     미리 적용하므로 끝나면 요소의 본래 값(불투명·변형 없음)으로
     돌아간다 — 남길 이유가 없는 것은 남기지 않는다. */
@keyframes page-in {
  from { opacity: 0; transform: translateY(var(--reveal-y)); }
  to   { opacity: 1; transform: none; }
}
body { animation: page-in var(--dur-3) var(--ease-out) backwards; }

/* ─── 스크롤 리빌 (§7-2) ────────────────────────────────────────
   JS 가 뷰포트에 들어온 요소에 .in 을 붙인다(1회성).
   되돌아 스크롤할 때 다시 사라지면 산만하다. */
.reveal {
  opacity: 0;
  transform: translateY(var(--reveal-y));
  transition: opacity var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-out);
}
.reveal.in { opacity: 1; transform: none; }

/* 형제 요소 순차 등장 — 6칸까지만. 그 이상은 마지막 칸이 너무 늦게 뜬다. */
.reveal-stagger > *:nth-child(1) { transition-delay: 0ms; }
.reveal-stagger > *:nth-child(2) { transition-delay: calc(var(--stagger) * 1); }
.reveal-stagger > *:nth-child(3) { transition-delay: calc(var(--stagger) * 2); }
.reveal-stagger > *:nth-child(4) { transition-delay: calc(var(--stagger) * 3); }
.reveal-stagger > *:nth-child(5) { transition-delay: calc(var(--stagger) * 4); }
.reveal-stagger > *:nth-child(6) { transition-delay: calc(var(--stagger) * 5); }
.reveal-stagger > *:nth-child(n+7) { transition-delay: calc(var(--stagger) * 5); }

/* ─── 패럴랙스 ─────────────────────────────────────────────────
   ★ background-attachment: fixed 를 쓰지 않는다. iOS 사파리가
     지원하지 않아 그 자리만 정지 배경이 된다.
   JS 가 transform 을 직접 쓴다. 여기서는 그릇만 만든다. */
.plx-wrap { position: relative; overflow: hidden; }
.plx {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  will-change: transform;
  transform: scale(var(--plx-scale));
}

/* ─── 텍스트 안전지대 ───────────────────────────────────────────
   미디어를 한쪽으로 몰고 반대쪽을 비운다. .safe-left 는 텍스트가
   왼쪽에 앉는 경우(미디어 오른쪽). 세로형은 .safe-bottom. */
.safe-media { position: relative; overflow: hidden; }
.safe-media > img,
.safe-media > video {
  position: absolute; top: 0; right: 0;
  width: var(--hero-media-w); height: 100%;
  object-fit: cover;
  -webkit-mask-image: linear-gradient(to right, var(--hero-fade-stops));
          mask-image: linear-gradient(to right, var(--hero-fade-stops));
}
.safe-media::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, var(--c-bg-alt) 20%, transparent var(--hero-ov-end));
  opacity: var(--hero-ov-alpha);
}
.safe-media > .safe-in {
  position: relative; z-index: 2;
  max-width: var(--hero-safe-w);
}

/* ─── 로딩 인트로 ───────────────────────────────────────────────
   ★ 콘텐츠는 이미 DOM 에 있고 오버레이만 걷힌다. 인트로가 렌더를
     막으면 SEO 와 폴백이 함께 죽는다.
   ★ JS 가 실패해도 애니메이션이 스스로 끝나 화면이 열린다. */
.intro {
  position: fixed; inset: 0; z-index: 999;
  background: var(--c-bg-alt);
  display: flex; align-items: center; justify-content: center;
  animation: intro-out var(--intro-dur) var(--mo-ease) forwards;
}
@keyframes intro-out {
  0%, 55% { opacity: 1; visibility: visible; }
  100%    { opacity: 0; visibility: hidden; }
}

/* ─── 동작 줄이기 ───────────────────────────────────────────────
   CSS 만 끄면 스크롤 핸들러는 계속 돈다. JS 쪽도 함께 막아야 한다
   (demo.js 가 matchMedia 로 확인한다). */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; scroll-behavior: auto !important;
      animation-duration: .01ms !important; animation-iteration-count: 1 !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
  .plx    { transform: none !important; }
  .intro  { display: none !important; }
  /* 페이지 진입도 끈다. 위의 animation-duration:.01ms 로 사실상 즉시
     끝나지만, 명시해 두어야 다음 사람이 이 규칙을 지우지 않는다. */
  body    { animation: none !important; }
}

/* ═══════════════════════════════════════════════════════════════
   레이아웃 변형 — 데모마다 골라 쓴다
   토큰은 "값"을 정하고, 이 클래스들은 "구성"을 정한다.
   포트폴리오가 주인공인 데모(프레임)는 아래를 쓰고,
   정보가 주인공인 데모(얼로이·리넨)는 위의 기본 구성을 쓴다.
   ═══════════════════════════════════════════════════════════════ */

/* 풀블리드 히어로 — 이미지가 화면을 꽉 채우고 그 위에 글자가 얹힌다 */
/* ★ 좁은 화면에서는 글자를 **위에** 세운다. flex-end 였던 탓에 글자 기둥이
   화면 중간 아래에서 시작하고, 주인공인 사진이 글자에 밀려 아래로 잘렸다.
   넓은 화면은 아래 @media 에서 center 로 되돌린다. */
.hero-full { position: relative; min-height: clamp(460px, 78svh, 760px); display: flex; align-items: flex-start; border-bottom: var(--bw) solid var(--c-line); overflow: hidden; }
.hero-full .hf-media { position: absolute; inset: 0; }
/* ── 스크림 (2026-09-07 Teo) — 사진과 글자 사이 한 겹 ────────────────
   글자가 앉는 쪽(왼쪽)만 덮고 반대편으로 빠진다. 사진 전체를 균일하게
   덮지 않는다 — 그러면 사진이 죽는다. 색은 종이색(--c-bg)이라 어두운
   사진 위에서 배경 밝기를 올려 **어두운 글자**의 대비를 벌어 준다.
   세기는 --scrim-strength 하나. 기본 0 이라 이 토큰을 주지 않는 데모
   (얼로이·프레임)에는 아무 것도 안 깔린다.
   ★ .hf-copy::before(⑯ 번짐)와는 다른 층이다. 번짐은 글자 상자 뒤만
     덮고, 이쪽은 사진 왼쪽 절반을 넓게 받친다 — 번짐의 흐린 가장자리에
     걸친 눈썹·표 끝줄이 그동안 대비를 못 얻던 자리다. */
.hero-full .hf-media::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to right,
    color-mix(in srgb, var(--c-bg) calc(var(--scrim-strength, 0) * 100%), transparent) 0%,
    color-mix(in srgb, var(--c-bg) calc(var(--scrim-strength, 0) * 100%), transparent) 30%,
    transparent 75%);
}
/* 풀블리드 자리표시자 — 테두리를 안쪽으로 넣어 이미지 자리임을 남기고,
   내용은 오른쪽으로 몰아 헤드라인과 겹치지 않게 한다. */
.hero-full .hf-media .ph { width: 100%; height: 100%; border-radius: 0; border: none; }
.hero-full .hf-media .ph::after {
  content: ''; position: absolute; inset: var(--sp-3);
  border: var(--bw) dashed var(--ph-line); pointer-events: none;
}
/* 실물이 들어오면 이 테두리도 사라져야 한다. 위 선택자가 .ph.filled::after
   보다 구체적이라 공통 .filled 규칙만으로는 안 꺼진다 — 여기서 끈다. */
.hero-full .hf-media .ph.filled::after { display: none; }
/* 자리표시자 내용은 카피와 같은 컨테이너 선에 맞춘다.
   .ph는 풀블리드(화면 전체)라, 단순히 오른쪽으로 밀면 초광폭에서
   라벨만 화면 끝까지 날아가 헤드라인과 따로 논다. */
/* ★ span 만 민다. `> *` 로 두면 <img> 까지 밀려 사진이 왼쪽으로 새고
     오른쪽에 배경색 띠가 남는다 — 실측 x=-68px, 우측 68px 띠.
     이 규칙은 라벨용인데 이미지가 들어온 적이 없어 드러나지 않았다. */
.hero-full .hf-media .ph > span {
  margin-left: auto;
  margin-right: max(var(--gutter), calc((100% - var(--w-wide)) / 2 + var(--gutter)));
}
.hero-full .hf-media .ph .ph-label { text-align: right; }
/* 크롭 초점. 21:9 컷을 세로가 긴 히어로에 덮으면 **가로로** 잘린다 —
   그래서 세로 위치(Y)는 대부분의 폭에서 아무 일도 하지 않는다. 6개 Y 값이
   전부 같은 수를 내서 알았다. 움직이는 것은 X 다.
   ★ 폭별로 나누지 않았다. 30% 한 값이 390~1920 다섯 폭 전부에서 통과한다
     (최저 5.48~7.57:1). 분기는 통과하는 값이 폭마다 다를 때만 만든다. */
/* ★ 기본값을 여기서 .hero-full 에 박지 않는다. 요소에 직접 적으면 데모의
   tokens.css(:root)가 준 값을 이겨서, "화면이 데모별로 준다"는 아래 주석이
   거짓이 된다 — 실제로 리넨이 62% 를 줬는데 30% 가 나왔다.
   기본값은 base.css :root 에 두고 여기서는 쓰기만 한다. */
/* ★ <picture> 가 끼면 .ph > img (position:absolute; inset:0) 가 안 걸린다
   — 선택자가 직계 자식이라 .ph > picture > img 가 되면서 끊긴다. 그러면
   img 가 static 으로 떨어지고 height:100% 가 <picture>(높이 auto)를 기준으로
   풀려, 사진이 .ph 의 padding(24px) 안쪽에 제 크기로 앉는다 —
   실측 1024 에서 976×414(박스는 1024×701). 2026-09-07 세로본 도입 때 생겼다.
   ★ 리넨 히어로(.hero-full)로 좁혀 둔다. 프레임의 <picture>(.hero-b-poster)는
     .ph 밖이라 애초에 안 걸리지만, 범위를 좁혀 두는 편이 안전하다. */
.hero-full .hf-media .ph > picture { position: absolute; inset: 0; display: block; }
.hero-full .hf-media .ph > picture > img { position: absolute; inset: 0; }

.hero-full .hf-media img { width: 100%; height: 100%; object-fit: cover;
  /* ★ 항상 정중앙이다 (2026-09-09 Teo 확정 · 재론 금지).
       어느 부분을 보여줄지는 **원본 크롭**이 정한다 — CSS 는 관여하지 않는다.
       전에는 슬롯마다 --hero-focus / --hero-focus-m 을 다르게 박아 두었는데,
       Teo 가 피사체를 정중앙에 맞춰 크롭한 사진을 코드가 다시 옆으로 당겨
       왼쪽으로 치우쳐 보였다. 값을 상수로 두어 그 당김을 없앤다.
       폭에 따라 갈라지던 ≤900 분기도 같이 지웠다 — 분기가 있으면 크롭과
       코드가 또 어긋난다. */
  object-position: 50% 50%; }
/* .ph 가 칸을 그대로 채워야 사진이 히어로를 덮는다 — 비율은 여기서 푼다 */
.hero-full .hf-media .ph { aspect-ratio: auto; min-height: 0; }
/* ── 히어로의 유일한 층: 글자 블록 뒤 흰 번짐 (2026-09-06 Teo ⑯) ────
   사진은 원본 그대로(덮개 없음). 글자 블록(.hf-copy)을 내용 폭으로 줄이고
   그 뒤(::before)에 배경색 면을 하나 깐다 — 글자 박스보다 위·아래·오른쪽
   --veil-pad 만큼 크고, 왼쪽은 화면 밖까지(히어로 overflow:hidden 이 자른다).
   가장자리는 filter: blur(--veil-blur) — 이 필터는 **면에** 거는 것이지
   사진에 거는 것이 아니다(§13 금지는 사진 위 블러). 중심 --veil-alpha(90%),
   바깥으로 0. 값은 base.css(≤900 은 40/32px).
   ★ 왼쪽 선: .wrap 은 margin auto 로 가운데 서는데 폭을 fit-content 로
     줄이면 가운데로 모인다. margin-left 를 --hf-inset(= .wrap 의 왼쪽
     여백)으로 박아 같은 선에 세운다. 오른쪽은 0.
   ★ ::before 는 .hf-copy 의 stacking context(z-index 2) 안에서 z-index -1
     — 글자 아래, 사진 위. 층은 하나다. */
.hero-full .hf-copy {
  position: relative; z-index: 2;
  width: fit-content; max-width: 100%;
  margin-left: var(--hf-inset); margin-right: 0;
  --hf-py: var(--sp-6);
  padding-block: var(--hf-py);
}
.hero-full.hero-veil .hf-copy::before {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  top:    calc(var(--hf-py) - var(--veil-pad));
  bottom: calc(var(--hf-py) - var(--veil-pad));
  right:  calc(var(--gutter) - var(--veil-pad));
  left:   -100vw;
  background: var(--c-bg);
  opacity: var(--veil-alpha);
  border-radius: var(--veil-radius, 0);   /* 데모가 준다(리넨 24px). 기본 0 */
  filter: blur(var(--veil-blur));
}

/* ─── 넓은 화면 : 글자를 왼쪽에 세우고 스크림도 가로로 (2026-08-28) ──
   세로 스크림은 글자가 아래에 깔릴 때의 구성이다. 넓어지면 글자가
   아래 한 줄로 눕는 대신 왼쪽에 서는 편이 읽기 좋고, 그때는 스크림도
   글자 쪽(왼쪽)에서 시작해 오른쪽으로 사라져야 한다.
   ★ 글자 폭을 스크림 폭 안으로 묶는다. 안 묶으면 문장이 스크림 밖
     맨 사진 위로 나가 그 줄만 대비가 무너진다. */
@media (min-width: 901px) {
  .hero-full { align-items: center; }
}
/* ─── 사진 위의 글자 (2026-08-28) ────────────────────────────────
   ★ 스크림을 올려서 풀지 않는다. 스크림은 가독을 위한 것이지 배경을
     다시 칠하는 것이 아니고(「미디어 가장자리 페이드」 — 대비는 페이드로 벌지 않는다), 진하게 만들면 풀블리드로 바꾼 이유
     자체가 없어진다. 대신 **글자 쪽**을 고친다.

   사진 위에서는 보조색이 먼저 무너진다. 배경이 한 색일 때는 --c-muted ·
   --c-faint 가 위계를 만들지만, 사진 위에서는 그 위계가 곧 대비 부족이
   된다. 실측 리넨 풀블리드에서 .hm-l 1.00:1 · em 1.00:1 이 나왔다.

   그래서 이 히어로 안에서만 위계를 **색이 아니라 굵기와 자간**으로
   옮긴다. 색은 본문색 한 겹으로 모으고, 작은 글자는 굵기를 올린다. */
/* ★ 2026-08-30 — 위계를 되살렸다. 위 판단은 스크림이 opacity .62 로
     눌린 층이었을 때의 것이다. 참조(farokorea)의 밴드는 글자 쪽에서
     알파 .96 이라 사진이 거의 비치지 않고, 참조 화면도 아이브로우를
     골드로, 보조 문장을 흐린 갈색으로 그대로 둔다.
     색을 한 겹으로 모으던 세 줄을 뺀다 — 재서 확인한 뒤에 뺐다.
     ★ 굵기 조정은 남긴다. 그건 대비를 벌려는 것이 아니라 작은 글자가
       사진 결 위에서 흐려 보이던 것을 잡는 손질이다. */
.hero-full .hf-copy .eyebrow,
.hero-full .hf-copy .hm-l { font-weight: 600; }
.hero-full .hf-copy .lead,
.hero-full .hf-copy .muted { font-weight: 500; }

/* ★ 참조와 갈라지는 유일한 자리 — 재서 갈라졌다.
   위계를 참조처럼 전부 되살리면 작은 글자 둘이 AA 에 못 미친다.
   1268 실측(밴드 위, 배경 rgb(241,236,229)):
     아이브로우  --c-accent #9a7b4f  3.36 : 1   (15px, 4.5 필요)
     지표 라벨   --c-faint  #8a8177  3.23 : 1   (15px, 4.5 필요)
   이건 밴드 탓이 아니다. 밴드가 종이색과 거의 같아진 자리라, **평범한
   페이지 배경 위에서도 같은 값**이 나온다 — 리넨 팔레트 자체의 문제다.
   참조(farokorea)는 팔레트가 통과해서 위계를 그대로 둘 수 있었다.

   그래서 이 둘만 한 단 내린다. 본문색으로 납작하게 접지 않고 --c-muted
   (#665d53 · 5.49:1) 로 옮긴다 — 위계는 남고 AA 는 지킨다.
   ★ 2026-09-04 — 색은 토큰(--hero-ink-*)으로 뺐다. 기본값은 위와 같고
     (muted · accent · text), 브러쉬 하나만 남긴 뒤 아직 모자란 자리는
     데모가 **자기 히어로에서만** 한 단 더 진하게 덮는다. 층은 안 얹는다.
     리넨 실측값은 linen/tokens.css 에 있다. */
.hero-full .hf-copy .eyebrow { color: var(--hero-ink-eyebrow); }
/* 눈썹→h1 간격 6px (2026-09-09 Teo) — 서체 후보 비교 전, h1 바로 위 눈썹만.
   :has(+ h1) 로 좁혀서 다른 히어로(h2 앞 눈썹)는 기존 --sp-2(16px) 그대로다. */
.hero-full .hf-copy .eyebrow:has(+ h1) { margin-bottom: 6px; }
.hero-full .hf-copy .lead,
.hero-full .hf-copy .muted   { color: var(--hero-ink-lead); }
/* ★ 2026-09-06 — .muted 를 추가했다. IMG-02·09(브랜드 "지키는 것" ·
   "한 번에 300개만") 를 사진 위 히어로로 바꾸면서(⑭) .muted 문단이
   처음으로 이 안에 들어왔다 — 측정 전까지는 .lead 와 같은 톤으로 맞춘다. */
.hero-full .hf-copy h1 em    { color: var(--hero-ink-em); }
.hero-full .hf-copy .hm-n    { color: var(--hero-ink-meta); }
/* ★ 지표 라벨만 한 단 더 올린다(--c-muted → --c-text).
   히어로 맨 아래 줄이라 브러쉬·밴드 어느 쪽으로도 못 받쳤다. 좁은 화면에서
   바닥 농도를 0 에서 .9 까지 올려 봐도 3.68 에서 한 자리도 안 움직였다 —
   그 자리가 밴드(400px) 아래이고 브러쉬의 옅은 가장자리이기 때문이다.
   층을 더 올리면 사진이 사라진다. 그래서 글자 쪽을 고친다.
   ★ 리넨 히어로 안에서만이다. 다른 곳의 --c-faint 위계는 그대로다. */
.hero-full .hf-copy .hm-l    { color: var(--hero-ink-meta); }
/* 라벨은 한 줄이다. 두 줄로 접히면 그 열만 줄간격이 벌어져 세 열의 밑선이
   어긋난다(2026-09-05, 390 "배치 생산량"). 안 들어가면 글자를 줄이지 말고
   **문구를 줄인다** — 그래서 "배치"로 축약했다. */
.hero-full .hf-copy .hm-l    { white-space: nowrap; }

/* ── 지표 줄 ─────────────────────────────────────────────────────
   어떤 폭에서도 가로 3열이다. 공용 규칙이 ≤560 에서 flex-wrap: wrap 을
   걸어 두는데(위 @media 560), 그러면 "9가지·300개 / 6개월" 로 2+1 로
   접혀 세 지표가 한 눈에 안 들어온다.
   ★ 좁아지면 **글자가 아니라 간격**을 줄인다. 글자 크기는 §2 최소 15px
     에 걸려 있고, 줄이면 지표가 장식이 된다. */
.hero-full .hf-copy .hero-meta {
  flex-wrap: nowrap;
  gap: clamp(var(--sp-1), 4vw, var(--sp-5));
  /* ★ 위 가로선을 없앤다. 사진 위에 얹힌 히어로에서 그 선만 화면을
     가로질러 남아, 지표 줄이 사진 위가 아니라 종이 위에 있는 것처럼
     보인다. 공용 .hero-meta 가 border-top 을 주므로 여기서 끈다. */
  border-top: 0;
  padding-top: 0;
  /* ★ 상자의 왼쪽 변이 버튼과 같은 세로선에 선다(2026-09-05 Teo 규칙).
     너비는 내용만큼이되 **버튼 행보다 좁아지지 않는다** — 최소 너비를
     버튼 행 실측값(--hf-actions-w)에 묶는다. 히어로 글자는 한 세로선에서
     시작하고, 상자도 그 선에 선다. */
  width: fit-content;
  min-width: var(--hf-actions-w);
  max-width: 100%;
  margin-inline: 0;
  justify-content: flex-start;
  /* ★ flex 가 아니라 grid 다(2026-09-05). 열 셋을 같은 폭으로 두되 **가장
     긴 내용보다 좁아지지 않아야** 한다. flex: 1 1 0 은 상자가 최소 너비
     (351)에 걸리면 열을 73px 로 눌러 "300개"(82px)가 넘쳤다. grid 의
     1fr 은 max-content 계산에서 세 열을 가장 넓은 내용에 맞춰 같은 폭으로
     잡고, 상자가 그보다 넓으면 남는 폭을 고르게 나눈다. */
  display: grid;
  /* 열 수를 적지 않는다(2026-09-05). 홈은 셋, 문의 히어로는 둘이다 —
     repeat(3, 1fr) 로 두면 둘짜리 상자에 빈 열이 남는다. auto-flow column
     + 1fr 은 몇 개든 같은 폭으로 나눈다. */
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  /* 둥근 22% 면 — 모든 폭(2026-09-05 Teo). 전에는 ≤900 만이었다. */
  padding: var(--meta-bg-pad);
  border-radius: var(--meta-bg-round);
  background: color-mix(in srgb, var(--c-bg) var(--meta-bg-alpha), transparent);
}
/* 열 안에서는 숫자와 라벨이 같은 세로축에 가운데 선다. 상자 자체는 왼쪽
   선에 있고 열 안만 가운데다. */
.hero-full .hf-copy .hero-meta > * { min-width: 0; text-align: center; }

/* 지표 뒤 둥근 면의 내력 — 2026-09-04 걷었다가 같은 날 되살렸고(좁은 화면만),
   2026-09-05 Teo 지시로 **모든 폭**으로 넓혔다(위 .hero-meta 규칙 안).
   걷었을 때 320~390 에서 지표 대비가 2.3~3.8 로 내려앉았다(기준 4.5).
   사진 위 사각형 하나와 지표 가독 사이에서 가독을 골랐다. */

/* 사진 위 보조 버튼 (2026-09-05) — 사진이 비치는 반투명 채움 + 반투명
   테두리 + 진한 글자. 채움을 올려서 읽히게 하지 않는다 — 그러면 옅은
   불투명 상자가 되어 글자가 상자에 묻힌다. 블러 필터는 쓰지 않는다. */
.hero-full .hf-copy .btn-ghost {
  color: var(--c-text);
  background: color-mix(in srgb, var(--ghost-bg-color) var(--ghost-bg-alpha), transparent);
  border-color: color-mix(in srgb, var(--c-text) var(--ghost-line-alpha), transparent);
}
/* 호버·포커스도 같은 흰색 계열 — 채움만 한 단 진해진다. 공용 .btn-ghost:hover
   가 --c-surface 로 갈아 끼우는데, 사진 위에서는 그게 불투명 상자다. */
.hero-full .hf-copy .btn-ghost:hover,
.hero-full .hf-copy .btn-ghost:focus-visible {
  background: color-mix(in srgb, var(--ghost-bg-color) var(--ghost-bg-alpha-hover), transparent);
  opacity: 1;
}

/* 히어로 본문 줄바꿈 — 2026-08-30 부터 **모든 폭**에 건다.
   ★ 처음에는 ≥901 전용이었다. 좁은 화면에서 문장이 스스로 접히는데 여기서
     또 끊으면 줄이 는다고 봤기 때문이다. Teo 가 PC 와 같은 자리에서
     끊기로 정했다 — 좁은 화면에서 몇 줄이 되는지는 재서 보고한다. */
.br-wide { display: inline; }

.hero-full h1 { font-size: var(--t-display); line-height: var(--lh-tight); }
.hero-full .lead { margin-top: var(--sp-3); max-width: var(--measure-tight); }

/* ── 서브페이지 히어로 (2026-09-05) ──────────────────────────────
   홈과 같은 구조(사진 위 글자 + 번짐)를 쓰되 **높이와 제목 크기만** 서브
   규격이다. 높이는 §5-3 의 --hero-h-sub(2열 히어로가 쓰던 값 그대로),
   제목은 페이지 대제목(--t-h1) — 대형 카피(--t-display)는 홈 하나다.
   ★ 좁은 화면도 같은 식이다. .hero-full 의 ≤900 규칙(clamp 470~660)보다
     이 선택자가 구체적이라 한 줄로 두 폭을 덮는다. */
.hero-full.hero-sub { min-height: max(var(--hero-h-sub-min), var(--hero-h-sub)); }
.hero-full.hero-sub h1 { font-size: var(--t-h1); }

/* ─── 공정 타임라인 — .steps (2026-09-05, 리넨 "만드는 순서") ────────
   넓은 화면은 가로 한 줄(5단계 = 5열), 좁은 화면은 세로 한 줄. 선 하나와
   점 하나가 "순서"를 만든다 — 번호는 .eyebrow, 제목은 .h3, 설명은 .muted
   로 있는 것을 쓴다. 전에는 사진 + 표(.spec 5행)였다.
   ★ 열 수는 항목 수다. 단계가 늘면 grid-template-columns 만 바꾼다. */
.steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--sp-4); }
.steps > li { position: relative; padding-top: var(--sp-3); border-top: var(--bw) solid var(--c-line-2); }
.steps > li::before {
  content: ''; position: absolute; left: 0; top: calc(-1 * var(--bw));
  width: 9px; height: 9px; border-radius: 50%; background: var(--c-accent);
  transform: translateY(-50%);
}
.steps .eyebrow { margin-bottom: var(--sp-1); }
.steps p { margin-top: var(--sp-1); }

/* ─── 사진 위 목록 3형 (2026-09-06 Teo) — 가로줄·라벨 열 없이 ─────────
   전부 .hf-copy 안에 담기면 §12 번짐이 알아서 뒤에 깔린다. 공통 부품이라
   페이지 인라인 스타일을 만들지 않는다. */

/* a) 한 줄 인라인 — 라벨(.muted, 사진 위 잉크 자동 적용) + 공백 + 내용 */
.list-inline { list-style: none; margin: 0; padding: 0; }
.list-inline > li + li { margin-top: 0.6em; }
.list-inline .muted { margin-right: 0.5em; }

/* b) 번호 목록 — 세리프 큰 숫자 + 소제목이 같은 줄 baseline */
.num-list { list-style: none; margin: 0; padding: 0; }
.num-list > li { display: flex; align-items: baseline; gap: var(--sp-3); }
.num-list > li + li { margin-top: var(--sp-4); }
.num-list .num {
  font-family: var(--font-head); font-weight: var(--fw-h1);
  font-size: calc(var(--t-h3) * 1.6); line-height: 1; flex: none;
  color: var(--c-muted);
}
.num-list .num-body p { margin-top: var(--sp-2); }

/* c) 세로 레일 — 왼쪽 1px 선 + 점, 점 옆에 연도 + 내용 한 줄 */
.rail-list { list-style: none; margin: 0; padding: 0; }
.rail-list > li {
  position: relative; padding: 0 0 var(--sp-4) var(--sp-4);
  border-left: var(--bw) solid var(--c-line-2);
  display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap;
}
.rail-list > li:last-child { padding-bottom: 0; }
.rail-list > li::before {
  content: ''; position: absolute; left: calc(-1 * var(--bw)); top: 0;
  width: 7px; height: 7px; border-radius: 50%; background: var(--c-accent);
  transform: translate(-50%, 4px);
}
.rail-list .rail-year {
  font-family: var(--font-mono); font-size: var(--t-eyebrow);
  letter-spacing: var(--ls-wide); white-space: nowrap; color: var(--c-muted);
}
@media (max-width: 560px) {
  .rail-list > li { flex-direction: column; gap: var(--sp-1); }
}
/* 사진 위(.hf-copy) 잉크 — --c-faint 대신 4.5 이상 나오는 톤(§13) */
.hero-full .hf-copy .num-list .num,
.hero-full .hf-copy .rail-list .rail-year { color: var(--hero-ink-lead); }
@media (max-width: 900px) {
  .steps { grid-template-columns: 1fr; gap: 0; }
  .steps > li { padding: 0 0 var(--sp-4) var(--sp-3); border-top: 0; border-left: var(--bw) solid var(--c-line-2); }
  .steps > li:last-child { padding-bottom: 0; }
  .steps > li::before { left: calc(-1 * var(--bw)); top: 0; transform: translate(-50%, 4px); }
}

/* 대형 작업물 그리드 — 캡션을 최소화하고 이미지를 크게 */
.works-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }
.works-grid.dense { grid-template-columns: repeat(3, 1fr); }
.work { position: relative; display: block; overflow: hidden; background: var(--c-surface); }
.work .ph { border-radius: 0; }
.work .w-cap { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); padding: var(--sp-2) 0 0; }
.work .w-t { font-family: var(--font-head); font-size: var(--t-h3); font-weight: var(--fw-head); }
.work .w-m { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); white-space: nowrap; }
/* 한 칸을 두 칸 폭으로 — 리듬을 만든다 */
.work.wide { grid-column: span 2; }

/* 가로 스크롤 스트립 — 작업물을 훑어보게 */
.strip-x { display: flex; gap: var(--sp-2); overflow-x: auto; padding-bottom: var(--sp-2); scroll-snap-type: x mandatory; }
.strip-x > * { flex: 0 0 clamp(240px, 32vw, 420px); scroll-snap-align: start; }

/* 전면 인용 — 큰 타이포로 한 문장만 */
/* 전환 섹션 — 여백이 도구인 자리다. 다만 --section-py 를 쓰면 1920 에서
   채움 29~39% 로 하한(40%)조차 못 넘어 "의도"가 아니라 "잔여물"로 읽힌다.
   한 단 줄여 여백을 남기되 의도가 보이게 한다. */
.pull { padding-block: var(--section-py-sm); text-align: center; }
/* ★ --t-h2 가 아니라 --t-h1 이다. 이 섹션의 존재 이유가 "대형 타이포가
   단독으로 서는 자리"(v2 R2-3)인데, h2 크기로는 두 줄짜리 문장이 여백을
   못 버틴다 — 프레임 스튜디오에서 채움 29%가 나왔다. 크기가 곧 근거다. */
.pull p { font-family: var(--font-head); font-size: var(--t-h1); line-height: var(--lh-head); letter-spacing: var(--ls-head); color: var(--c-text); max-width: 14em; margin: 0 auto; }
.pull .pull-by { font-family: var(--font-mono); font-size: var(--t-eyebrow); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--c-faint); margin-top: var(--sp-3); }

/* ═══════════════════════════════════════════════════════════════
   인용구 섹션 — .quote (v3 §4)

   ★ .pull 을 대체한다. .pull 은 검정 위에 글자만 놓는 구성이었고,
     그것을 페이지마다 두세 번 반복한 것이 2.5차 검수의 지적
     ("전부 순수 검정 배경 + 텍스트만 있어 페이지 전체가 단조로움")이다.
     .pull 은 아직 리넨·얼로이가 쓰고 있어 남겨 둔다 — E-3 전파 때 함께
     넘어온다.

   구성
     <section class="quote">
       <div class="plx" data-plx="weak" style="background-image:url('/demo/frame/img/img-10-1600.webp')"></div>
       <div class="wrap wrap-copy quote-in">
         <blockquote>두 줄까지</blockquote>
         <div class="quote-by">Eser Studio</div>
       </div>
     </section>

   ★ 배경은 <img> 가 아니라 background-image 다. 패럴랙스가 이 요소를
     확대하고 움직이는데, <img> 로 두면 그 변형이 접근성 트리와
     지연로딩에 함께 얹힌다. 장식은 장식으로 둔다 (alt 도 없다).

   ★ 경로는 인라인 style 의 background-image 로 직접 주고, **루트 기준
     절대 경로**로 적는다. 커스텀 프로퍼티(--quote-img)에 담아 CSS 에서
     var() 로 받는 방법을 먼저 썼는데, 크롬이 그 안의 url() 을 이
     스타일시트(_shared/) 기준으로 풀어 _shared/img/… 를 찾으러 갔다.
     ★ 그 뒤 상대 경로로 적었더니 이번에는 **주소 모양**에 따라 기준이
       달라졌다 — /demo/frame 으로 들어오면 /demo/img/… 를 찾는다.
       "어느 파일에서 푸느냐"도 "어느 주소로 들어왔느냐"도 자산의 위치와
       무관하다. 루트에서부터 적으면 둘 다 질문이 아니게 된다.

   ★ 인용문은 최대 두 줄이다 (§4). 세 줄이 되면 카피를 줄인다.
     대형 타이포가 단독으로 서던 자리를 배경 이미지가 대신 채우므로,
     크기로 여백을 버틸 필요가 없어졌다 — h2 토큰으로 내려온 이유다.
   ═══════════════════════════════════════════════════════════════ */

.quote {
  position: relative; overflow: hidden;
  --quote-overlay: linear-gradient(to bottom,
    color-mix(in srgb, var(--c-bg) 90%, transparent),
    color-mix(in srgb, var(--c-bg) 76%, transparent) 50%,
    color-mix(in srgb, var(--c-bg) 92%, transparent));
}
/* 글이 많은 섹션(표·목록)을 사진 위에 올릴 때 — 리넨 성분·공정 "전 성분"
   (2026-09-06 Teo, 식 그대로). 인용구 오버레이(76%)로는 표 글자가 안 읽혔다. */
.quote.quote-dense {
  --quote-overlay: linear-gradient(to bottom,
    color-mix(in srgb, var(--c-bg) 97%, transparent),
    color-mix(in srgb, var(--c-bg) 92%, transparent) 90%,
    color-mix(in srgb, var(--c-bg) 97%, transparent));
}

.quote > .plx {
  background-size: cover;
  background-position: center;
}

/* 어둠 오버레이 — 가운데가 가장 밝고 위아래로 짙어진다.
   ★ 가운데 76%가 하한이다 (§4 는 72% 이상). 배경이 순백이어도
     #F5F5F4 글자 대비가 8.8:1 로 AA 를 넘는다 — 어떤 사진이 들어와도
     읽히게 하는 것이 이 숫자의 목적이다.
   ★ --c-bg 로 흐린다. 밝은 데모(리넨)에서는 종이색 오버레이에
     어두운 글자가 되어 같은 규칙이 그대로 성립한다. */
.quote::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: var(--quote-overlay);
}

/* 글자 층은 .wrap 이면 된다 — 인용구(.quote-in)뿐 아니라 섹션 하나를 통째로
   사진 위에 올릴 때(리넨 성분·공정 "전 성분", 2026-09-05)도 같은 그릇을 쓴다:
   <section class="section quote"> + .plx + .wrap. 오버레이 농도는 그대로다. */
.quote > .wrap { position: relative; z-index: 2; }
.quote-in {
  padding-block: var(--section-py);
  text-align: center;
}

.quote blockquote {
  margin: 0 auto;
  max-width: 20em;
  font-family: var(--font-head);
  font-size: var(--t-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  font-weight: var(--fw-h2);
  color: var(--c-text);
}

.quote-by {
  font-family: var(--font-mono);
  font-size: var(--t-eyebrow);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-faint);
  margin-top: var(--sp-3);
}

@media (max-width: 900px) {
  .works-grid, .works-grid.dense { grid-template-columns: 1fr; }
  .work.wide { grid-column: span 1; }

  /* ── ≤900 히어로 (2026-09-06 Teo ⑭⑮⑯) ────────────────────────
     PC 와 같은 구조 — 사진 전체 + 글자 블록 뒤 번짐 하나(::before, 여백·
     blur 만 작다). 정렬도 PC 와 같다 — 가운데로 모으지 않는다. 여기서는
     히어로 전체 높이만 mobile 값으로 낮춘다. .hero-full.hero-sub 는 더
     구체적이라 이 값을 이긴다(위 §5-3 주석). */
  .hero-full { min-height: clamp(470px, 76svh, 660px); }
  /* 글자 블록 뒤로 사진이 드러날 구간을 확보한다(2026-09-06 Teo) — 글자가
     사진을 다 덮지 않도록 높이를 더 주는 히어로만 이 클래스를 단다.
     ★ 2026-09-09 — 전에는 "초점을 오른쪽 아래로 옮긴(--hero-focus-m) 히어로"가
       기준이었는데, 초점 조정 자체가 없어졌다(위 object-position 주석). 높이만
       남은 규칙이다. */
  .hero-full.hero-tall-m { min-height: calc(clamp(470px, 76svh, 660px) + var(--hero-extra-m, 40svh)); }
  /* 자리표시자(사진 없는 슬롯)는 왼쪽 위에 붙인다 */
  .hero-full .hf-media .ph {
    justify-content: flex-start;
    align-items: flex-start;
    padding: var(--sp-3) var(--gutter);
  }
  .hero-full .hf-media .ph > * { margin-left: 0; margin-right: 0; }
  .hero-full .hf-media .ph .ph-label { text-align: left; }
  .hero-full .hf-media .ph::after { inset: var(--sp-2); }
  .hero-full .hf-copy { --hf-py: var(--sp-4); }
}

/* 버튼이 두 줄로 접히는 폭 — 상자를 글 블록 전 폭으로, 맨 아래에.
   ★ 왜 398: 버튼 둘(149 + 16 + 186 = 351)이 한 줄에 서려면 콘텐츠 폭이
     351 이상이어야 하고, 좌우 여백 24×2 를 더하면 399 다. 398 에서 접힌다
     (실측 — 398 은 두 줄, 399 는 한 줄). 접히면 버튼 행 너비가 186 이
     되어 세 열(261 필요)이 못 들어간다. 그래서 이 폭에서만 전 폭이다.
   ★ 버튼 문구가 바뀌어 접히는 폭이 달라지면 이 숫자도 같이 바꾼다.
   ★ 아래 변은 히어로 바닥에 붙이고 위 모서리만 둥글다. 전 폭 상자의
     좌우 모서리를 둥글리면 화면 변에 홈이 파인다. */
@media (max-width: 398px) {
  .hero-full { align-items: stretch; }
  .hero-full .hf-copy { display: flex; flex-direction: column; padding-bottom: 0; }
  .hero-full .hf-copy .hero-meta {
    margin-top: auto;
    padding-top: var(--meta-bg-pad);
    width: auto;
    min-width: 0;
    max-width: none;
    margin-inline: calc(-1 * var(--gutter));
    border-radius: var(--meta-bg-round) var(--meta-bg-round) 0 0;
  }
  /* 지표 위 여백 — 버튼과 상자 사이 최소 간격. margin-top: auto 가 남는
     높이를 다 먹으므로, 히어로가 낮아 남는 높이가 없을 때만 이 값이 선다. */
  .hero-full .hf-copy .btn-row { margin-bottom: var(--sp-5); }
}


/* ═══════════════════════════════════════════════════════════════
   본문 가독 폭 — 컨테이너를 넓혀도 긴 문단은 여기서 멈춘다.
   컨테이너만 넓히고 문단이 화면 끝까지 퍼지면 오히려 읽기 나빠진다.
   카드·표·좁은 컨테이너 안은 이미 폭이 제한돼 있어 대상이 아니고,
   이미지·카드 그리드는 넓어진 폭을 그대로 다 쓴다.
   ═══════════════════════════════════════════════════════════════ */
.lead { max-width: var(--measure); }

/* 섹션 바로 아래 놓인 문단 (카드/표 안이 아닌 것) */
.section > .wrap > p,
.section > .wrap > .stack > p,
.section-sm > .wrap > p { max-width: var(--measure); }

/* 히어로 카피는 더 짧게 — 제목 옆이라 길면 눈이 흔들린다 */
.hero .lead { max-width: var(--measure-tight); }
.hero-full .lead { max-width: var(--measure-tight); }

/* CTA 밴드의 설명문 */
.cta p { max-width: var(--measure); }

/* 필요할 때 직접 붙이는 유틸 */
.measure { max-width: var(--measure); }

/* 그리드 열수 — docs/DESIGN_SYSTEM.md §4 표와 일치시킨다.
   .grid-4는 1024에서 2열을 유지한다(4열이면 카드가 222px로 눌린다). */
@media (min-width: 1280px) {
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
}


/* ═══════════════════════════════════════════════════════════════
   유틸리티 — 파일 끝에 둔다 (v3 §2)

   ★ 순서가 규칙이다. .cta 와 .ft 는 스스로 background 를 정하는
     부품이라, 배경 유틸이 그 위에 앉으려면 뒤에 와야 한다.
     선택자 특이도로 이기려 하지 말 것 — !important 는 어느 쪽이
     이겼는지 화면만 보고는 알 수 없게 만든다.
   ═══════════════════════════════════════════════════════════════ */

/* ─── §9-1 컨테이너 라인 스냅 ──────────────────────────────────
   한 섹션에 요소가 둘 이상 가로로 놓이면 **첫 요소는 컨테이너 좌측 라인,
   마지막 요소는 우측 라인**에 붙는다. 어중간한 중간 지점 배치 금지.

     <div class="wrap wrap-wide cta-in row-snap">
       <div> 제목 · 설명 </div>
       <div class="btn-row"> 버튼 </div>
     </div>

   ★ 2.5차까지 .cta-in 은 1.3fr .7fr 격자였다. 버튼이 둘째 칸의 "왼쪽"에
     붙으니 컨테이너 우측 라인에서 한참 떨어진 지점에 떠 있었고, 그것이
     "버튼이 어중간한 데 있어 좌측 쏠림"으로 읽혔다. 칸을 나누는 방식으로는
     마지막 요소를 우측 라인에 붙일 수 없다 — 나눈 칸의 왼쪽에 붙기 때문이다.
     space-between 은 남는 폭을 요소 사이로 보내므로 양 끝이 라인에 닿는다.

   ★ 파일 끝(유틸리티)에 둔다. .cta-in 의 display:grid 를 순서로 이긴다.

   ★ 리넨·얼로이는 아직 이 클래스를 안 붙였다. 전파(E-3)는 마크업에
     row-snap 을 더하는 것으로 끝난다 — 규칙은 여기 하나뿐이다. */
.row-snap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  flex-wrap: wrap;
}
/* 첫 요소가 남는 폭을 먹고, 마지막 요소는 제 크기만 갖는다.
   24em 은 되감김 기준 — 그보다 좁아지면 두 줄로 내려간다. */
.row-snap > :first-child { flex: 1 1 24em; min-width: 0; }
.row-snap > :last-child  { flex: 0 0 auto; }
.row-snap > .btn-row:last-child { justify-content: flex-end; }

/* 되감긴 뒤(한 줄에 못 들어갈 때)에는 버튼이 좌측 라인으로 돌아온다.
   좁은 화면에서 버튼만 오른쪽 끝에 떨어져 있으면 본문과 무관해 보인다. */
@media (max-width: 700px) {
  .row-snap > .btn-row:last-child { justify-content: flex-start; }
}

/* ─── 터치 대상 하한 (2.9차 I-3) ──────────────────────────────
   손가락이 닿는 것은 44×44px 이상이어야 한다. 글자 크기를 키우는 것이
   아니라 **누를 수 있는 상자**를 키운다 — 인라인 요소의 padding 은
   줄 높이를 바꾸지 않으면서 히트 영역만 넓히므로 레이아웃이 흔들리지
   않는다.

   ★ 데스크톱에서는 걸지 않는다. 마우스 포인터는 1px 을 짚는다.
   ────────────────────────────────────────────────────────── */
@media (pointer: coarse), (max-width: 900px) {
  /* 메뉴 버튼 — 모바일의 주 조작부다. 40×36 이었다. */
  .burger { width: 44px; height: 44px; }

  /* 푸터 목록 링크 — 28×19 였다. 줄 간격을 건드리지 않고 상자만 키운다. */
  .ft li a { display: flex; align-items: center; min-height: 44px; }
  .ft li + li { margin-top: 0; }

  /* 데모 배너 링크·브랜드 — 문장 안에 있어 줄 높이를 바꾸면 안 된다.
     인라인 padding 은 히트 영역만 넓힌다. */
  .demo-bar a { padding-block: var(--sp-2); }
  .brand { padding-block: var(--sp-2); }

  /* 좁은 화면 : 로고를 폭에 비례해 줄인다.
     상한은 데스크톱 값(16 / 15px)이라 넓은 쪽에서는 아무 일도 없다 —
     헤더가 모자란 폭에서만 작아진다. 하한은 읽을 수 있는 선에서 멈춘다.
     ★ 여기는 §1 하한(caption 15px)의 예외다. 브랜드는 읽는 문장이
     아니라 알아보는 표지이고, 줄어드는 것과 접히는 것 중 하나를
     골라야 하는 자리다. 본문·칩에는 이 예외를 옮기지 않는다. */
  .brand .b-mark { font-size: clamp(13px, 4.2vw, var(--t-h4)); }
  .brand .b-sub  { font-size: clamp(10px, 3.2vw, var(--t-eyebrow)); }
}

/* ─── 배경 3단 ────────────────────────────────────────────────
   인접 섹션에 같은 톤을 연속으로 쓰지 않는다. bg-0 → bg-1 → bg-0 →
   bg-2 … 로 교차하고, 틴트(bg-2)는 한 페이지에 1~2회까지다.

   ★ 값은 데모의 tokens.css 가 정한다. 아직 v3 를 이행하지 않은 데모
     (리넨·얼로이 — 전파는 E-3)에서는 --bg-* 가 없어 폴백으로 떨어진다.
     그쪽 화면이 지금과 같게 유지되는 것이 폴백의 목적이다. */
.bg-0 { background: var(--bg-0, var(--c-bg)); }
.bg-1 { background: var(--bg-1, var(--c-bg-alt)); }
.bg-2 { background: var(--bg-2, var(--c-surface)); }

/* 톤이 갈리면 경계는 이미 보인다. 거기에 선까지 그으면 두 번 말하는
   것이 되므로, 같은 톤이 맞붙는 자리에서만 선을 남긴다. */
.section + .section,
.bg-0 + .bg-0, .bg-1 + .bg-1, .bg-2 + .bg-2 { border-top: var(--bw) solid var(--c-line); }
.bg-0 + .bg-1, .bg-0 + .bg-2,
.bg-1 + .bg-0, .bg-1 + .bg-2,
.bg-2 + .bg-0, .bg-2 + .bg-1 { border-top: none; }
