docs/static/css/main.css (31755B)
1 /* sqzass documentation
2 *
3 * Dark first, and one accent. The page is otherwise achromatic, because the
4 * colour that matters is in the code: highlighting is the feature this tool
5 * does properly, and a second palette competing with it would be noise. The
6 * accent is warm on purpose — it sits away from the blues and greens of the
7 * syntax theme instead of arguing with them.
8 *
9 * The accent carries meaning, never decoration: links, the focus ring, the
10 * current item, and the rule under the page title. If it is orange, it is
11 * something you can go to or something you are on.
12 *
13 * The +++ rule appears once per page, between the title block and the body.
14 * That is the same boundary it marks in a source file: front matter ends,
15 * content begins. It is the one decorative-looking thing here and it is not
16 * decoration.
17 *
18 * The theme is chosen by [data-theme], written into the document by the
19 * template and overridden from localStorage — not by prefers-color-scheme.
20 * The generated highlight stylesheet keys off the same attribute, so the two
21 * cannot disagree, including with JavaScript off.
22 */
23
24 :root {
25 color-scheme: dark;
26
27 --paper: #0e1013;
28 --raised: #171a1e;
29 --ink: #e8eaec;
30 --ink-2: #a7adb4;
31 --ink-3: #6f767e;
32 --rule: #242930;
33 --accent: #f0a15e;
34
35 --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans KR",
36 "Apple SD Gothic Neo", "Malgun Gothic", Roboto, sans-serif;
37 --mono: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas,
38 "Liberation Mono", monospace;
39
40 --measure: 68ch;
41 --gutter: clamp(1.25rem, 4vw, 3rem);
42
43 /* 크롬 컨트롤의 치수. 검색·언어·GitHub·테마가 전부 이 값을 쓴다.
44 하나만 다른 모양이면 그 하나가 고장난 것처럼 보인다.
45 터미널에서 모는 도구라 키캡처럼 다룬다 — 얇은 테두리, 작은 반경. */
46 --control-h: 2rem;
47 --radius: 3px;
48 --masthead-h: 3.5rem;
49 /* 한 박자. 값을 여기 한 번만 두면 모든 상호작용이 같은 속도로 반응하고,
50 그게 "같은 물건"이라는 느낌을 만든다. */
51 --tr: 0.14s ease;
52
53 --s-1: 0.8125rem;
54 --s0: 1.0625rem;
55 --s1: 1.25rem;
56 --s2: 1.5rem;
57 --s3: 1.9rem;
58 --s4: 2.5rem;
59 }
60
61 :root[data-theme="light"] {
62 color-scheme: light;
63
64 --paper: #fbfbfc;
65 --raised: #f1f3f5;
66 --ink: #14171a;
67 --ink-2: #4a5058;
68 --ink-3: #767d86;
69 --rule: #e2e5e9;
70 /* Darker than the dark theme's accent: the same orange that reads clearly on
71 near-black fails contrast on near-white. */
72 --accent: #9a4e05;
73 }
74
75 *, *::before, *::after { box-sizing: border-box; }
76
77 html {
78 -webkit-text-size-adjust: 100%;
79 /* 목차나 heading anchor를 눌렀을 때 뛰지 않고 미끄러진다. 어디로 갔는지가
80 보여야 방금 누른 것과 지금 보는 것을 연결할 수 있다. */
81 scroll-behavior: smooth;
82 }
83
84 /* 스크롤바를 사이트의 일부로 다룬다. 코드 블록 하나만 손보고 페이지·사이드바·
85 목차·검색 결과를 기본값으로 두면, 스크롤이 생기는 자리마다 다른 물건이 나온다.
86 Firefox는 표준 속성을, 나머지는 -webkit- 의사요소를 본다. */
87 * {
88 scrollbar-width: thin;
89 scrollbar-color: var(--rule) transparent;
90 }
91 ::-webkit-scrollbar { width: 8px; height: 8px; }
92 ::-webkit-scrollbar-track { background: transparent; }
93 ::-webkit-scrollbar-thumb {
94 background: var(--rule);
95 border-radius: 999px;
96 }
97 ::-webkit-scrollbar-thumb:hover { background: var(--ink-3); }
98 ::-webkit-scrollbar-corner { background: transparent; }
99
100 /* 선택 영역. 기본 파랑은 이 사이트의 어느 색과도 관계가 없다. */
101 ::selection {
102 background: color-mix(in srgb, var(--accent) 30%, transparent);
103 color: var(--ink);
104 }
105
106 body {
107 margin: 0;
108 background: var(--paper);
109 color: var(--ink);
110 font: var(--s0)/1.7 var(--sans);
111 /* Korean does not break inside a word. Without this, browsers split 어절
112 mid-word at narrow widths and the text becomes hard to read. */
113 word-break: keep-all;
114 overflow-wrap: anywhere;
115 }
116
117 a { color: var(--accent); text-underline-offset: 0.2em; transition: color var(--tr); }
118 a:hover { text-decoration-thickness: 2px; }
119
120 :focus-visible {
121 outline: 2px solid var(--accent);
122 outline-offset: 2px;
123 border-radius: 2px;
124 }
125
126 .skip {
127 position: absolute;
128 left: -9999px;
129 top: 0;
130 background: var(--paper);
131 color: var(--ink);
132 padding: 0.6rem 1rem;
133 z-index: 10;
134 }
135 .skip:focus { left: 0; }
136
137 /* ---------- header ---------- */
138
139 /* 높이를 고정한다. 내용에 따라 늘어나면 페이지마다 상단바 높이가 달라지고,
140 그게 "비율이 이상하다"로 읽힌다.
141 align-items는 center다. baseline이면 로고를 품은 브랜드와 옆 내비가 서로
142 다른 기준선에 앉아 글자들이 어긋난 것처럼 보인다. */
143 .masthead {
144 position: sticky;
145 top: 0;
146 z-index: 20;
147 display: flex;
148 align-items: center;
149 gap: clamp(0.75rem, 2.5vw, 2rem);
150 height: var(--masthead-h);
151 padding: 0 var(--gutter);
152 background: var(--paper);
153 border-bottom: 1px solid var(--rule);
154 }
155
156 .brand {
157 display: inline-flex;
158 align-items: center;
159 gap: 0.5rem;
160 flex: none;
161 font-family: var(--mono);
162 font-size: var(--s1);
163 font-weight: 600;
164 letter-spacing: -0.03em;
165 color: var(--ink);
166 text-decoration: none;
167 /* 좁은 화면에서 "sqzass"가 한 글자씩 세로로 쪼개지던 자리. */
168 white-space: nowrap;
169 }
170 .brand:hover { color: var(--accent); }
171
172 /* 검은 선화라 다크 배경에서는 그냥 안 보인다. 파일을 두 벌 두는 대신 반전한다 —
173 순수 흑백 선화에서는 반전이 정확히 흰 선을 만든다. */
174 .brand img {
175 width: 1.9em;
176 height: 1.9em;
177 filter: invert(1);
178 }
179 :root[data-theme="light"] .brand img { filter: none; }
180
181 /* 문서 갈래. 사이드바와 같은 데이터를 한 단계 위에서. */
182 .sections {
183 display: flex;
184 align-items: center;
185 gap: clamp(0.6rem, 1.6vw, 1.4rem);
186 min-width: 0;
187 overflow: hidden;
188 font-size: var(--s-1);
189 white-space: nowrap;
190 }
191 .sections a {
192 color: var(--ink-2);
193 text-decoration: none;
194 line-height: var(--masthead-h);
195 border-bottom: 2px solid transparent;
196 }
197 .sections a { transition: color var(--tr), border-color var(--tr); }
198 .sections a:hover { color: var(--ink); }
199 .sections a[aria-current] { color: var(--ink); border-bottom-color: var(--accent); }
200
201 .tools {
202 margin-left: auto;
203 display: flex;
204 align-items: center;
205 gap: 0.4rem;
206 flex: none;
207 }
208
209 /* ---------- chip: 크롬 컨트롤 하나의 모양 ----------
210 검색, 언어, GitHub, 테마가 전부 이것이다. 링크든 버튼이든 겉모습은 같아야
211 한다 — 사용자에게는 둘 다 "누르는 것"이고, 하나만 맨 텍스트면 그 하나가
212 빠뜨린 것처럼 보인다. */
213 .chip {
214 display: inline-flex;
215 align-items: center;
216 gap: 0.5rem;
217 height: var(--control-h);
218 padding: 0 0.6rem;
219 font: inherit;
220 font-family: var(--mono);
221 font-size: var(--s-1);
222 line-height: 1;
223 color: var(--ink-2);
224 background: none;
225 border: 1px solid var(--rule);
226 border-radius: var(--radius);
227 text-decoration: none;
228 white-space: nowrap;
229 cursor: pointer;
230 }
231 .chip { transition: color var(--tr), background var(--tr), border-color var(--tr); }
232 .chip:hover {
233 color: var(--ink);
234 background: var(--raised);
235 border-color: var(--ink-3);
236 }
237
238 /* 아이콘만 있는 컨트롤은 정사각이어야 한다. 글자 폭에 따라 너비가 달라지면
239 같은 줄의 컨트롤들이 서로 다른 리듬을 갖는다. */
240 .chip-icon {
241 width: var(--control-h);
242 padding: 0;
243 justify-content: center;
244 }
245 .chip-icon svg { display: block; }
246
247 /* 버튼은 상태가 아니라 동작을 말한다. 다크에서는 해(→ 라이트로), 라이트에서는 달. */
248 .theme-toggle .i-moon { display: none; }
249 :root[data-theme="light"] .theme-toggle .i-sun { display: none; }
250 :root[data-theme="light"] .theme-toggle .i-moon { display: block; }
251
252 .search-trigger { min-width: 11rem; justify-content: space-between; }
253 .search-trigger kbd {
254 font: inherit;
255 font-size: 0.7rem;
256 color: var(--ink-3);
257 border: 1px solid var(--rule);
258 border-radius: var(--radius);
259 padding: 0.1rem 0.3rem;
260 }
261
262 /* ---------- breadcrumb ---------- */
263
264 .crumbs {
265 display: flex;
266 flex-wrap: wrap;
267 gap: 0.5rem;
268 font-family: var(--mono);
269 font-size: 0.75rem;
270 letter-spacing: 0.06em;
271 text-transform: uppercase;
272 margin-bottom: 0.9rem;
273 }
274 .crumbs a { color: var(--ink-2); text-decoration: none; }
275 .crumbs a:hover { color: var(--accent); text-decoration: underline; }
276 /* 마지막 조각은 현재 위치라 링크가 아니다. 다른 조각과 같은 밝기면 어디까지가
277 갈 수 있는 곳인지 읽히지 않는다. */
278 .crumbs a:last-child { color: var(--ink); }
279 .crumbs a + a::before { content: "/"; margin-right: 0.5rem; color: var(--rule); }
280
281 /* ---------- layout ---------- */
282
283 .layout {
284 display: grid;
285 grid-template-columns: 15rem minmax(0, 1fr) 13rem;
286 gap: clamp(1.5rem, 4vw, 3rem);
287 align-items: start;
288 max-width: 84rem;
289 margin: 0 auto;
290 padding: 2.5rem var(--gutter) 5rem;
291 }
292
293 main { min-width: 0; max-width: var(--measure); }
294
295 .sidebar, .toc {
296 position: sticky;
297 /* 상단바가 sticky이므로 그 아래에 붙는다. */
298 top: calc(var(--masthead-h) + 1.5rem);
299 font-size: var(--s-1);
300 line-height: 1.6;
301 max-height: calc(100vh - var(--masthead-h) - 3rem);
302 overflow-y: auto;
303 }
304
305 /* 사이드바는 높이를 채운다. 내용 높이에 맞추면 경계선이 목록 끝에서 뚝 끊겨
306 구역이 아니라 미완성으로 읽힌다. */
307 .sidebar { height: calc(100vh - var(--masthead-h) - 3rem); }
308
309 /* ---------- sidebar ---------- */
310
311 /* <details>다. 넓은 화면에서는 열린 채 고정이라 summary를 감추고, 좁은 화면에서만
312 서랍이 된다. 구역을 나누는 건 오른쪽 경계선 하나 — 배경을 따로 깔지 않아도
313 사이드바가 본문과 다른 층이라는 게 읽힌다. */
314 /* 구역을 나누는 건 경계선 하나다. 여기에 면과 라운드를 더 얹어 봤더니 사이드바가
315 구역이 아니라 떠 있는 카드로 읽혔다. 층은 항목의 hover와 현재 표시가 만든다 —
316 판 전체에 색을 깔면 그 층이 묻힌다. */
317 .sidebar {
318 border-right: 1px solid var(--rule);
319 padding-right: 1rem;
320 }
321 .sidebar > summary { display: none; }
322
323 .nav-tree, .nav-tree ul { list-style: none; margin: 0; padding: 0; }
324 .nav-tree > li + li { margin-top: 0.15rem; }
325
326 /* 갈래 제목. 누르면 접힌다는 걸 셰브런이 말한다 — 텍스트로 쓸 수 없는 자리라
327 여기서는 도형을 쓰되, 폰트가 아니라 CSS로 그려서 폴백 문제를 피한다. */
328 .nav-group > summary {
329 display: flex;
330 align-items: center;
331 gap: 0.5rem;
332 padding: 0.4rem 0.6rem;
333 border-radius: var(--radius);
334 font-family: var(--mono);
335 font-size: 0.75rem;
336 letter-spacing: 0.08em;
337 text-transform: uppercase;
338 color: var(--ink-3);
339 cursor: pointer;
340 list-style: none;
341 }
342 .nav-group > summary::-webkit-details-marker { display: none; }
343 .nav-group > summary:hover { color: var(--ink); background: var(--raised); }
344
345 .nav-group > summary::before {
346 content: "";
347 width: 0.35rem;
348 height: 0.35rem;
349 border-right: 1.5px solid currentColor;
350 border-bottom: 1.5px solid currentColor;
351 transform: rotate(-45deg);
352 transition: transform var(--tr);
353 }
354 .nav-group[open] > summary::before { transform: rotate(45deg); }
355
356 .nav-tree a {
357 display: block;
358 padding: 0.35rem 0.6rem;
359 border-radius: var(--radius);
360 color: var(--ink-2);
361 text-decoration: none;
362 }
363 .nav-tree a { transition: color var(--tr), background var(--tr); }
364 .nav-tree a:hover { color: var(--ink); background: var(--raised); }
365
366 /* 현재 항목은 판 전체 폭을 쓰는 알약이다. 왼쪽 막대 하나보다 훨씬 빨리 눈에 띈다. */
367 .nav-tree a[aria-current="page"] {
368 color: var(--ink);
369 font-weight: 600;
370 background: var(--raised);
371 box-shadow: inset 2px 0 0 var(--accent);
372 }
373
374 .nav-group > ul { margin: 0.1rem 0 0.6rem; padding-left: 0.6rem; }
375
376 /* ---------- table of contents ---------- */
377
378 .toc ul { list-style: none; margin: 0; padding: 0; }
379 .toc > ul > li + li { margin-top: 0.15rem; }
380 .toc ul ul { padding-left: 0.85rem; }
381 .toc a {
382 display: block;
383 padding: 0.2rem 0 0.2rem 0.7rem;
384 border-left: 2px solid transparent;
385 color: var(--ink-3);
386 text-decoration: none;
387 }
388 .toc a { transition: color var(--tr), border-color var(--tr); }
389 .toc a:hover { color: var(--ink); }
390 /* 지금 읽고 있는 자리. ui.js가 스크롤을 보고 표시한다 — 없으면 그냥 목록이다. */
391 .toc a[aria-current] { color: var(--ink); border-left-color: var(--accent); }
392
393 .toc::before {
394 content: attr(data-label);
395 display: block;
396 font-family: var(--mono);
397 font-size: 0.75rem;
398 letter-spacing: 0.08em;
399 text-transform: uppercase;
400 color: var(--ink-3);
401 margin-bottom: 0.6rem;
402 }
403
404 /* ---------- title block and the +++ rule ---------- */
405
406 /* Mono carries structure -- wordmark, headings, labels, code. The sans face is
407 reserved for prose. For a tool you drive from a terminal that split says
408 something; it also keeps headings visually adjacent to the commands they
409 introduce. Mono runs wide, so the scale is pulled in a step. */
410 .title-block h1 {
411 font-family: var(--mono);
412 font-size: var(--s3);
413 font-weight: 600;
414 letter-spacing: -0.04em;
415 line-height: 1.15;
416 margin: 0;
417 }
418
419 /* A short accent rule under the title. It is the one place the page states
420 "this is where you are" in colour rather than in words. */
421 .title-block h1::after {
422 content: "";
423 display: block;
424 width: 2.5rem;
425 height: 2px;
426 margin-top: 0.7rem;
427 background: var(--accent);
428 }
429
430 .title-block .lede {
431 color: var(--ink-2);
432 font-size: var(--s1);
433 line-height: 1.55;
434 margin: 0.6rem 0 0;
435 }
436
437 /* The front matter fence. Ends the metadata, opens the content -- exactly
438 what +++ does in the source file this page was written in. */
439 .fence {
440 display: flex;
441 align-items: center;
442 gap: 0.85rem;
443 font-family: var(--mono);
444 font-size: var(--s0);
445 font-weight: 500;
446 letter-spacing: 0.1em;
447 color: var(--ink-3);
448 margin: 1.6rem 0 2.25rem;
449 user-select: none;
450 }
451 .fence::after {
452 content: "";
453 flex: 1;
454 border-top: 1px solid var(--rule);
455 }
456
457 /* ---------- prose ---------- */
458
459 .prose > :first-child { margin-top: 0; }
460
461 .prose h2, .prose h3, .prose h4 {
462 font-family: var(--mono);
463 font-weight: 600;
464 letter-spacing: -0.03em;
465 line-height: 1.3;
466 /* 상단바가 sticky이므로 그 높이만큼 비켜야 한다. 이 값이 작으면 목차를
467 눌렀을 때 제목이 상단바 아래로 들어가 보이지 않는다 — 실제로 24px 가렸다. */
468 scroll-margin-top: calc(var(--masthead-h) + 1.5rem);
469 }
470 .prose h2 { font-size: var(--s2); margin: 2.9rem 0 0.9rem; }
471 .prose h3 { font-size: var(--s1); margin: 2.25rem 0 0.7rem; }
472 .prose h4 { font-size: var(--s0); margin: 1.75rem 0 0.5rem; }
473
474 /* h2 opens a section; a hairline above it makes that legible at a glance
475 without adding another visual device. */
476 .prose h2 {
477 border-top: 1px solid var(--rule);
478 padding-top: 2.9rem;
479 }
480 .prose h2:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
481
482 .prose p, .prose ul, .prose ol { margin: 0 0 1.1rem; }
483 .prose li + li { margin-top: 0.3rem; }
484
485 /* The anchor is there for people who want to link to a section, not for
486 everyone else to look at. */
487 .anchor {
488 margin-left: 0.4rem;
489 color: var(--ink-3);
490 text-decoration: none;
491 opacity: 0;
492 font-weight: 400;
493 transition: opacity var(--tr), color var(--tr);
494 }
495 .anchor:hover { color: var(--accent); }
496 h1:hover > .anchor, h2:hover > .anchor, h3:hover > .anchor,
497 h4:hover > .anchor, h5:hover > .anchor, h6:hover > .anchor,
498 .anchor:focus-visible { opacity: 1; }
499 @media (hover: none) { .anchor { opacity: 1; } }
500
501 /* ---------- code ---------- */
502
503 .prose code {
504 font-family: var(--mono);
505 font-size: 0.875em;
506 background: var(--raised);
507 padding: 0.15em 0.35em;
508 border-radius: 3px;
509 }
510
511 .prose pre.highlight {
512 position: relative;
513 font-family: var(--mono);
514 font-size: 0.875rem;
515 line-height: 1.6;
516 background: var(--raised);
517 border: 1px solid var(--rule);
518 border-radius: var(--radius);
519 padding: 0.9rem 1.1rem;
520 margin: 0 0 1.3rem;
521 overflow-x: auto;
522
523 /* 넘치는 코드에는 스크롤이 있다는 표시가 필요하다. 잘린 줄만 보이고 아무
524 힌트가 없으면 사람들은 그게 전부인 줄 안다. 스크롤바 모양은 위쪽 전역
525 규칙이 정하고, 여기서는 대비만 한 단계 올린다 — 코드 블록의 면이 본문보다
526 밝아서 같은 색이면 묻힌다. */
527 scrollbar-color: var(--ink-3) transparent;
528 }
529 .prose pre.highlight::-webkit-scrollbar-thumb { background: var(--ink-3); }
530
531 /* 복사 버튼. 도구 설정 키가 아니라 이 사이트의 CSS와 JS다 — 문서에도 그렇게
532 적어 두었고, 여기가 그 말을 지키는 자리다.
533 포커스로도 닿아야 하므로 hover에만 기대지 않는다. */
534 .copy {
535 position: absolute;
536 top: 0.45rem;
537 right: 0.45rem;
538 height: 1.5rem;
539 padding: 0 0.45rem;
540 font: inherit;
541 font-size: 0.7rem;
542 color: var(--ink-3);
543 background: var(--paper);
544 border: 1px solid var(--rule);
545 border-radius: var(--radius);
546 cursor: pointer;
547 opacity: 0;
548 transition: opacity var(--tr), color var(--tr), border-color var(--tr);
549 /* 버튼은 pre 안에 있어서, 블록을 통째로 긁어 복사하면 이 라벨까지 따라온다.
550 선택에서 빼면 복사문이 코드만 남는다 — 줄 번호 레시피와 같은 이유다. */
551 user-select: none;
552 }
553 .prose pre.highlight:hover .copy,
554 .copy:focus-visible { opacity: 1; }
555 .copy:hover { color: var(--ink); border-color: var(--ink-3); }
556 .copy[data-done] { color: var(--accent); border-color: var(--accent); opacity: 1; }
557
558 /* 터치 화면에는 hover가 없다. 항상 보이게 둔다. */
559 @media (hover: none) {
560 .prose pre.highlight .copy { opacity: 1; }
561 }
562 .prose pre.highlight code {
563 /* 블록으로 넓혀 두는 이유: hl-line 표시와 data-name 라벨이 이 박스의 폭을
564 물려받는다. auto 폭이면 스크롤되는 블록에서 보이는 폭까지만 칠해져서,
565 옆으로 넘긴 순간 강조가 사라진 채로 줄의 나머지가 보인다. */
566 display: block;
567 width: max-content;
568 min-width: 100%;
569 background: none;
570 padding: 0;
571 border-radius: 0;
572 font-size: inherit;
573 }
574
575 /* 펜스 옵션 `hl_lines=`가 감싼 줄. <mark>의 브라우저 기본(노란 배경, 검은 글자)은
576 두 테마 어느 쪽과도 안 맞으므로 악센트 기운의 면으로 바꾼다. 음수 마진으로
577 pre의 좌우 패딩까지 칠해 "줄 전체"가 표시된 것으로 읽히게 한다. */
578 .prose pre.highlight mark.hl-line {
579 display: block;
580 margin: 0 -1.1rem;
581 padding: 0 1.1rem;
582 background: color-mix(in srgb, var(--accent) 12%, transparent);
583 box-shadow: inset 2px 0 0 var(--accent);
584 color: inherit;
585 }
586
587 /* 펜스 옵션 `name=`. 빌드는 data-name 속성만 내보내고 보여주는 건 사이트 몫이다 —
588 여기가 그 몫을 하는 자리. */
589 .prose pre.highlight code[data-name]::before {
590 content: attr(data-name);
591 display: block;
592 margin: -0.9rem -1.1rem 0.9rem;
593 padding: 0.4rem 1.1rem;
594 font-size: 0.75rem;
595 color: var(--ink-3);
596 background: var(--paper);
597 border-bottom: 1px solid var(--rule);
598 border-radius: var(--radius) var(--radius) 0 0;
599 }
600
601 /* ---------- benchmark bars ---------- */
602
603 /* 도구 식별은 행 라벨이, 수치는 막대 옆 텍스트가 운반한다. 색은 우리(악센트)와
604 비교군(중립) 둘뿐이라 색이 단독으로 정보를 나르지 않는다. 막대 폭이 style
605 속성이 아니라 SVG rect의 표현 속성인 이유: 인라인 style은 이 사이트가
606 지켜 온 strict style-src CSP 가능성을 깨기 때문이다. */
607 .bench {
608 display: grid;
609 grid-template-columns: auto 1fr;
610 gap: 0.45rem 0.8rem;
611 align-items: center;
612 margin: 0.8rem 0 1.8rem;
613 }
614 .bench .b-name {
615 font-family: var(--mono);
616 font-size: 0.8rem;
617 color: var(--ink);
618 text-align: right;
619 }
620 .bench .b-row { display: flex; align-items: center; gap: 0.6rem; min-width: 0; }
621 .bench svg { flex: 1; min-width: 0; height: 0.75rem; display: block; }
622 .bench rect { fill: var(--ink-3); opacity: 0.55; }
623 .bench rect.b-us { fill: var(--accent); opacity: 1; }
624 .bench .b-val {
625 font-family: var(--mono);
626 font-size: 0.78rem;
627 color: var(--ink-2);
628 white-space: nowrap;
629 }
630
631 /* ---------- tables ---------- */
632
633 /* `display: block`이었다. 넓은 표를 가로로 굴리려던 것인데, 그러면 표가 내부
634 레이아웃을 잃고 내용 폭으로 쭈그러들어 본문의 40%만 쓰는 모양이 된다.
635 우리 표는 짧은 텍스트 두세 열이라 그냥 감싸면 되고, 진짜 넓은 표가 생기면
636 그때 감싸는 상자를 두는 게 맞다. */
637 .prose table {
638 border-collapse: collapse;
639 width: 100%;
640 max-width: 100%;
641 font-size: var(--s-1);
642 margin: 0 0 1.3rem;
643 }
644 .prose th, .prose td {
645 text-align: left;
646 padding: 0.55rem 0.9rem 0.55rem 0;
647 border-bottom: 1px solid var(--rule);
648 vertical-align: top;
649 }
650 /* 마지막 열의 오른쪽 여백은 필요 없다 — 표의 오른쪽 끝이 이미 여백이다. */
651 .prose th:last-child, .prose td:last-child { padding-right: 0; }
652 /* 머리글과 본문 사이는 더 굵게. 어디까지가 라벨인지가 한눈에 보여야 한다. */
653 .prose thead th { border-bottom-color: var(--ink-3); }
654 .prose th {
655 font-family: var(--mono);
656 font-size: 0.75rem;
657 letter-spacing: 0.06em;
658 text-transform: uppercase;
659 color: var(--ink-3);
660 font-weight: 500;
661 }
662
663 /* ---------- admonitions (comrak alerts) ---------- */
664
665 .prose .markdown-alert {
666 border-left: 2px solid var(--ink-3);
667 padding: 0.1rem 0 0.1rem 1rem;
668 margin: 0 0 1.3rem;
669 color: var(--ink-2);
670 }
671 .prose .markdown-alert-title {
672 font-family: var(--mono);
673 font-size: 0.75rem;
674 letter-spacing: 0.08em;
675 text-transform: uppercase;
676 color: var(--ink-3);
677 margin: 0 0 0.35rem;
678 }
679 .prose .markdown-alert > p:last-child { margin-bottom: 0; }
680
681 .prose blockquote {
682 margin: 0 0 1.3rem;
683 padding-left: 1rem;
684 border-left: 2px solid var(--rule);
685 color: var(--ink-2);
686 }
687
688 .prose hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }
689
690 /* ---------- section index ---------- */
691
692 .children { list-style: none; margin: 0; padding: 0; }
693 .children li { border-top: 1px solid var(--rule); }
694 .children li:last-child { border-bottom: 1px solid var(--rule); }
695 .children a {
696 display: block;
697 padding: 0.9rem 0;
698 text-decoration: none;
699 color: var(--ink);
700 }
701 .children a:hover { color: var(--accent); }
702 .children .desc { display: block; color: var(--ink-3); font-size: var(--s-1); margin-top: 0.15rem; }
703
704 /* ---------- footer ---------- */
705
706 .colophon {
707 border-top: 1px solid var(--rule);
708 max-width: 84rem;
709 margin: 0 auto;
710 padding: 2.5rem var(--gutter) 3.5rem;
711 font-size: var(--s-1);
712 }
713
714 /* 세 덩이. 브랜드 열이 넓고 링크 열이 좁은 건 왼쪽이 읽는 것이고 오른쪽이
715 찾는 것이기 때문이다. */
716 /* 열 폭을 비율로 두면 넓은 화면에서 링크 열이 오른쪽 끝까지 밀려 서로 상관없는
717 덩이처럼 보인다. 왼쪽부터 채우고 남는 공간은 오른쪽에 둔다. */
718 .colophon-grid {
719 display: grid;
720 grid-template-columns: minmax(0, 20rem) repeat(2, minmax(0, 9rem));
721 justify-content: start;
722 gap: clamp(1.5rem, 5vw, 4rem);
723 }
724
725 .colophon-mark {
726 display: inline-flex;
727 align-items: center;
728 gap: 0.5rem;
729 font-family: var(--mono);
730 font-size: var(--s0);
731 font-weight: 600;
732 letter-spacing: -0.03em;
733 color: var(--ink-2);
734 text-decoration: none;
735 }
736 .colophon-mark:hover { color: var(--ink); text-decoration: none; }
737
738 .colophon-brand img {
739 width: 1.7rem;
740 height: 1.7rem;
741 filter: invert(1);
742 opacity: 0.6;
743 }
744 :root[data-theme="light"] .colophon-brand img { filter: none; }
745
746 .colophon-brand p {
747 margin: 0.6rem 0 0;
748 max-width: 24rem;
749 color: var(--ink-3);
750 line-height: 1.6;
751 }
752
753 /* 열 제목은 사이드바 갈래 제목과 같은 처리다. 라벨은 사이트 어디서나 같은
754 물건으로 보여야 한다. */
755 .colophon h2 {
756 margin: 0 0 0.7rem;
757 font-family: var(--mono);
758 font-size: 0.72rem;
759 font-weight: 500;
760 letter-spacing: 0.08em;
761 text-transform: uppercase;
762 color: var(--ink-3);
763 }
764
765 .colophon ul { list-style: none; margin: 0; padding: 0; }
766 .colophon li + li { margin-top: 0.4rem; }
767 .colophon a {
768 color: var(--ink-2);
769 text-decoration: none;
770 transition: color var(--tr);
771 }
772 .colophon a:hover { color: var(--accent); text-decoration: underline; }
773
774 /* ---------- Korean ----------
775 Three Latin devices do not survive contact with Hangul:
776
777 letter-spacing pushes syllable blocks apart until a word stops reading as one
778 word; text-transform does nothing at all; and a monospace family is the worst
779 of them, because Hangul falls back to a proportional font while the *space*
780 still comes from the mono face, so every gap between 어절 renders double
781 width and the heading looks broken.
782
783 Structure still reads through size, weight and colour, which need no help. */
784 html[lang="ko"] .title-block h1,
785 html[lang="ko"] .prose h2,
786 html[lang="ko"] .prose h3,
787 html[lang="ko"] .prose h4,
788 html[lang="ko"] .nav-group > summary,
789 html[lang="ko"] .colophon h2,
790 html[lang="ko"] .toc::before,
791 html[lang="ko"] .prose th,
792 html[lang="ko"] .crumbs,
793 html[lang="ko"] .search-crumb,
794 html[lang="ko"] .prose .markdown-alert-title {
795 font-family: var(--sans);
796 letter-spacing: -0.01em;
797 text-transform: none;
798 }
799 html[lang="ko"] .title-block h1,
800 html[lang="ko"] .prose h2,
801 html[lang="ko"] .prose h3,
802 html[lang="ko"] .prose h4 {
803 font-weight: 700;
804 }
805
806 /* ---------- pager ---------- */
807
808 .pager {
809 display: flex;
810 gap: 1rem;
811 margin-top: 4rem;
812 padding-top: 1.5rem;
813 border-top: 1px solid var(--rule);
814 }
815
816 .pager a {
817 flex: 1;
818 display: block;
819 padding: 0.85rem 1rem;
820 border: 1px solid var(--rule);
821 border-radius: 4px;
822 color: inherit;
823 text-decoration: none;
824 }
825 .pager a:hover { border-color: var(--accent); }
826
827 /* A single "next" sits on the right, where the reader is heading. */
828 .pager-next { text-align: right; }
829 .pager-next:only-child { margin-left: auto; flex: 0 1 auto; }
830
831 .pager span {
832 display: block;
833 font-family: var(--mono);
834 font-size: 0.7rem;
835 letter-spacing: 0.08em;
836 text-transform: uppercase;
837 color: var(--ink-3);
838 margin-bottom: 0.2rem;
839 }
840 .pager strong { font-weight: 600; }
841
842 html[lang="ko"] .pager span {
843 font-family: var(--sans);
844 letter-spacing: -0.01em;
845 text-transform: none;
846 }
847
848 @media (max-width: 40rem) {
849 .pager { flex-direction: column; }
850 .pager-next { text-align: left; }
851 }
852
853 /* ---------- search palette ----------
854 A <dialog>. Escape, the backdrop, the focus trap and returning focus to the
855 trigger are all the element's own behaviour; none of it is written here. */
856
857 .search-dialog {
858 border: none;
859 background: none;
860 padding: 0;
861 margin: 0 auto;
862 width: min(40rem, calc(100vw - 2rem));
863 max-width: none;
864 max-height: 100dvh;
865 margin-top: 12vh;
866 }
867 .search-dialog::backdrop { background: rgb(0 0 0 / 0.55); }
868
869 .search-panel {
870 background: var(--paper);
871 border: 1px solid var(--rule);
872 border-radius: 8px;
873 box-shadow: 0 1.5rem 3rem rgb(0 0 0 / 0.35);
874 overflow: hidden;
875 }
876
877 .search-panel input {
878 width: 100%;
879 font: var(--s1)/1.4 var(--sans);
880 color: var(--ink);
881 background: none;
882 border: none;
883 border-bottom: 1px solid var(--rule);
884 padding: 1rem 1.15rem;
885 }
886 .search-panel input:focus { outline: none; }
887 .search-panel input::placeholder { color: var(--ink-3); }
888 /* The UA's clear button for type=search is a heavy glyph that sits at a
889 different weight from everything else here. Escape already clears. */
890 .search-panel input::-webkit-search-cancel-button { display: none; }
891
892 .search-results {
893 list-style: none;
894 margin: 0;
895 padding: 0.35rem;
896 max-height: min(26rem, 55dvh);
897 overflow-y: auto;
898 }
899 .search-results:empty { display: none; }
900
901 .search-results a {
902 display: block;
903 padding: 0.6rem 0.8rem;
904 border-radius: 5px;
905 color: inherit;
906 text-decoration: none;
907 }
908 .search-results a[aria-selected] { background: var(--raised); }
909 .search-results a[aria-selected] .search-title { color: var(--accent); }
910
911 .search-crumb {
912 display: block;
913 font-family: var(--mono);
914 font-size: 0.7rem;
915 letter-spacing: 0.08em;
916 text-transform: uppercase;
917 color: var(--ink-3);
918 }
919 .search-title {
920 display: block;
921 font-weight: 600;
922 line-height: 1.35;
923 }
924 .search-snippet {
925 display: block;
926 font-size: var(--s-1);
927 line-height: 1.5;
928 color: var(--ink-2);
929 /* Two lines is enough to show why a page matched. More turns the list into
930 a wall and hides the results below it. */
931 display: -webkit-box;
932 -webkit-line-clamp: 2;
933 -webkit-box-orient: vertical;
934 overflow: hidden;
935 }
936 .search-results mark {
937 background: none;
938 color: var(--accent);
939 font-weight: 600;
940 }
941
942 .search-foot {
943 display: flex;
944 justify-content: space-between;
945 gap: 1rem;
946 border-top: 1px solid var(--rule);
947 padding: 0.5rem 1.15rem;
948 font-family: var(--mono);
949 font-size: 0.72rem;
950 color: var(--ink-3);
951 }
952
953 /* ---------- narrow screens ----------
954 여기 규칙이 없어서 420px에서 마스트헤드가 무너졌다. 브랜드가 한 글자씩 세로로
955 쪼개지고 GitHub도 마찬가지였다. 좁아지면 줄이는 게 아니라 **뺀다** — 갈래 내비는
956 사이드바 서랍이 같은 일을 하고, GitHub는 콜로폰에 이미 있다. */
957
958 @media (max-width: 60rem) {
959 .layout {
960 grid-template-columns: minmax(0, 1fr);
961 padding-top: 1.5rem;
962 }
963
964 /* 서랍이 된다. 열고 닫는 건 <details>가, 처음 닫는 건 ui.js가 한다. */
965 .sidebar {
966 position: static;
967 /* 넓은 화면에서 경계선을 채우려고 준 높이를 되돌린다. 안 그러면 서랍이
968 접혀 있어도 100vh를 차지해 본문이 화면 밖으로 밀린다. */
969 height: auto;
970 max-height: none;
971 overflow: visible;
972 border-right: none;
973 border-bottom: 1px solid var(--rule);
974 padding: 0 0 1rem;
975 margin-bottom: 1.5rem;
976 }
977 .sidebar > summary {
978 display: inline-flex;
979 align-items: center;
980 gap: 0.5rem;
981 height: var(--control-h);
982 padding: 0 0.6rem;
983 border: 1px solid var(--rule);
984 border-radius: var(--radius);
985 font-family: var(--mono);
986 font-size: var(--s-1);
987 color: var(--ink-2);
988 cursor: pointer;
989 list-style: none;
990 }
991 .sidebar > summary::-webkit-details-marker { display: none; }
992 .sidebar > summary::before {
993 content: "";
994 width: 0.35rem;
995 height: 0.35rem;
996 border-right: 1.5px solid currentColor;
997 border-bottom: 1.5px solid currentColor;
998 transform: rotate(-45deg);
999 }
1000 .sidebar[open] > summary::before { transform: rotate(45deg); }
1001 .sidebar[open] > nav { margin-top: 0.75rem; }
1002
1003 /* 목차는 사이드바 서랍과 같은 말을 한 번 더 하는 셈이라 뺀다. */
1004 .toc { display: none; }
1005
1006 .colophon-grid { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
1007
1008 /* 갈래 내비도 서랍에 있다. */
1009 .sections { display: none; }
1010
1011 /* GitHub는 콜로폰에 있다. 검색은 라벨만 남기고 단축키 힌트를 뺀다 —
1012 터치 화면에는 누를 키가 없다. */
1013 .chip-wide { display: none; }
1014 .search-trigger { min-width: 0; }
1015 .search-trigger kbd { display: none; }
1016 }
1017
1018 @media (max-width: 26rem) {
1019 /* 이보다 좁으면 워드마크까지 자리를 다툰다. 마크만 남긴다 — 로고가 곧 이름이다. */
1020 .brand span { display: none; }
1021 }
1022
1023 @media (prefers-reduced-motion: reduce) {
1024 html { scroll-behavior: auto; }
1025 *, *::before, *::after {
1026 animation-duration: 0.01ms !important;
1027 transition-duration: 0.01ms !important;
1028 }
1029 }