/* ========================================================================== RFS — 메인 스타일시트 (2026-10-06 재작업) 구조 원칙: 페이지는 밝은 쿨 바탕 한 체제다. 어두운 면은 캔버스 화면(.hero-stage) 하나뿐이고 그것은 띠가 아니라 계기판의 부품이다. 이전 판의 .band-dark/.band-light 토큰 재정의 규칙은 띠 교대 자체를 없앴으므로 같이 없앴다. 토큰은 tokens.css 한 곳에서만 정의한다. ========================================================================== */ /* Spoqa Han Sans Neo — OFL. jsdelivr 고정 버전 3.3.0, 한글 서브셋(Subset/) woff2. 쓰는 굵기는 400·700 두 종만 선언한다 — 패밀리 CSS 를 그대로 싣으면 5종이 선언되고 브라우저가 500·300 을 합성 대신 요청할 수 있다 */ @font-face { font-family: "Spoqa Han Sans Neo"; font-weight: 400; font-display: swap; src: local("Spoqa Han Sans Neo Regular"), url("https://cdn.jsdelivr.net/gh/spoqa/spoqa-han-sans@3.3.0/Subset/SpoqaHanSansNeo/SpoqaHanSansNeo-Regular.woff2") format("woff2"); } @font-face { font-family: "Spoqa Han Sans Neo"; font-weight: 700; font-display: swap; src: local("Spoqa Han Sans Neo Bold"), url("https://cdn.jsdelivr.net/gh/spoqa/spoqa-han-sans@3.3.0/Subset/SpoqaHanSansNeo/SpoqaHanSansNeo-Bold.woff2") format("woff2"); } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; scroll-behavior: smooth; overflow-x: hidden; background: var(--surface); font-synthesis-style: none; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } body { min-height: 100dvh; font-family: var(--font-body); font-size: var(--step-0); line-height: var(--leading-normal); background: var(--surface); color: var(--ink); word-break: keep-all; overflow-wrap: anywhere; line-break: strict; overflow-x: hidden; } img, picture, canvas, svg { display: block; max-width: 100%; } a { color: inherit; text-decoration: none; text-underline-position: under; } button, input, select, textarea { font: inherit; color: inherit; } em, i, cite { font-style: normal; font-weight: var(--weight-strong); } h1, h2, h3 { font-weight: var(--weight-strong); line-height: var(--leading-tight); letter-spacing: -0.01em; text-wrap: balance; } strong { font-weight: var(--weight-strong); color: var(--ink); } .num { font-variant-numeric: tabular-nums; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; } .wrap { width: min(100% - 2 * var(--pad-inline), var(--container)); margin-inline: auto; } /* ── 접근성 ─────────────────────────────────────────────────────── */ .skip-link { position: fixed; top: var(--space-3); left: var(--space-3); z-index: 200; padding: var(--space-2) var(--space-3); background: var(--ink); color: var(--surface); transform: translateY(-250%); transition: transform var(--dur-quick) var(--ease); } .skip-link:focus-visible { transform: translateY(0); } :where(a, button, input, [tabindex]):focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; } /* ── 상단 바 — 고정하지 않는다. 페이지가 다섯 화면 남짓이라 늘 보일 이유가 없다 ── */ .site-head { background: var(--surface); border-bottom: 1px solid var(--line); } .head-inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 56px; min-width: 0; } .wordmark { display: inline-flex; align-items: center; gap: var(--space-2); flex-shrink: 0; color: var(--ink); } .wordmark-mark { display: block; width: 28px; color: var(--accent); } .wordmark-text { font-weight: var(--weight-strong); letter-spacing: 0.04em; } .nav { display: flex; align-items: center; gap: clamp(var(--space-3), 2vw, var(--space-5)); min-width: 0; } .nav a { color: var(--ink-muted); font-size: var(--step--1); white-space: nowrap; padding: var(--space-2) var(--space-1); transition: color var(--dur-quick) var(--ease); } .nav a:hover { color: var(--ink); } .nav .nav-cta { color: var(--accent); font-weight: var(--weight-strong); } /* ── 계기판 (히어로) ───────────────────────────────────────────── */ .hero-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); padding-block: var(--space-4) var(--space-6); } .hero h1 { font-size: var(--step-2); max-width: 30em; } .hero-lead { margin-top: var(--space-2); max-width: var(--measure); color: var(--ink-muted); } .hero-screen { min-width: 0; } /* 캔버스 화면 — 페이지에서 유일한 어두운 면 */ .hero-stage { position: relative; width: 100%; height: clamp(220px, 40dvh, 340px); overflow: clip; background: var(--screen); border: 1px solid var(--screen-line); } #thermalField { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity var(--dur-slow) var(--ease); } [data-field-state="live"] #thermalField { opacity: 1; } .hero-fallback { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 46%; opacity: 0.72; mix-blend-mode: screen; transition: opacity var(--dur-slow) var(--ease); } /* 캔버스가 실제로 돌기 시작하면 실물 렌더는 물러난다 — 둘이 겹치면 무엇이 실시간인지 알 수 없다 */ [data-field-state="live"] .hero-fallback { opacity: 0; } .stage-grid { position: absolute; inset: 0; background-image: linear-gradient(to right, var(--screen-line) 1px, transparent 1px), linear-gradient(to bottom, var(--screen-line) 1px, transparent 1px); background-size: 64px 64px; opacity: 0.4; pointer-events: none; } /* 장비 형상 — 셰이더 상수와 같은 좌표. 랙 x 44%–56% · FWU x 3% 부터 */ .stage-solid { position: absolute; pointer-events: none; border: 1px solid var(--screen-line); } .stage-rack { left: 44%; right: 44%; bottom: 12%; top: 38%; background: repeating-linear-gradient( to bottom, color-mix(in srgb, var(--screen) 92%, transparent) 0 6px, color-mix(in srgb, var(--screen-2) 92%, transparent) 6px 8px ); } .stage-fwu { left: 3%; width: 2.5%; bottom: 10%; height: 24%; background: color-mix(in srgb, var(--screen-2) 88%, transparent); border-right: 2px solid var(--screen-accent); } .stage-tag { position: absolute; display: flex; align-items: center; gap: var(--space-2); font-size: var(--step--1); color: var(--screen-ink-muted); pointer-events: none; } .stage-tag span { background: color-mix(in srgb, var(--screen) 82%, transparent); padding: 2px var(--space-2); border-left: 2px solid var(--screen-accent); } /* 캔버스가 돌기 전(loading·JS 없음)과 정지 상태(감소 모션·WebGL 없음)에서는 실물 렌더가 남는다. 2D 데모의 장비 좌표는 실물 렌더와 맞지 않으므로 형상·표식·격자를 live 상태에서만 보인다 — 그림과 라벨이 어긋나면 라벨이 거짓말이 된다 */ [data-field-shell]:not([data-field-state="live"]) .stage-solid, [data-field-shell]:not([data-field-state="live"]) .stage-tag, [data-field-shell]:not([data-field-state="live"]) .stage-grid { display: none; } .stage-tag-fwu { left: 5.5%; bottom: 34%; } .stage-tag-rack { left: 44%; bottom: 62%; } .stage-foot { display: grid; gap: var(--space-2); padding-top: var(--space-3); } .temp-key { display: flex; flex-wrap: wrap; gap: var(--space-3); color: var(--ink-muted); font-size: var(--step--1); } .temp-key span { display: inline-flex; align-items: center; gap: var(--space-2); } .temp-key i { width: 1.5rem; height: 3px; background: var(--temp-cold); } .temp-key .temp-mid { background: var(--temp-mid); } .temp-key .temp-hot { background: var(--temp-hot); } .hero-note { max-width: var(--measure); font-size: var(--step--1); color: var(--ink-muted); line-height: var(--leading-loose); } /* 제어 패널 — 흰 면 하나에 장면·슬라이더·판독이 위아래로 쌓인다 */ .hero-panel { display: grid; grid-template-rows: auto auto 1fr; background: var(--panel); border: 1px solid var(--line); min-width: 0; } .panel-block { padding: var(--space-3) var(--space-4); } .panel-block + .panel-block { border-top: 1px solid var(--line); } .panel-title { font-size: var(--step--1); color: var(--ink-faint); margin-bottom: var(--space-2); } .scenario-btns { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); } .scenario-btn, .vis-tab { min-height: 44px; padding-inline: var(--space-2); border: 1px solid var(--line-strong); background: transparent; color: var(--ink-muted); cursor: pointer; font-size: var(--step--1); transition: color var(--dur-quick) var(--ease), background-color var(--dur-quick) var(--ease), border-color var(--dur-quick) var(--ease); } .scenario-btn + .scenario-btn, .vis-tab + .vis-tab { border-left: 0; } .scenario-btn:hover, .vis-tab:hover { color: var(--ink); border-color: var(--accent); } .scenario-btn[aria-pressed="true"], .vis-tab[aria-pressed="true"] { color: var(--accent-ink); background: var(--accent); border-color: var(--accent); } .hero-controls { display: grid; gap: var(--space-3); } .ctl-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); font-size: var(--step--1); color: var(--ink-muted); margin-bottom: var(--space-1); } .ctl-head output { color: var(--ink); font-weight: var(--weight-strong); } input[type="range"] { width: 100%; min-height: 44px; /* 터치 타깃 — 트랙은 가늘지만 누르는 영역은 44px */ accent-color: var(--accent); background: transparent; } .field-readout-state { color: var(--ink); } .field-readout-grid { display: grid; gap: var(--space-2); margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--line); } .field-readout-grid > div { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: var(--space-2); font-size: var(--step--1); } .field-readout-grid dt { color: var(--ink-faint); } .field-readout-grid dd { color: var(--ink-muted); } /* 900px 부터 계기판: 왼쪽 표제 + 캔버스, 오른쪽 제어 패널 */ @media (min-width: 900px) { .hero-grid { grid-template-columns: minmax(0, 1fr) var(--panel-width); grid-template-rows: auto auto; column-gap: var(--space-5); row-gap: var(--space-3); align-items: start; padding-block: var(--space-5) var(--space-6); } .hero-copy { grid-column: 1; grid-row: 1; } .hero-screen { grid-column: 1; grid-row: 2; } .hero-panel { grid-column: 2; grid-row: 1 / span 2; align-self: stretch; /* 패널 바닥이 캔버스 바닥과 같은 선에 닿는다 */ } .hero-note { grid-column: 1; grid-row: 3; } .hero-stage { height: clamp(26rem, 54vh, 34rem); } } /* ── 섹션 공통 — 한 체제. 섹션 사이는 선 하나와 간격으로만 나눈다 ── */ .section { border-top: 1px solid var(--line); } .section > .wrap { padding-block: var(--space-8); } .section h2 { font-size: var(--step-2); max-width: 24em; } .sub-heading { font-size: var(--step-1); max-width: 24em; } .section-lead, .split-note { margin-top: var(--space-3); max-width: var(--measure); color: var(--ink-muted); line-height: var(--leading-loose); } .section-lead { font-size: var(--step-1); } /* 초광폭에서는 정보 표면만 넓힌다. 읽는 글의 폭은 각 블록의 max-width 가 계속 제어한다 */ @media (min-width: 120rem) { .wrap { width: min(100% - 2 * var(--pad-inline), var(--container-wide)); } } /* ── 레이아웃 패밀리 A · 비대칭 2열 ─────────────────────────────── */ .split { display: grid; gap: var(--space-5); } @media (min-width: 900px) { .split { grid-template-columns: 5fr 7fr; gap: var(--space-7); align-items: start; } } .split-body > * + * { margin-top: var(--space-4); } .split-body p { max-width: var(--measure); color: var(--ink-muted); } /* ── 레이아웃 패밀리 B · 대조 목록 ──────────────────────────────── */ .contrast { margin-top: var(--space-5); border-top: 1px solid var(--line-strong); } .contrast > div { display: grid; gap: var(--space-1) var(--space-4); padding-block: var(--space-3); border-bottom: 1px solid var(--line); } @media (min-width: 640px) { .contrast > div { grid-template-columns: 11rem 1fr; } } .contrast dt { font-weight: var(--weight-strong); color: var(--ink); } .contrast dd { color: var(--ink-muted); font-size: var(--step--1); line-height: var(--leading-loose); } .contrast > div:last-child dt { color: var(--accent); } /* ── 레이아웃 패밀리 C · 사양 원장 (행 문법) ────────────────────── */ .spec-ledger { margin-top: var(--space-5); list-style: none; border-top: 1px solid var(--line-strong); } .spec-ledger li { display: grid; gap: var(--space-1) var(--space-4); padding-block: var(--space-3); border-bottom: 1px solid var(--line); } @media (min-width: 720px) { .spec-ledger li { grid-template-columns: 9rem 14rem 1fr; align-items: baseline; } } .spec-name { color: var(--ink-faint); font-size: var(--step--1); } .spec-value { color: var(--ink); font-weight: var(--weight-strong); } .spec-note { color: var(--ink-muted); font-size: var(--step--1); line-height: var(--leading-loose); } /* ── 레이아웃 패밀리 D · 계층 ──────────────────────────────────── */ .spec-ledger + .sub-heading { margin-top: var(--space-6); } .layers { margin-top: var(--space-4); list-style: none; border: 1px solid var(--line-strong); background: var(--panel); } .layers li { display: grid; gap: var(--space-1) var(--space-4); padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--line); } .layers li:last-child { border-bottom: 0; } @media (min-width: 760px) { .layers li { grid-template-columns: 3rem 13rem 1fr; align-items: baseline; } } /* 아래로 갈수록 물리 계층에 가까워진다는 것을 면의 농도로 말한다 */ .layers li:nth-child(2) { background: color-mix(in srgb, var(--surface-2) 25%, transparent); } .layers li:nth-child(3) { background: color-mix(in srgb, var(--surface-2) 50%, transparent); } .layers li:nth-child(4) { background: color-mix(in srgb, var(--surface-2) 75%, transparent); } .layers li:nth-child(5) { background: var(--surface-2); } .layer-id { color: var(--accent); font-size: var(--step--1); font-weight: var(--weight-strong); } .layer-name { font-weight: var(--weight-strong); } .layer-body { color: var(--ink-muted); font-size: var(--step--1); line-height: var(--leading-loose); } /* ── 레이아웃 패밀리 E · 장비 목록 ─────────────────────────────── */ .equip-list { margin-top: var(--space-5); display: grid; gap: var(--space-4); border-top: 1px solid var(--line-strong); padding-top: var(--space-4); } @media (min-width: 760px) { .equip-list { grid-template-columns: repeat(2, 1fr); column-gap: var(--space-7); } /* 다섯 개다. 마지막 하나가 홀로 남으므로 두 칸을 쓰게 한다 */ .equip-list article:last-child { grid-column: 1 / -1; border-top: 1px solid var(--line); padding-top: var(--space-4); } } .equip-list h3 { font-size: var(--step-1); display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; } .equip-list p { margin-top: var(--space-2); color: var(--ink-muted); max-width: 30em; line-height: var(--leading-loose); } .tag { font-size: var(--step--1); font-weight: 400; color: var(--accent); border: 1px solid var(--line-strong); padding: 1px var(--space-2); } .editor { margin-top: var(--space-6); padding-top: var(--space-5); border-top: 1px solid var(--line-strong); } /* ── 기능 행 ────────────────────────────────────────────────────── */ .feature-rows { border-top: 1px solid var(--line); } .feature-rows > div { display: grid; gap: var(--space-1) var(--space-4); padding-block: var(--space-3); border-bottom: 1px solid var(--line); } @media (min-width: 640px) { .feature-rows > div { grid-template-columns: 9rem 1fr; } } .feature-rows dt { font-weight: var(--weight-strong); } .feature-rows dd { color: var(--ink-muted); font-size: var(--step--1); line-height: var(--leading-loose); } .keys { margin-top: var(--space-4); padding: var(--space-3) var(--space-4); background: var(--panel); border: 1px solid var(--line); } .keys-title { font-size: var(--step--1); color: var(--ink-faint); margin-bottom: var(--space-2); } .keys ul { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); font-size: var(--step--1); color: var(--ink-muted); } .keys li { display: inline-flex; align-items: center; gap: var(--space-2); } kbd { font-family: inherit; font-variant-numeric: tabular-nums; font-size: var(--step--1); padding: 1px 6px; background: var(--surface); border: 1px solid var(--line-strong); border-bottom-width: 2px; color: var(--ink); } kbd + kbd { margin-left: 2px; } /* ── 레이아웃 패밀리 F · 시각화 (목록 + 실물) ──────────────────── */ .vis-layout { margin-top: var(--space-5); display: grid; gap: var(--space-5); } @media (min-width: 960px) { .vis-layout { grid-template-columns: 6fr 5fr; gap: var(--space-7); align-items: start; } } .vis-list { list-style: none; counter-reset: vis; border-top: 1px solid var(--line-strong); } .vis-list li { counter-increment: vis; padding-block: var(--space-4); border-bottom: 1px solid var(--line); display: grid; grid-template-columns: 2.5rem 1fr; gap: var(--space-2) var(--space-3); } .vis-list li::before { content: counter(vis, decimal-leading-zero); font-variant-numeric: tabular-nums; font-size: var(--step--1); color: var(--accent); font-weight: var(--weight-strong); grid-row: 1 / span 2; padding-top: 0.35em; } .vis-list h3 { font-size: var(--step-1); } .vis-list p { color: var(--ink-muted); font-size: var(--step--1); line-height: var(--leading-loose); grid-column: 2; } .vis-shots { display: grid; gap: var(--space-3); min-width: 0; } .vis-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); } .vis-tabs { display: flex; } .vis-tab { min-width: 4.5rem; } .vis-source { color: var(--ink-faint); font-size: var(--step--1); text-align: right; } .vis-frame { min-width: 0; } /* 실물 렌더는 어두운 바탕의 그림이다 — 캔버스 화면과 같은 면 위에 싣는다 */ .vis-shots figure { display: grid; grid-template-rows: minmax(0, 1fr) auto; border: 1px solid var(--screen-line); background: var(--screen); min-height: clamp(320px, 42vw, 520px); } .vis-shots figure[hidden] { display: none; } .vis-shots img { width: 100%; height: 100%; min-height: 0; object-fit: contain; } .vis-shots figcaption { padding: var(--space-2) var(--space-3); border-top: 1px solid var(--screen-line); font-size: var(--step--1); color: var(--screen-ink-muted); } .shots-note { font-size: var(--step--1); color: var(--ink-faint); line-height: var(--leading-loose); } /* ── 요구사양 표 ────────────────────────────────────────────────── */ .req { width: 100%; border-collapse: collapse; font-size: var(--step--1); background: var(--panel); border: 1px solid var(--line); } .req caption { text-align: left; } .req th, .req td { text-align: left; padding: var(--space-3); border-bottom: 1px solid var(--line); vertical-align: top; } .req thead th { color: var(--ink-faint); font-weight: 400; border-bottom: 1px solid var(--line-strong); } .req tbody th { font-weight: var(--weight-strong); width: 7rem; } .req td { color: var(--ink-muted); font-variant-numeric: tabular-nums; } /* ── CTA ────────────────────────────────────────────────────────── */ .adopt-cta { margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--line-strong); } .btn { display: inline-block; margin-top: var(--space-4); padding: var(--space-3) var(--space-5); background: var(--accent); color: var(--accent-ink); font-weight: var(--weight-strong); white-space: nowrap; border: 1px solid var(--accent); transition: background-color var(--dur-quick) var(--ease); } .btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-ink); } /* ── 다열 색인 푸터 ─────────────────────────────────────────────── */ .site-foot { border-top: 1px solid var(--line-strong); background: var(--surface-2); } .foot-grid { display: grid; gap: var(--space-5) var(--space-6); padding-block: var(--space-6) var(--space-5); } @media (min-width: 640px) { .foot-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (min-width: 960px) { .foot-grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; } } .foot-col ul { list-style: none; display: grid; gap: var(--space-1); font-size: var(--step--1); color: var(--ink-muted); } .foot-col a { color: var(--ink-muted); padding-block: 4px; display: inline-block; } .foot-col a:hover { color: var(--accent); text-decoration: underline; } .foot-head { font-size: var(--step--1); color: var(--ink); font-weight: var(--weight-strong); margin-bottom: var(--space-2); } .foot-name { font-weight: var(--weight-strong); letter-spacing: 0.04em; } .foot-line { margin-top: var(--space-2); color: var(--ink-muted); font-size: var(--step--1); max-width: 24em; line-height: var(--leading-loose); } .foot-facts { display: grid; gap: var(--space-1); font-size: var(--step--1); } .foot-facts > div { display: grid; grid-template-columns: 4.5rem 1fr; gap: var(--space-2); } .foot-facts dt { color: var(--ink-faint); } .foot-facts dd { color: var(--ink-muted); } .foot-fine { padding-bottom: var(--space-5); } .foot-fine p { padding-top: var(--space-3); border-top: 1px solid var(--line); font-size: var(--step--1); color: var(--ink-faint); line-height: var(--leading-loose); max-width: 44em; } /* ── 좁은 화면 ──────────────────────────────────────────────────── */ @media (max-width: 640px) { .head-inner { flex-wrap: wrap; padding-block: var(--space-2); row-gap: var(--space-2); } .nav { order: 3; width: 100%; justify-content: space-between; gap: var(--space-2); border-top: 1px solid var(--line); padding-top: var(--space-2); } .stage-tag { font-size: 0.6875rem; } .panel-block { padding-inline: var(--space-3); } .req th, .req td { padding-inline: var(--space-2); } .vis-shots figure { min-height: 300px; } } /* ── 모션을 줄이는 설정 — 캔버스는 스크립트가 정지시키고 실물 렌더가 남는다 ── */ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* ── 인쇄 ───────────────────────────────────────────────────────── */ @media print { .site-head, .hero-stage, .hero-panel, .skip-link { display: none; } .section > .wrap { padding-block: var(--space-4); } }