# grove — 슬라이드 만들기 지시서 (채팅 AI용)

> 이 파일은 **ChatGPT·Claude·Gemini 같은 채팅 AI**에게 통째로 주면, 이 디자인(`grove`)으로 수업 슬라이드를 만들어 주도록 쓴 지시서입니다.
> 파일을 읽고 쓸 수 있는 에이전트 AI(Claude Code 등)를 쓴다면 갤러리의 **받기 .zip**을 쓰세요.

## 사용법 (사람이 읽는 안내)

1. 채팅 AI 대화창에 **이 파일을 첨부**합니다. 첨부가 안 되면 파일 내용을 **전부 복사해 붙여 넣습니다**(끝까지 빠짐없이).
2. 이어서 만들고 싶은 수업을 말합니다(**"결과는 .html 파일로 만들어 줘"**도 같이 말하면 좋습니다). 예) "6학년 2학기 과학 1단원 3~4차시, 학습 목표는 ○○○, 00초등학교, 슬라이드로 만들어 줘(장 수와 구성은 내용에 맞게 정해 줘)."
3. AI가 먼저 **장별 구성안**(장마다 제목·내용·쓸 부품)을 보여 줍니다. 마음에 들면 "좋아, 만들어 줘", 고칠 곳이 있으면 "3장은 빼고 퀴즈를 하나 더 넣어 줘"처럼 말합니다. 구성 확인 없이 바로 받고 싶으면 2번에서 "구성 확인 없이 바로 만들어 줘"라고 하세요.
4. AI가 **슬라이드.html 파일**을 만들어 줍니다. 내려받아 더블클릭하면 브라우저에서 열립니다. 방향키(→ ←)와 스페이스로 넘깁니다. 만약 파일 대신 **코드만 글로 보여 주면** "코드 말고 .html 파일로 만들어서 내려받게 해 줘"라고 다시 요청하세요. 그래도 안 되면 코드를 모두 복사해 메모장에 붙여 넣고 `슬라이드.html`로 저장합니다(파일 형식은 '모든 파일', 인코딩은 UTF-8).
5. 고치고 싶으면 AI에게 "2장 제목을 ○○로 바꿔 줘"처럼 말하면 됩니다. 중간에 코드가 끊기면 "끊긴 곳부터 이어서 출력해 줘"라고 하세요.

서비스별 팁 (메뉴 이름은 서비스마다 바뀔 수 있습니다)
- 공통: 고를 수 있다면 **생각하는(추론) 모드**나 상위 모델을 쓰세요. 긴 코드를 끝까지 정확하게 쓰는 데 차이가 큽니다.
- ChatGPT: "캔버스로 만들어 줘" 또는 "다운로드할 수 있는 .html 파일로 만들어 줘"라고 요청합니다.
- Gemini: 입력창의 **Canvas**를 켜고 요청하면 화면에서 바로 보고 내려받을 수 있습니다.
- Claude: 결과가 **아티팩트**(오른쪽 창)로 열리면 거기서 내려받습니다.
- AI가 그림 파일(이미지)을 따로 만들어 주더라도 슬라이드에는 들어가지 않습니다. 슬라이드는 .html 파일입니다.
- **AI 화면 안의 미리보기에서는 색·모양·버튼이 안 나올 수 있습니다.** 이 디자인의 공통 모양과 동작은 슬라이드를 열 때 인터넷(`https://k-slide.web.app`)에서 불러오는데, 일부 서비스의 미리보기 창은 바깥 파일을 막기 때문입니다. **내려받아 브라우저(크롬·엣지 등)로 열면 정상**으로 보입니다.

알아 둘 점
- 이 디자인의 한글 글씨체는 제목 Grandiflora One(400) · 본문 Noto Sans KR Thin(100)입니다. 글씨체와 공통 모양·동작은 **인터넷에서 불러오므로 슬라이드를 열 때 인터넷이 필요**합니다(영문 글꼴도 마찬가지). 인터넷 없이 발표하려면 갤러리의 **받기 .zip**(모든 것이 파일 안에 들어 있음)을 쓰세요.
- AI가 긴 코드를 일부 생략하거나 어긋나게 쓸 수 있습니다. 열어서 한 번 확인하세요. 안 열리거나 깨지면 "코드가 생략되었어. 완성된 HTML 하나로 처음부터 끝까지 다시 출력해 줘"라고 요청합니다.
- 사진·학생 이름 같은 개인정보는 AI에게 넣지 마세요.

## 라이선스

폰트 라이선스 안내
==================

- Grandiflora One: Grandiflora One — KERIS 학교 안심폰트(https://copyright.keris.or.kr/wft/fntDwnld, 전체 OFL 안내)에서 받은 TTF (2026-10-07) / 파일 안 저작권: Copyright 2022 The Grandiflora Project Authors (https://github.com/JAMO-TYPEFACE/Grandiflora)
  파일: GrandifloraOne-Regular.ttf
- Noto Sans KR Thin: Noto Sans KR — KERIS 학교 안심폰트(https://copyright.keris.or.kr/wft/fntDwnld, 전체 OFL 안내)에서 받은 TTF (2026-10-07) / 파일 안 저작권: (c) 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'.
  파일: NotoSansKR-Thin.ttf

SIL Open Font License 1.1 요약
- 폰트를 자유롭게 사용·복제·수정·재배포할 수 있습니다(폰트 파일 단독 판매는 불가).
- 수정본(글자를 줄인 서브셋 포함)은 원래 폰트의 '예약 이름'을 쓰지 못할 수 있습니다.
- 재배포할 때는 저작권 표시와 라이선스 문구를 함께 넣어야 합니다.
- 전문: https://openfontlicense.org

학교안심 글꼴과 easy3D 폴더의 글꼴은 사용자가 OFL로 확인한 내용을 따랐습니다. 배포 전에 각 배포처에서 최신 라이선스를 다시 확인하세요.

design.md 라이선스 (MIT)
=======================
design.md는 frontend-slides(https://github.com/zarazhangrui/frontend-slides)의 템플릿을 바탕으로 한 것이며, 아래 MIT 라이선스를 따릅니다.

MIT License

Copyright (c) 2025 Zara Zhang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

---

# ▼▼▼ 여기부터 AI에게 주는 지시서 ▼▼▼

(이 줄 위의 '사용법'과 '라이선스'는 사람을 위한 안내입니다. AI는 아래 지시서를 따르세요.)

## 0. 당신(AI)의 일

당신은 슬라이드 제작 도우미입니다. 사용자가 알려 주는 수업 내용으로, 아래 **디자인 규칙(1)**, **부품 사전(3)**, **HTML 뼈대(4)**를 지켜 **브라우저에서 바로 열리는 슬라이드 HTML 파일 하나**를 만듭니다.
이 디자인의 공통 CSS(글씨체·색·부품 모양)와 동작 엔진은 사이트에 올려 둔 파일이라, 뼈대의 `<link>`·`<script src>` 두 줄이 불러옵니다. **당신이 쓰는 것은 슬라이드 HTML(`<section class="slide">`들과 내비)과, 꼭 필요할 때만 '추가 부품' CSS뿐입니다.** 공통 CSS의 내용은 참고용으로 5에 있습니다.

### 진행 순서
1. 사용자가 아직 수업 내용을 말하지 않았다면, 먼저 아래를 짧게 물어보세요: 학교급·학년·과목, 단원과 차시, 학습 목표, 학교 이름(선택), 원하는 장 수(말하지 않으면 내용 분량에 맞게 정함).
2. **장별 구성안을 먼저 보여 주고 확인을 받습니다**(아래 '구성은 내용이 정합니다'). 이때는 코드를 쓰지 마세요.
3. 확인을 받으면 HTML을 만들고, 내보내기 전에 아래 '내보내기 전 점검'을 하나씩 확인합니다.
- 내용은 사용자가 준 것만 쓰고, **숫자·사실·출처를 지어내지 마세요.** 모르면 묻거나 "(예시)"라고 표시하세요.
- **사용자가 주지 않은 사실(연도·기록·수치·등재·'세계 최고'·'가장 ○○하다' 같은 말)을 덧붙이고 싶으면, 구성안에 '추가 사실'로 따로 적어 확인을 받으세요.** 확인받지 않은 사실은 슬라이드에 넣지 않습니다. 널리 알려진 이야기라도 정확한 대상을 쓰세요(예: 유네스코 세계기록유산에 오른 것은 '한글'이 아니라 《훈민정음》 해례본이라는 책).

### 반드시 지킬 것
1. **디자인 규칙(1)을 어기지 마세요.** 색·글꼴·배치·장식·움직임 규칙과 '하지 말 것'을 그대로 따릅니다. 규칙에 없는 효과(임의의 애니메이션, 새 색, 새 장식)를 더하지 마세요.
2. **뼈대(4)의 공통 파일 두 줄을 그대로 두세요.** `<link rel="stylesheet" href="https://k-slide.web.app/kit/v1/grove.css">`(head 안)와 `<script src="https://k-slide.web.app/kit/v1/grove.js"></script>`(body 맨 끝)는 주소 한 글자도 바꾸거나 지우지 마세요. **공통 CSS(5)나 엔진을 HTML 안에 옮겨 쓰거나, 줄이거나, 비슷하게 새로 짜지 마세요**(이미 불러오므로 같은 규칙을 또 쓰면 서로 부딪혀 깨집니다). `<style>`에는 '추가 부품' 줄과 그 아래에 직접 정의한 클래스만 둡니다. 엔진이 쓰는 속성 이름(`data-steps`, `data-s`, `data-quiz`, `data-opt`, `data-act`, `data-blank` 등)은 5의 엔진 사용법대로 씁니다.
3. **클래스는 부품 사전(3)과 공통 CSS(5)에 있는 것만 쓰세요.** 없는 클래스 이름을 지어 쓰면 아무 모양도 적용되지 않아 배치가 무너집니다(창이 좁아지고 빈 공간이 생김). 새 부품이 꼭 필요하면 `<style>`의 '추가 부품' 주석 줄 **아래에** 그 클래스를 직접 정의하세요(디자인 규칙의 색·선·글꼴만 사용). 모양을 `style="…"`로 하나하나 지정하는 것보다 부품 클래스를 쓰는 쪽이 낫습니다.
   - **새 배치를 만들기 전에, 예시 장의 배치 클래스로 할 수 있는지 먼저 보세요.** 직접 만든 배치는 화면을 볼 수 없는 상태에서 쓰기 때문에 깨질 위험이 큽니다. 예시 배치로 안 될 때만 새로 정의합니다.
4. **슬라이드의 장 수·순서·내용은 사용자 수업에 맞게 새로 구성하세요.** 뼈대의 슬라이드는 '계절의 변화' 예시이므로 그 문구는 하나도 남기지 마세요. 장을 늘리거나 줄이거나 순서를 바꾸면 **내비 점 개수와 쪽 표시(예: 01 / 06)를 실제 장 수에 맞추세요.** 뼈대의 6번째 장은 이 디자인의 대표 장치를 눌러 보여 주는 안내 장이므로, 그 부품·움직임(예: data-demo 시연 버튼과 .play 상태)만 참고하고 디자인 안내 문구는 남기지 마세요. 아래 '구성은 내용이 정합니다'를 따르세요.
5. **글씨체는 바꾸지 마세요.** 한글 글씨체(제목 Grandiflora One(400) · 본문 Noto Sans KR Thin(100))는 공통 CSS가 불러옵니다. `@font-face`를 새로 쓰지 말고, 글꼴은 `var(--kt)`(제목)·`var(--kb)`(본문)·`var(--ka)`(포인트) 변수로만 지정하세요.
6. **글자 크기와 화면 채우기** (슬라이드는 1920×1080 기준으로 그려져 화면 크기에 맞게 통째로 줄어듭니다)
   - 읽어야 하는 글(본문·설명·표 내용·보기)은 **28px 이상**, 보조 글(캡션·주석)은 **22px 이상**으로 씁니다. 창 제목줄·쪽 번호·작은 라벨 같은 장식 글자는 디자인 규칙의 크기를 따릅니다.
   - 창·판·카드가 **화면의 대부분을 채우게** 하세요. 한쪽에 작게 몰리거나 큰 빈 공간이 남으면 안 됩니다. 내용이 적은 장은 글자와 그림을 키우거나 다른 장과 합칩니다.
   - **높이 예산**: 장 제목과 아래 내비를 빼면 본문에 쓸 수 있는 높이는 대략 **650~750px**입니다. 모든 내용이 1920×1080 안에 들어가야 하고, 넘친 부분은 잘려서 보이지 않습니다. 쓰기 전에 줄 수 × 줄 높이(글자 크기의 약 1.5배)로 높이를 어림해 보세요.
   - **카드 규칙**: 한 장에 카드(칸)를 4개 이상 놓으면 카드마다 글은 **3줄 이하**로 줄입니다. 글이 들어가는 칸은 **너비 400px 이상**으로 두고(그림+글을 나란히 놓을 때는 글 칸 기준), 한 줄에 몇 글자밖에 안 들어가게 좁히지 마세요. 카드를 두 줄로 쌓아야 하면 그 줄들의 높이 합이 높이 예산 안에 드는지 확인합니다.
   - 제목은 **두 줄 이내**로 씁니다. 표지 제목은 한 줄에 15자 안팎으로 끊어 줄을 바꾸세요. 글이 창 밖으로 넘치거나 잘리면 글자를 줄이지 말고 문장을 짧게 고칩니다.
   - 한글은 단어 중간에서 줄이 바뀌지 않게(`word-break:keep-all`) 합니다(공통 CSS에 이미 들어 있음).
7. 저작권이 있는 글·그림·사진을 가져오지 마세요. **사용자가 요청하지 않으면 그림 파일(이미지 생성)을 따로 만들지 마세요. 슬라이드에 필요한 그림은 HTML 안에서 SVG·CSS로 그립니다.**

### 구성은 내용이 정합니다
- 예시 덱(deck.html)은 이 디자인의 **부품 견본**입니다. 따라야 할 장 구성이 아닙니다. 예시의 장 순서·장 수·제목 짜임·문장 구조를 그대로 따라 하지 마세요.
- 사용자가 준 내용을 먼저 살펴 **필요한 장 수와 장별 역할을 직접 정하세요.** 내용이 적으면 3~4장, 많으면 8장 이상도 괜찮습니다.
- **코드를 쓰기 전에 장별 구성안(장마다 제목 · 전할 내용 한 줄 · 쓸 부품)을 먼저 보여 주고 확인을 받으세요.** 사용자가 고치라고 하면 고친 구성안을 다시 보여 주고, "좋아", "만들어 줘"라고 하면 그때 만듭니다. 사용자가 처음부터 "구성 확인 없이 바로 만들어 줘"라고 했으면 이 단계는 건너뜁니다.
- 아래 부품 중 **필요한 것만 골라 쓰세요.** 안 쓰는 부품이 있어도 되고(예: 퀴즈 없는 덱), 같은 부품을 여러 장에 쓰거나 한 장에 섞어 써도 됩니다.
  · 표지 · 단계 버튼이 있는 흐름(data-steps) · 큰 숫자와 통계 · 그래프 · 비교와 표 · 목록과 인용 · 퀴즈와 다시 풀기 · 빈칸 채우기 · 시연 버튼(data-demo)
- 장마다 전하는 메시지는 하나로 하고, 같은 모양의 장이 연달아 나오지 않게 하세요.
- 지켜야 하는 것은 디자인 규칙(design.md)과 동작 엔진입니다. 자유로운 것은 장의 수·순서·구성입니다.

### 출력 방식
- **결과물은 '.html 파일'로 주세요.** 파일 이름은 슬라이드.html, 형식은 UTF-8 HTML 문서 하나입니다. 파일을 만들거나 내려받게 해 주는 기능(아티팩트·캔버스·코드 실행 환경·파일 첨부 등)이 있으면 **반드시 그 기능으로 .html 파일을 만들어 내려받거나 열 수 있게** 하세요. 코드만 글로 보여 주고 끝내지 마세요.
- 파일을 만들 수 있는 기능이 **전혀 없을 때만**, 설명은 한두 줄로 줄이고 **완성된 HTML 전체를 ```html 코드 블록 하나**로 출력하세요(이때 사용자가 직접 .html로 저장합니다).
- **`...`, "(이하 동일)", "(생략)" 같은 생략 표시를 쓰지 마세요.** `<!DOCTYPE html>`부터 `</html>`까지 전부 씁니다.
- 너무 길어 끊길 것 같으면 먼저 쓰지 말고, 사용자에게 "장 수를 줄일까요?"라고 묻거나, 끊기면 사용자가 "이어서"라고 할 때 **끊긴 글자 바로 다음부터** 이어 쓰세요.
- **미리보기 창에서 색·모양·버튼이 안 보여도 고치지 마세요.** 공통 CSS·엔진은 인터넷 주소(`https://k-slide.web.app`)에서 불러오는데, 일부 미리보기 창은 바깥 파일을 막습니다. 내려받아 브라우저로 열면 정상입니다. 이때 공통 CSS나 엔진을 HTML 안에 직접 써 넣으면 안 됩니다. 사용자에게 "내려받아 브라우저로 열어 보세요"라고 안내하세요.
- 파일을 코드(파이썬 등)로 써서 만든다면, HTML 전체를 **원문 그대로의 문자열**로 쓰세요(예: 파이썬은 따옴표 세 개짜리 raw 문자열). 백슬래시(`\`)가 다른 문자로 바뀌어 깨지는 일을 막기 위해서입니다. 새로 쓰는 CSS·JS에서도 `\00a0` 같은 이스케이프 대신 실제 문자를 쓰세요.
- 파일로 줬다면 "슬라이드.html을 내려받아 더블클릭하세요(열 때 인터넷 필요)"라고, 코드 블록으로 줬다면 "코드를 모두 복사해 메모장에 붙여 넣고 슬라이드.html로 저장한 뒤 더블클릭하세요(열 때 인터넷 필요)"라고 한 줄로 안내하세요.

### 내보내기 전 점검 (하나라도 아니면 고친 뒤 내보내기)
1. HTML에 쓴 클래스가 모두 부품 사전(3)·공통 CSS(5)·'추가 부품' 칸에 정의되어 있는가?
2. 공통 파일 두 줄(`<link>`·`<script src>`)이 주소 그대로 있고, 공통 CSS·엔진을 HTML 안에 옮겨 쓰지 않았는가? `@font-face`를 새로 쓰지 않았는가?
3. 장 수 = 내비 점 개수 = 쪽 표시 숫자인가?
4. 모든 장의 내용이 1920×1080 안에 들어가는가(높이 예산, 카드 규칙)? 제목·글이 창 밖으로 넘치거나 잘리지 않는가(특히 표지 제목과 마지막 장)? 읽는 글이 28px 이상이고, 화면에 큰 빈 공간이 없는가?
5. '계절의 변화' 예시 문구나 디자인 안내 문구가 남아 있지 않은가?
6. 사용자가 주지 않았고 확인도 받지 않은 사실을 넣지 않았는가?
7. `...`·"(생략)" 같은 생략이 없고, `<!DOCTYPE html>`부터 `</html>`까지 다 있는가?
8. 사용자가 요청하지 않은 그림 파일을 따로 만들지 않았는가?

---

## 1. 디자인 규칙 (`grove` design.md)

````markdown
---
version: alpha
name: Grove
description: A quiet, editorial-serif presentation system in the register of a well-bound monograph or boutique brand book. Playfair Display at weight 400 (never bold) carries every headline, italicized in terracotta coral for accent emphasis as the signature move. Jost weight 300 carries every paragraph as the "good paper" body face. JetBrains Mono at weight 300 holds labels, kickers, and the thin chrome bars. The palette pairs a deep forest green canvas (#192b1b) with warm cream text (#d4cfbf) and a single terracotta coral accent (#c8524a). Generous negative space, hairline 1px borders, and a near-invisible serif watermark numeral give it the calm authority of a literary journal.

colors:
  bg: "#192b1b"
  bg-alt: "#1e3221"
  bg-light: "#e8e4d6"
  bg-light-alt: "#dedad0"
  fg: "#d4cfbf"
  fg-2: "rgba(212, 207, 191, 0.6)"
  fg-3: "rgba(212, 207, 191, 0.32)"
  fg-light: "#192b1b"
  fg-light-2: "rgba(25, 43, 27, 0.58)"
  fg-light-3: "rgba(25, 43, 27, 0.33)"
  accent: "#c8524a"
  border: "rgba(212, 207, 191, 0.12)"
  border-light: "rgba(25, 43, 27, 0.14)"
  watermark-dark: "rgba(212, 207, 191, 0.06)"
  watermark-light: "rgba(25, 43, 27, 0.06)"

color-aliases:
  fg-light: bg
  fg-light-canonical: "fg-light shares hex #192b1b with bg — the green is reused as both surface and primary text-on-light"

typography:
  display:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "10vw"
    fontWeight: 400
    lineHeight: 1
    letterSpacing: -0.01em
  h1:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "5.5vw"
    fontWeight: 400
    lineHeight: 1.1
  h1-statement:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "min(4.5vw, 7.5vh, 88px)"
    fontWeight: 400
    lineHeight: 1.15
  h2:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "3.2vw"
    fontWeight: 400
    lineHeight: 1.2
  h3:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "2vw"
    fontWeight: 400
    lineHeight: 1.3
  quote-text:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "3.2vw"
    fontWeight: 400
    lineHeight: 1.35
    letterSpacing: -0.01em
    fontStyle: italic
  quote-mark:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "8vw"
    fontWeight: 400
    lineHeight: 0.6
  stat-value:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "4.5vw"
    fontWeight: 400
    lineHeight: 1
    letterSpacing: -0.02em
  grove-num:
    fontFamily: "'Playfair Display', 'Noto Serif SC', Georgia, serif"
    fontSize: "18vw"
    fontWeight: 400
    lineHeight: 1
    letterSpacing: -0.03em
  lead:
    fontFamily: "'Jost', 'Noto Sans SC', system-ui, sans-serif"
    fontSize: "1.45vw"
    fontWeight: 300
    lineHeight: 1.65
  body:
    fontFamily: "'Jost', 'Noto Sans SC', system-ui, sans-serif"
    fontSize: "1.05vw"
    fontWeight: 300
    lineHeight: 1.75
  body-list-emph:
    fontFamily: "'Jost', 'Noto Sans SC', system-ui, sans-serif"
    fontSize: "max(1.4vw, 17px)"
    fontWeight: 300
    lineHeight: 1.6
  caption:
    fontFamily: "'Jost', 'Noto Sans SC', system-ui, sans-serif"
    fontSize: "0.82vw"
    fontWeight: 300
    lineHeight: 1.55
  label:
    fontFamily: "'JetBrains Mono', monospace"
    fontSize: "0.7vw"
    fontWeight: 300
    letterSpacing: 0.12em
  kicker:
    fontFamily: "'JetBrains Mono', monospace"
    fontSize: "0.7vw"
    fontWeight: 300
    letterSpacing: 0.14em
    textTransform: uppercase
  chapter-num:
    fontFamily: "'JetBrains Mono', monospace"
    fontSize: "0.7vw"
    fontWeight: 300
    letterSpacing: 0.2em
    textTransform: uppercase
  stat-label:
    fontFamily: "'JetBrains Mono', monospace"
    fontSize: "0.7vw"
    fontWeight: 300
    letterSpacing: 0.12em
    textTransform: uppercase

spacing:
  pad-x: "8vw"
  pad-y: "6.5vh"
  pad-quote-x: "calc(8vw * 1.1)"
  pad-quote-y: "calc(6.5vh * 1.2)"
  gap-lg: "4.5vh"
  gap-md: "2.8vh"
  gap-sm: "1.4vh"
  rule-short: "36px"

motion:
  ease-slide: "cubic-bezier(0.77, 0, 0.175, 1)"
  dur-slide: "0.9s"
  ease-enter: "cubic-bezier(0.16, 1, 0.3, 1)"
  dur-enter: "0.7s"
  stagger-delays: "0 / 0.08s / 0.18s / 0.3s / 0.44s / 0.6s / 0.78s"

canvas:
  width: 100vw
  height: 100vh

components:
  slide-chrome:
    description: "Thin top bar separating slide content from the page edge. A flex space-between row holding mono label text on each side, separated below by a 1px hairline in {colors.border} (or {colors.border-light} on light slides). Pads with {spacing.gap-sm} above the rule, then {spacing.gap-md} below before content starts."
    border: "1px solid {colors.border}"
    typography: "{typography.label}"
  slide-foot:
    description: "Matching bottom bar. Flex space-between row holding section name and 'NN / TT' counter, with 1px hairline border-top. The chrome and foot together frame every content slide; cover, chapter, quote, and end slides hide both."
    border: "1px solid {colors.border}"
    typography: "{typography.label}"
  grove-num:
    description: "Massive serif digit placed in the background at very low opacity (6%) as compositional texture. Sits absolutely at right: {spacing.pad-x}, bottom: -0.15em, with pointer-events disabled. The system's signature wallpaper element on chapter and section moments."
    fontSize: "18vw"
    color: "{colors.watermark-dark} on dark / {colors.watermark-light} on light"
  grove-stat:
    description: "Stat card: large Playfair value in {colors.accent}, mono uppercase label beneath, 1px border-bottom hairline. No background fill — the card is defined by the rule and the type ratio alone."
    valueColor: "{colors.accent}"
    valueSize: "4.5vw"
    labelTypography: "{typography.stat-label}"
    borderBottom: "1px solid {colors.border}"
  bullet-list:
    description: "Two-column grid list (2em / 1fr) where the bullet is a coral em-dash glyph rendered in JetBrains Mono, not a CSS bullet. The em-dash is the system's bullet language."
    bulletGlyph: "—"
    bulletColor: "{colors.accent}"
    bulletFont: "'JetBrains Mono', monospace"
  rule-coral:
    width: "{spacing.rule-short}"
    height: "1px"
    background: "{colors.accent}"
    description: "A 36px-wide 1px-tall terracotta coral rule. The compositional beat between a kicker and the headline that follows."
  rule-full:
    width: "100%"
    height: "1px"
    background: "{colors.border} on dark / {colors.border-light} on light"
    description: "Full-width hairline divider. Used between stacked sections inside a slide."
  kicker:
    typography: "{typography.kicker}"
    color: "{colors.accent}"
    description: "Mono uppercase eyebrow in coral, placed above an h1/h2 headline."
  chapter-num:
    typography: "{typography.chapter-num}"
    color: "{colors.accent}"
    marginBottom: "{spacing.gap-md}"
  quote-mark:
    typography: "{typography.quote-mark}"
    color: "{colors.accent}"
    description: "Massive Playfair opening-quote glyph in coral, placed above the italic quote body."
  img-placeholder:
    background: "{colors.bg-alt} on dark / {colors.border-light} on light"
    color: "{colors.fg-3} on dark / {colors.fg-light-3} on light"
    typography: "{typography.label}"
    minHeight: "30vh"
    description: "Image-region marker — solid darker-than-background fill with a mono caption stating the placeholder text. Use this in place of <img> until a real image is available."
  nav-dots:
    position: "fixed"
    placement: "bottom: 24px, horizontally centered"
    dotSize: "5px"
    dotBackground: "rgba(255, 255, 255, 0.22)"
    activeBackground: "rgba(255, 255, 255, 0.8)"
    activeTransform: "scale(1.4)"
    description: "Small white dots at the bottom of the viewport indicating slide position. The fixed counter (#slide-counter) is intentionally disabled — the slide-foot already shows NN / TT."
---

## Frontend Slides Fixed-Stage Policy

When this design system is used by the `frontend-slides` skill, generate the final deck as a **fixed 1920×1080 stage** that scales uniformly to the browser viewport. The deck should preserve a 16:9 slide canvas on every screen, including phones; it may letterbox or pillarbox, but it should not reflow slide content for mobile.

This policy has higher priority than any source-template responsive behavior described later in this file. If a later section says the original template is viewport-fluid, treat that as source history only, not as the target generation model for `frontend-slides`.

This policy applies even if the source template was originally implemented with viewport-fluid CSS such as `100vw`, `100vh`, `vw`, `vh`, or `clamp()`. Treat those values as design proportions to translate into 1920×1080 stage coordinates, not as live responsive rules in the generated deck.

Use `deck-stage.js` or an equivalent inline stage scaler for final output: render each slide at 1920×1080, scale the whole stage with one transform, and verify rendered screenshots for both text overflow and panel overlap.


## Overview

Grove is a **quiet, editorial-serif presentation system** in the register of a literary monograph or boutique brand book. The foundational premise is restraint: every slide carries one focused content moment surrounded by deep negative space, anchored by thin 1px chrome bars at top and bottom and supported by a small library of compositional beats (coral kicker, 36px coral rule, italic-coral accent, em-dash bullet, near-invisible watermark numeral).

The typographic stack runs four faces, each in a single specific weight:

- **Playfair Display at weight 400** carries every headline, every quote, every stat figure, and every watermark numeral. **Bold serif is not permitted** — the no-bold rule is the system's most important typographic commitment. Italicized Playfair in `{colors.accent}` coral is the signature accent move: `<em>` inside any headline switches to italic coral.
- **Jost at weight 300** carries every paragraph and bullet body. The light weight is the "good paper" feel — it sits back and lets the serif lead.
- **JetBrains Mono at weight 300** carries every label, kicker, footline, slide counter, and stat caption. Always uppercase, always with at least 0.12em letter-spacing.
- **Noto Serif SC / Noto Sans SC at weight 300–500** are loaded as Chinese fallbacks for every role. The deck is built bilingually-aware — Chinese characters render through the Noto cuts when present in the content.

The palette is a tight two-surface system with one accent. Dark slides use `{colors.bg}` (a deep forest green) with `{colors.fg}` (warm cream — never pure white) for body text. Light slides flip to `{colors.bg-light}` (warm parchment) with `{colors.fg-light}` (the same forest green now used as text). The single accent is **terracotta coral** (`{colors.accent}` — #c8524a), used only in small, deliberate places: the italic emphasis inside headlines, the 36px rule under a kicker, the em-dash bullet, the stat figure, the chapter ordinal, and the opening quote mark. Coral is the system's accent voice; using it as a surface fill or for body paragraphs shatters the editorial discipline.

Depth is **flat and air-based**. There are no drop shadows, no gradients, no blur, no glow. The only "depth" devices are 1px hairline borders (the chrome bars, the stat-card divider, the compare-panel separator) and the watermark numeral at 6% opacity. The whole system reads as printed ink on linen paper, not as a digital surface.

The system carries a built-in **motion vocabulary**: slide-to-slide transitions use a sharp `cubic-bezier(0.77, 0, 0.175, 1)` pan at 0.9s; element entrances stagger via `[data-anim]` and `[data-delay]` attributes (fade-up / fade-in / reveal-right / reveal-left / scale-in) with a spring-y `cubic-bezier(0.16, 1, 0.3, 1)` curve at 0.7s and delays of 0 / 0.08 / 0.18 / 0.3 / 0.44 / 0.6 / 0.78s. Animation is part of the system identity; new slides should use the animation attribute stack rather than authoring custom transitions.

**Density philosophy: sparse and breathing.** Grove reads as elegant when slides are quiet — one headline, one supporting paragraph, three stats in a row, two columns of text-and-image. The 8vw horizontal and 6.5vh vertical padding is generous by design. A slide that crowds 6 elements into the canvas breaks the system; a slide with one headline, one lede, and one accent rule reads as authoritative. Reach for fewer, larger elements. The system rewards silence around the type, not density of content.

**Key Characteristics:**
- Playfair Display at weight 400 — never bold — for every serif moment. Italic in `{colors.accent}` is the headline accent.
- Jost weight 300 for every body paragraph. Lighter weight is the system's "good paper" voice.
- JetBrains Mono weight 300 uppercase with 0.12em–0.2em letter-spacing for every label, kicker, footline, counter, and caption.
- Dark slides on `{colors.bg}` deep forest green with `{colors.fg}` warm cream text. Light slides on `{colors.bg-light}` parchment with `{colors.fg-light}` green text.
- One accent color: `{colors.accent}` terracotta coral. Used only for italic headline emphasis, the 36px coral rule, em-dash bullets, stat figures, chapter ordinals, and the quote mark.
- 1px hairline borders only (`{colors.border}` on dark, `{colors.border-light}` on light). No thick borders, no shadows, no gradients.
- Em-dash glyph rendered in JetBrains Mono coral is the system's universal bullet.
- A massive serif watermark numeral (`{components.grove-num}` at 18vw, 6% opacity) sits in the bottom-right corner of chapter and section slides as compositional texture.
- A staggered fade / reveal animation system (`[data-anim]` + `[data-delay]`) is built-in; new content should use it rather than appearing without entrance.

## Colors

### Palette
- **BG / Deep Forest** (`{colors.bg}` — #192b1b): The dominant dark canvas. A deep, considered forest green — grounded, editorial, the Grove base. The default slide background.
- **BG Alt** (`{colors.bg-alt}` — #1e3221): A slightly lighter forest green for secondary dark surfaces. Used as the `{components.img-placeholder}` fill on dark slides, where it reads as a tonal step up from the canvas without leaving the green family.
- **BG Light / Parchment** (`{colors.bg-light}` — #e8e4d6): The warm parchment surface used on light slides. Reads as good-quality paper stock, not as digital white.
- **BG Light Alt** (`{colors.bg-light-alt}` — #dedad0): A slightly cooler parchment for secondary light surfaces. Available for tonal separation between adjacent light regions.
- **FG / Warm Cream** (`{colors.fg}` — #d4cfbf): Primary text color on dark surfaces. Warm cream — never pure white. The off-white temperature is essential to the printed-ink feel.
- **FG-2** (`{colors.fg-2}` — rgba(212,207,191,0.6)): Secondary / muted text on dark. Used for ledes, captions, and supporting copy that needs to recede from primary.
- **FG-3** (`{colors.fg-3}` — rgba(212,207,191,0.32)): Tertiary / hint text on dark. Near-invisible — used for image-placeholder captions and the faintest metadata.
- **FG Light** (`{colors.fg-light}` — #192b1b): Primary text on light surfaces. **Shares its hex with `{colors.bg}`** — the deep forest green is used both as the dark canvas and as the dark text-on-light color. This is the system's primary surface-text inversion device.
- **FG Light 2** (`{colors.fg-light-2}` — rgba(25,43,27,0.58)): Secondary muted text on light.
- **FG Light 3** (`{colors.fg-light-3}` — rgba(25,43,27,0.33)): Tertiary text on light.
- **Accent / Terracotta Coral** (`{colors.accent}` — #c8524a): The single warm note. Used sparingly: italic emphasis inside headlines, the 36px coral rule under a kicker, the em-dash bullet, the stat figure, the chapter ordinal, the opening quote mark, and nav-dot active state. Never used as a surface fill, never used for body paragraphs.
- **Border** (`{colors.border}` — rgba(212,207,191,0.12)): Hairline divider on dark slides. 12% cream — visible as a structural line but never loud.
- **Border Light** (`{colors.border-light}` — rgba(25,43,27,0.14)): Hairline divider on light slides. 14% forest green.

### Defaults
- **Default slide background**: `{colors.bg}` (deep forest green) for the deck's dominant tonal register. Reach for `{colors.bg-light}` (parchment) when the content wants a literal "page" feel — typically for quote slides, lighter chapters, or contrast.
- **Default primary text color on dark surface**: `{colors.fg}` (warm cream).
- **Default primary text color on light surface**: `{colors.fg-light}` (deep forest green — same hex as the dark surface).
- **Default secondary / muted text**: `{colors.fg-2}` on dark, `{colors.fg-light-2}` on light. Use the `.muted` utility class.
- **Default headline color**: matches the surface's primary text color. Headlines never appear in coral except for the italic `<em>` accent inside them.
- **Default body color**: matches the surface's primary text color.
- **Default border color**: `{colors.border}` on dark, `{colors.border-light}` on light — both at 1px solid hairline weight.
- **Default kicker / chapter-num color**: `{colors.accent}` (terracotta coral). The mono uppercase kicker is the only chrome element that takes the accent color.
- **Default rule color** for the short 36px compositional rule under a kicker: `{colors.accent}`. The full-width section divider rule uses `{colors.border}` instead.

The palette is intentionally minimal. The dark forest / light parchment / terracotta coral triad is the entire color vocabulary. Introducing a fourth color (a navy, a yellow, a second accent) breaks the system's editorial restraint.

## Typography

### Font Family
The system loads four families from Google Fonts:

- **Playfair Display** at weights 400 and 500 (italic and roman). **Only weight 400 is used in published slides.** Weight 500 is loaded but reserved; bold serif (weight 700) is explicitly not permitted by the system rules.
- **Jost** at weights 200, 300, 400, 500. **Only weight 300 is used.** The light weight is the system's body voice.
- **JetBrains Mono** at weights 300 and 400. **Only weight 300 is used.** The light mono is the chrome voice.
- **Noto Serif SC / Noto Sans SC** at weights 300–500, loaded as the Chinese fallback chain for every role.

The four-family stack is strict by role: Playfair for every serif moment (display, headlines, quotes, stat figures, watermark), Jost for every body paragraph and bullet, JetBrains Mono for every label / kicker / footline / counter / caption.

### Display, Body, and Chrome Scale

| Token | Size | Family | Weight | Use |
|---|---|---|---|---|
| `{typography.grove-num}` | 18vw | Playfair | 400 | Watermark ordinal numeral, 6% opacity, absolute bottom-right |
| `{typography.display}` | 10vw | Playfair | 400 | Cover hero title |
| `{typography.quote-mark}` | 8vw | Playfair | 400 | Opening quotation glyph |
| `{typography.h1}` | 5.5vw | Playfair | 400 | Chapter title, statement headline |
| `{typography.stat-value}` | 4.5vw | Playfair | 400 / coral | Stat-card numeric value |
| `{typography.h2}` | 3.2vw | Playfair | 400 | Routine slide headline |
| `{typography.quote-text}` | 3.2vw | Playfair | 400 italic | Quote body |
| `{typography.h3}` | 2vw | Playfair | 400 | Sub-headline, compare-panel title |
| `{typography.lead}` | 1.45vw | Jost | 300 | Lead paragraph |
| `{typography.body-list-emph}` | max(1.4vw, 17px) | Jost | 300 | List-slide body / bullets — emphasized for legibility |
| `{typography.body}` | 1.05vw | Jost | 300 | Standard body paragraph |
| `{typography.caption}` | 0.82vw | Jost | 300 | Captions, footnotes, chart sources |
| `{typography.label}` | 0.7vw | JetBrains Mono | 300 / 0.12em | Chrome label, mono metadata |
| `{typography.kicker}` | 0.7vw | JetBrains Mono | 300 / 0.14em / UPPER | Eyebrow above a headline |
| `{typography.chapter-num}` | 0.7vw | JetBrains Mono | 300 / 0.2em / UPPER | Chapter ordinal label |
| `{typography.stat-label}` | 0.7vw | JetBrains Mono | 300 / 0.12em / UPPER | Mono label beneath a stat figure |

### Defaults
- **Default size for a routine slide headline**: `{typography.h2}` (3.2vw).
- **Default size for a chapter or statement headline**: `{typography.h1}` (5.5vw). For statement-class moments specifically, use `{typography.h1-statement}` which caps at `min(4.5vw, 7.5vh, 88px)` to prevent overflow on short viewports.
- **Default size for a cover hero title**: `{typography.display}` (10vw).
- **Default size for a body paragraph**: `{typography.body}` (1.05vw) in Jost weight 300.
- **Default size for a lede / introductory paragraph**: `{typography.lead}` (1.45vw).
- **Default size for a kicker / eyebrow**: `{typography.kicker}` (0.7vw) in JetBrains Mono uppercase 0.14em with `{colors.accent}` color.
- **Default size for a stat-card figure**: `{typography.stat-value}` (4.5vw) in Playfair coral.
- **Default weight for every serif element**: 400.
- **Default weight for every body / Jost element**: 300.
- **Default weight for every mono / JetBrains element**: 300.

When the body text on a list-style slide reads too small at 1.05vw on typical laptop viewports, the system bumps it to `{typography.body-list-emph}` (max of 1.4vw or 17px). Reach for the emphasized size whenever a slide's primary content moment is a body paragraph or bulleted list — the default 1.05vw is calibrated for projector viewing, not laptop reading.

### Signature Treatments
These treatments are **non-optional whenever the corresponding element type is used**:

- **Every Playfair element runs at weight 400.** Bold serif (weight 700) is not used anywhere in the system. This is the system's most important typographic rule.
- **An `<em>` tag inside any Playfair headline (h1, h2, h3, or quote) renders as italic in `{colors.accent}` coral.** This is the signature Grove accent — the way you add emphasis to a headline is to italicize a word in coral. The em-style switch is automatic via CSS.
- **The opening quote mark on a quote slide is rendered as a massive Playfair glyph at 8vw in `{colors.accent}` coral.** A quote without the oversized coral quote-mark is broken.
- **The quote body is always italic.** Roman-style quote text is not in the system.
- **Every kicker / chapter-num / footline / label / caption is JetBrains Mono uppercase with at least 0.12em letter-spacing.** Mono in sentence case or without tracking reads as code, not chrome.
- **Bullet glyphs are em-dashes (`—`) rendered in JetBrains Mono `{colors.accent}` coral.** Never bullets (`•`), never hyphens (`-`), never asterisks. The em-dash is the system's bullet language.
- **Every chrome bar (top and bottom) carries a 1px solid `{colors.border}` rule (or `{colors.border-light}` on light slides).** The hairline rule is what makes the chrome read as a "frame around the page" rather than a banner.

### Typography Principles
The Playfair-400 / Jost-300 / JetBrains-Mono-300 weight commitment is the system's voice. Switching any of those weights breaks the editorial register. There is no weight ladder — each face has exactly one weight.

Italics are reserved for two specific roles: the coral accent inside headlines (via `<em>`) and the quote body. Italic body paragraphs are not used. Underline is not used. Emphasis in body text is communicated by the `.accent` color utility (changes color to coral), not by italic or bold.

## Layout

### Canvas System
The system targets a fluid viewport — each `.slide` is `100vw × 100vh` with `padding: {spacing.pad-y} {spacing.pad-x}` (6.5vh / 8vw). Slides sit side-by-side in a horizontal `#deck` flex strip and translate via `transform: translateX(...)` with the `{motion.dur-slide}` / `{motion.ease-slide}` transition. Only one slide is `is-active` at a time; non-active slides hold their content invisible (`[data-anim]` is `opacity: 0`) until the active class triggers the entrance animations.

### Padding Scale
| Token | Value | Use |
|---|---|---|
| `{spacing.pad-x}` | 8vw | Horizontal slide padding |
| `{spacing.pad-y}` | 6.5vh | Vertical slide padding |
| `{spacing.pad-quote-x}` | 8.8vw | Slightly wider horizontal padding on quote slides |
| `{spacing.pad-quote-y}` | 7.8vh | Slightly taller vertical padding on quote slides |
| `{spacing.gap-lg}` | 4.5vh | Between major content sections |
| `{spacing.gap-md}` | 2.8vh | Between related elements |
| `{spacing.gap-sm}` | 1.4vh | Between tightly coupled elements |

### Chrome Anatomy
Every content slide carries a **slide-chrome** at the top — a thin flex space-between row holding two mono labels separated below by a 1px hairline border — and a **slide-foot** at the bottom — a matching row with section name + "NN / TT" counter, separated above by a matching hairline.

The chrome and foot are hidden on cover, chapter, quote, and end slides — those are the deck's chromeless emotional moments. Every other slide carries the frame.

### Border-Radius
**Zero structural radius.** No rounded cards, no rounded buttons, no rounded image placeholders. The only round shapes in the system are the 5px nav-dots (50% radius — fully circular) at the bottom of the viewport.

## Depth and Elevation

### Flat, No Shadows
The system uses **zero box-shadow, zero text-shadow, zero blur, zero gradient**. Depth is communicated entirely through:

1. **1px hairline borders** — the chrome bars at top and bottom, the stat-card divider, the compare-panel separator, the section-divider rule.
2. **Opacity layers in text color** — primary (`{colors.fg}`), muted (`{colors.fg-2}`), hint (`{colors.fg-3}`). The three-step opacity ladder is the system's text-elevation device.
3. **The watermark numeral** at 6% opacity — a massive Playfair digit sitting in the bottom-right corner of chapter and section slides as compositional texture, not as UI.

The absence of shadow is itself the elevation language. Adding `box-shadow: 0 4px 12px rgba(0,0,0,0.1)` shatters the printed-ink feel.

### Watermark Numeral
The `{components.grove-num}` element is a Playfair digit at 18vw / 6% opacity, positioned absolutely at `right: {spacing.pad-x}, bottom: -0.15em`. It reads as a faint background texture, never as content. Use it on chapter / section / statement slides where the empty bottom-right would otherwise feel un-anchored. It is part of the depth language even though it appears as a decorative element.

## Shapes and Treatment

### Border Weight and Style
- **1px solid `{colors.border}`** — the universal hairline on dark slides. Chrome bar borders, stat-card bottoms, compare-panel separators, full-width section dividers.
- **1px solid `{colors.border-light}`** — the matching hairline on light slides.
- **1px solid `{colors.accent}`** — the 36px-wide coral rule (`{components.rule-coral}`) used as the compositional beat between a kicker and the headline that follows.

Borders are never thicker than 1px. Never dashed, never dotted. The 1px hairline is the system's structural rhythm.

### Decorative Element Types

**Slide Chrome Bar** (`{components.slide-chrome}`) — Thin top bar with mono labels on each side, separated below by a 1px hairline. The system's universal "you are reading a page" frame.

**Slide Foot Bar** (`{components.slide-foot}`) — Matching bottom bar with section name + counter, separated above by 1px hairline.

**Coral Rule** (`{components.rule-coral}`) — A 36px-wide 1px terracotta coral horizontal rule. The compositional beat that sits between a kicker (above) and a headline (below). Use it once per slide where a kicker leads into a primary headline — do not stack multiple coral rules.

**Full Rule** (`{components.rule-full}`) — Full-width 1px hairline divider in `{colors.border}` / `{colors.border-light}`. Used as a section break inside a slide body.

**Kicker** (`{components.kicker}`) — Mono uppercase eyebrow in coral, placed above an h1 or h2 headline. Often paired with a coral rule below it.

**Chapter Number** — A mono uppercase ordinal in coral (`{typography.chapter-num}`) above the chapter title. Sets the chapter's identity at chapter-opener slides.

**Em-Dash Bullet** (`{components.bullet-list}`) — A two-column grid list (2em / 1fr) where the bullet is a coral em-dash glyph rendered in JetBrains Mono. Never use a true bullet (`•`); always the em-dash.

**Grove Stat** (`{components.grove-stat}`) — A vertical stack: large Playfair coral value (4.5vw) on top, mono uppercase label beneath, 1px border-bottom hairline. No background fill — the card is defined by the rule + the type, not by a containing box.

**Quote Mark** (`{components.quote-mark}`) — Massive Playfair opening-quote glyph at 8vw in coral, placed above the italic quote body. Always present on a quote slide.

**Watermark Numeral** (`{components.grove-num}`) — Playfair digit at 18vw / 6% opacity in the bottom-right corner. Compositional texture only, never UI.

**Image Placeholder** (`{components.img-placeholder}`) — A solid `{colors.bg-alt}` (on dark) or `{colors.border-light}` (on light) fill marking where a real `<img>` will go. Holds a mono caption like "[ image ]" in `{colors.fg-3}`.

**Nav Dots** (`{components.nav-dots}`) — Fixed 5px circular dots at the bottom of the viewport indicating slide position. Inactive: 22% white. Active: 80% white at 1.4× scale.

## Do's and Don'ts

### Do
- Run every serif moment in Playfair Display weight 400. Never bold.
- Use `<em>` inside any Playfair headline to switch a word to italic terracotta coral — this is the system's signature accent move.
- Run every paragraph in Jost weight 300. The lighter weight is the system's body voice.
- Run every label, kicker, chapter-num, footline, counter, and stat-caption in JetBrains Mono weight 300, uppercase, with at least 0.12em letter-spacing.
- Default slide background to `{colors.bg}` (deep forest green). Use `{colors.bg-light}` (parchment) on quote slides and any moment that wants a literal "page" feel.
- Pair the kicker with the 36px coral rule as a single compositional unit above the slide's primary headline.
- Use the em-dash glyph (`—`) in coral as the universal bullet. Never use round bullets or hyphens.
- Apply the `{components.grove-num}` watermark numeral to chapter and section-opener slides — the bottom-right corner needs that anchor.
- Use the built-in `[data-anim]` + `[data-delay]` animation stack (fade-up / fade-in / reveal-right / reveal-left / scale-in, with delays 0–6) for every new content element. Animation is part of the system identity, not optional polish.
- Keep slides spacious — one headline + one supporting paragraph + one accent rule is the rhythm. Reach for fewer elements at larger sizes.

### Don't
- Don't use bold serif. Playfair at weight 700 is not in this system. Bold serif breaks the editorial voice.
- Don't introduce a fourth typeface beyond Playfair, Jost, JetBrains Mono, and Noto SC. The four-family stack is the entire typographic identity.
- Don't use coral as a surface fill or for body paragraphs. Coral is the accent voice: italic emphasis, kicker, rule, em-dash bullet, stat figure, quote mark, chapter ordinal. That's the entire coral vocabulary.
- Don't use box-shadow, gradient, blur, or any rgba shadow. The system is flat.
- Don't use thick borders (2px+). The 1px hairline is the system's structural rhythm.
- Don't round any corners. The only round shape is the 5px nav-dot.
- Don't use round bullets, hyphens, or asterisks for list items. The em-dash in coral is the only bullet glyph.
- Don't put a kicker without a coral rule below it. The kicker → rule → headline triad is one unit.
- Don't render mono text in sentence case or without letter-spacing. Mono is always uppercase chrome with 0.12em+ tracking.
- Don't omit the slide-chrome and slide-foot on content slides. The thin frame at top and bottom is what makes the slide read as a page.
- Don't crowd the canvas. Grove rewards silence around the type. If a slide feels full, remove a supporting element rather than shrinking type.

## Responsive Behavior

The system uses **vw / vh units throughout** — every size, padding, gap, and rule is proportional to the viewport. The same composition renders correctly on a 1280×720 laptop, a 1920×1080 monitor, and a 2560×1440 display without breakpoints.

### Scaling Behavior
- Display headline scales linearly with viewport width (10vw → 192px at 1920, 128px at 1280).
- Body text scales similarly (1.05vw → 20.16px at 1920, 13.44px at 1280) — except on list-slide body, where the `max(1.4vw, 17px)` floor prevents the text from going unreadable on smaller viewports.
- Padding, gaps, and grove-num all scale proportionally with vw/vh.
- The 1px hairline borders are fixed-pixel and do not scale, which means at larger viewports they appear proportionally finer. This is by design.

### Presenter Behavior
- Slides advance via right/down arrow / space / page-down (handled by the deck JavaScript).
- Slides reverse via left/up arrow / page-up.
- The horizontal `#deck` strip translates via `transform: translateX(-N * 100vw)` between slides, with the `{motion.dur-slide}` sharp deceleration curve at 0.9s.
- Each slide carries an `is-active` class that triggers the staggered entrance animations on its `[data-anim]` elements.
- Bottom nav-dots reflect current slide position; clicking a dot jumps to that slide.

### Animation
The `[data-anim]` + `[data-delay]` system is built-in:
- `fade-up`: opacity 0 → 1, translateY 28px → 0
- `fade-in`: opacity 0 → 1
- `reveal-right`: clip-path inset(0 100% 0 0) → inset(0 0 0 0)
- `reveal-left`: clip-path inset(0 0 0 100%) → inset(0 0 0 0)
- `scale-in`: opacity 0 → 1, scale 0.94 → 1

All entrance animations use `{motion.ease-enter}` at `{motion.dur-enter}` (0.7s). Stagger delays: 0 / 0.08s / 0.18s / 0.3s / 0.44s / 0.6s / 0.78s.

### Print / Export
The system has no `@media print` rule. Print export will inherit the horizontal-strip layout and is unlikely to paginate cleanly. Treat Grove as a screen-first system; PDF export requires a dedicated print stylesheet.

## CJK & International Content

### Recommended Chinese Pairing

| Role | Chinese Face | Weight | Why |
|---|---|---|---|
| Display / h1 / h2 / h3 (Playfair roles, 2–10vw) | 霞鹜文楷 LXGW WenKai | 400 | Literary, hand-set warmth that mirrors Playfair Display at weight 400 — never bold, exactly the Grove rule |
| Quote text / mark (8vw mark, 3.2vw text) | 霞鹜文楷 LXGW WenKai | 400 | The same literary warmth carries the italic-quote moment; Chinese has no italic so the face does the expressive work |
| Stat figure (4.5vw, coral) | 霞鹜文楷 LXGW WenKai | 400 | Keeps stat figures in the Playfair register; the coral color carries the accent |
| Watermark numeral (18vw) | 霞鹜文楷 LXGW WenKai | 400 | The 6%-opacity watermark works at this scale with LXGW WenKai's open letterforms |
| Body / lede (Jost roles, 1.05–1.45vw) | 思源宋体 Noto Serif SC | 300–400 | Mincho body voice — calm, literary, sits back like Jost weight 300 |
| Label / kicker / chapter-num (JetBrains Mono roles) | 思源等宽 Noto Sans Mono CJK SC | 300–400 | Preserves the typewriter-chrome quality for kickers and footlines |

### Mixed-Content Strategy

Use **Strategy C** — keep Playfair Display as the Latin serif and let CJK glyphs fall through to LXGW WenKai. Playfair Display weight 400 (never bold) is the system's most important typographic commitment; replacing it with a CJK serif wholesale would break the monograph / boutique-brand-book register that defines Grove. The system already loads Noto Serif SC / Noto Sans SC as fallbacks per its existing font stack — the change is to add LXGW WenKai as the preferred CJK display face ahead of Noto Serif SC:

```css
/* Playfair roles (display, h1, h2, h3, quote, stat, watermark) */
font-family: 'Playfair Display', 'LXGW WenKai TC', 'Noto Serif SC', Georgia, serif;
/* Jost roles (lead, body, caption) */
font-family: 'Jost', 'Noto Serif SC', system-ui, sans-serif;
/* JetBrains Mono roles (label, kicker, chapter-num, stat-label) */
font-family: 'JetBrains Mono', 'Noto Sans Mono CJK SC', monospace;
```

(Note: the system currently lists `'Noto Sans SC'` in the Jost stack — for Grove's literary register, swap to `'Noto Serif SC'` instead. The Mincho body voice is closer to Jost weight 300's "good paper" feel than the Hei sans face.)

Baseline mismatch at display sizes (5.5–10vw) is mild — LXGW WenKai and Playfair Display both sit at similar optical baselines, so mixed-script headlines like `A Quiet 山林` read cleanly. The `<em>` italic-coral accent rule is the more delicate piece (see Known CJK Gap below).

### Loading

Replace the existing Noto-only fallback with an LXGW WenKai + Noto Serif SC + Noto Sans Mono CJK pair:

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=LXGW+WenKai+TC&family=Noto+Serif+SC:wght@300;400;500&family=Noto+Sans+Mono+CJK+SC:wght@300;400&display=swap" rel="stylesheet">
```

LXGW WenKai TC is the version hosted on Google Fonts (covers both traditional and simplified glyphs).

### Universal CJK Adjustments

These adjustments apply to **every CJK block** in this system, regardless of size or role:

- **Loosen line-height by 0.05–0.08.** CJK glyphs are full-width squares with more visual weight than Latin letterforms; line-heights tuned for Latin (1.0–1.1 on display, 1.65–1.75 on body) read as cramped in Chinese. Bump display to 1.15–1.25 and body to 1.7–1.85.
- **Remove negative letter-spacing on CJK headlines.** Playfair Display uses -0.01em to -0.03em tracking, which collides Chinese glyphs into each other. For CJK runs, set `letter-spacing: 0` — or a tiny positive `0.02em` if the headline feels visually packed.
- **Never `text-transform: uppercase` on CJK text.** Chinese has no case; the CSS property does nothing on Han glyphs but will silently break any mixed-script line where the JetBrains Mono portion was meant to be capitalized.
- **Use Chinese full-width punctuation** (`，。：；！？「」『』（）`) inside Chinese sentences, not the Latin equivalents (`,.:;!?""''()`). Mixing punctuation systems within one sentence reads as a typesetting error.
- **No period (。) at the end of CJK headlines.** Chinese headlines follow the same rule as Latin — title-style lines drop terminal punctuation. Body paragraphs keep their 。
- **Apply Pangu spacing (盘古之白) at the boundary between CJK and Latin runs.** A space (or 0.25em margin) belongs between a Chinese character and an adjacent Latin word or digit, e.g. `2026 年 5 月` not `2026年5月`. Either type the spaces manually or use a `pangu.js`-style auto-spacer.
- **One font per sentence.** Don't switch between LXGW WenKai and Noto Serif SC inside the same sentence — pick the face that matches the role (display = LXGW WenKai, body = Noto Serif SC) and commit to it for the whole run.

### Aesthetic Notes for This System

Grove's whole voice is "literary monograph / boutique brand book" with a single accent of terracotta coral. The Chinese equivalent of that quiet authority is **LXGW WenKai for every serif moment** (its hand-set warmth is the closest Chinese analog to Playfair Display at weight 400) and **Noto Serif SC at weight 300–400 for body** (the Mincho calm that matches Jost weight 300's reticence). Avoid running Chinese body in Noto Sans SC — the geometric sans tips the system from "good paper" to "modern app."

The em-dash bullet glyph in DM Mono coral works in Chinese without modification — the em-dash character itself is the same Unicode glyph (—) and reads as a deliberate mark in front of a Chinese list item. Keep the bullet column in Noto Sans Mono CJK SC at weight 300 coral, exactly mirroring the Latin pattern.

The watermark numeral (18vw, 6% opacity) is particularly effective in Chinese — render a Chinese ordinal character (e.g., 「三」or 「五」) instead of a Western digit. At 6% opacity it reads as faint compositional texture, and the Han glyph's denser visual weight at that scale balances the slide better than a thin Western numeral would.

### Known CJK Gap

The system's signature `<em>` italic-coral treatment is the hardest piece to translate: **Chinese has no italic concept** — slanted Han glyphs read as broken, not as emphasis. The current Grove CSS depends on the browser's default `<em>` styling (italic) plus a CSS color rule for the coral. In Chinese, the italic does nothing visually, so the emphasis collapses to "just coral text inside a headline." That's not bad — coral inside an LXGW WenKai headline still reads as a deliberate accent — but the system loses one of its two emphasis dimensions (color + slant) and is left with only color.

To compensate, two options: (1) accept color-only emphasis on Chinese headlines and let the coral carry the weight, or (2) switch the `<em>` portion to a different face inside Chinese headlines — e.g., **站酷小薇体 (ZCOOL XiaoWei)** — to provide a face-based contrast that approximates the slant contrast of Latin italic. Add to the CSS:

```css
.h1 em, .h2 em, .h3 em, .quote-text em {
  color: var(--c-accent);
  font-family: 'Playfair Display', 'ZCOOL XiaoWei', 'LXGW WenKai TC', serif;
  font-style: italic; /* Latin renders italic; CJK ignores */
}
```

This gives the Latin portion italic-coral and the CJK portion face-shift-coral. Test on a per-deck basis — for many Grove decks, color-only emphasis is sufficient.

## Iteration Guide

1. Any new headline is Playfair Display weight 400. Pick the size from the headline ladder (10vw display / 5.5vw h1 / 3.2vw h2 / 2vw h3) — do not invent a new size.
2. Any new emphasis inside a headline uses `<em>` and renders as italic coral automatically. Do not author a manual color/italic style.
3. Any new body paragraph is Jost weight 300 at 1.05vw (or 1.45vw for lede, or `max(1.4vw, 17px)` on list-style slides).
4. Any new label / kicker / footline / counter / caption is JetBrains Mono weight 300 uppercase with at least 0.12em letter-spacing.
5. Any new chrome line (top bar, bottom bar, section divider, stat-card border) is a 1px solid hairline in `{colors.border}` (dark) or `{colors.border-light}` (light). No thicker borders.
6. Any new bullet list uses the `{components.bullet-list}` pattern (two-column grid, em-dash in coral mono). Do not author a different bullet style.
7. Any new accent moment uses `{colors.accent}` terracotta coral. The accent is for italic headline emphasis, the 36px rule, em-dashes, stat figures, quote marks, chapter ordinals, and the kicker text. Nothing else.
8. Any new chapter or section-opener slide carries a `{components.grove-num}` watermark numeral in the bottom-right corner.
9. Any new content element gets a `[data-anim]` attribute (fade-up, fade-in, reveal-right, reveal-left, or scale-in) plus a `[data-delay]` (0–6) for staggered entrance. Do not author custom transitions.
10. When in doubt, add space rather than content. Grove's editorial register is silence, not density.

## Known Gaps

- The four Google Fonts (Playfair Display, Jost, JetBrains Mono, Noto Serif SC / Noto Sans SC) are loaded via `<link>`. Offline rendering will fall back to Georgia, system-ui, monospace, and system serif/sans for the Noto roles — which preserves the rough character but loses the typographic identity. Self-hosting recommended for offline / print reliability.
- The system loads Playfair weight 500 and Jost weights 200/400/500 — these are not used in the published CSS but are available. Using them would break the system's single-weight commitment.
- The `<em>` italic-coral treatment relies on the CSS rule `.h1 em, .h2 em, .h3 em { color: var(--c-accent); }` — note that the actual italic property is NOT set in the rule (the comment block is empty). The italic comes from the browser's default `<em>` styling. If a stylesheet override removes the default italic, the coral accent will lose its italic character.
- The grove-num watermark uses 18vw font-size — on extremely wide viewports (3000px+) this becomes very large and may push the slide-foot area. The CSS positions it at `bottom: -0.15em` to absorb some overflow, but very tall numerals at very wide viewports may need adjustment.
- The vertical sidebar component (`.grove-sidebar`) is loaded in the CSS but explicitly disabled (`display: none !important`). It was a chapter-tab decoration that read as clutter; the slide-chrome bar provides the section name already.
- The fixed slide-counter (`#slide-counter`) is also disabled — the slide-foot bar already shows "NN / TT" so the fixed counter was a duplicate.
- The CSS has a few empty rule blocks (`.h1 em { }`, `.grove-stat-val em { }`, `.quote-text { ... }`) which appear to be leftover stubs from earlier iterations. They are inert and can be removed without effect.
- The deck is built bilingually-aware (Chinese fallbacks via Noto Serif SC / Noto Sans SC) but no actual Chinese content is in the published source — the bilingual support is structural, not active.
- The image-placeholder component is a layout reservation, not a real image. Replacing the placeholder div with an `<img>` requires manually matching the parent's flex behavior and background.
````

---

## 2. 글씨체

- 이 디자인의 한글 글씨체: 제목 Grandiflora One(400) · 본문 Noto Sans KR Thin(100). (쓰는 폰트: Grandiflora One, Noto Sans KR Thin)
- 글씨체 파일은 공통 CSS(5)의 `@font-face`가 인터넷의 폰트 창고(`https://maker-ssam.github.io/korean-ofl-fonts/…`)에서 불러옵니다. 설치하지 않아도 되지만 열 때 인터넷이 필요합니다.
- 글씨체 규칙(굵기·크기·자간)은 1의 디자인 규칙을 따르고, 한글 폰트는 변수(`--kt` 제목, `--kb` 본문, `--ka` 포인트)로만 지정하세요.

---

## 3. 부품 사전 (이 디자인의 CSS에 정의된 클래스)

이 목록과 공통 CSS(5)에 있는 클래스만 쓰세요. '○장'은 아래 뼈대에서 그 클래스를 쓴 예시 장이고, '예'는 그 장의 실제 태그입니다. 장 구성은 따라 하지 말고, 필요한 부품을 골라 어느 장에서든 쓰면 됩니다. 클래스를 조합하는 방법(어떤 부품 안에 어떤 부품이 들어가는지)은 뼈대의 예시 장을 보고 따르세요.
- 여러 장에서 쓰인 클래스는 이 디자인의 공통 부품입니다(창·제목줄·버튼·표 등).
- 한 장에서만 쓰인 클래스 중 크기·위치를 정하는 것(이름에 번호가 붙은 창·격자 등)은 **그 예시 장 전용 배치**입니다. 같은 배치를 다시 쓸 때만 쓰고, 새 배치가 필요하면 비슷한 이름을 지어 쓰지 말고 '추가 부품' 칸에 직접 정의하세요.
- 예시 장의 측정기 그림(`g-…` 클래스)은 예시 주제 전용이라 이 목록에 없습니다. 새 주제의 그림은 SVG로 새로 그리세요.

**1장(표지)에서 처음 나오는 부품**
- `.c1` — 1장 — 예: `<i class="crule c1" data-anim="reveal-right" data-delay="1">`
- `.cnum` — 1장 — 예: `<div class="cnum" data-anim="fade-in" data-delay="3">`
- `.crule` — 1·2·3·4·5·6장 — 예: `<i class="crule c1" data-anim="reveal-right" data-delay="1">`
- `.dark` — 1·2·3·5장 — 예: `<section class="slide dark">`
- `.gk` — 1장 — 예: `<em class="gk">`
- `.k1` — 1장 — 예: `<div class="kick k1" data-anim="fade-up" data-delay="0">`
- `.kick` — 1·2·3·4·5·6장 — 예: `<div class="kick k1" data-anim="fade-up" data-delay="0">`
- `.sig` — 1장 — 예: `<div class="sig" data-anim="fade-in" data-delay="5">`
- `.slide` — 1·2·3·4·5·6장 — 예: `<section class="slide dark">`
- `.t1` — 1장 — 예: `<h1 class="t1" data-anim="fade-up" data-delay="3">`
- `.unit` — 1장 — 예: `<p class="unit" data-anim="fade-up" data-delay="2">`
- `.wm` — 1장 — 예: `<div class="wm">`

**2장(흐름·단계)에서 처음 나오는 부품**
- `.bar` — 2·3·4·5·6장 — 예: `<div class="bar top">`
- `.bot` — 2·3·4·5·6장 — 예: `<div class="bar bot">`
- `.bt` — 2·5·6장 — 예: `<button class="bt fill" data-act="step">`
- `.btns` — 2장 — 예: `<div class="btns" data-anim="fade-in" data-delay="5">`
- `.cap` — 2장 — 예: `<p class="cap" data-caps="[&quot;버튼을 눌러 순서대로 만들어 보세요&quot;,&quot;01 — 자 붙이기&quot;,&quot;02 — 수수깡 꽂기&quot;,&quo…`
- `.dash` — 2·4·5·6장 — 예: `<span class="dash">`
- `.def` — 2장 — 예: `<p class="def" data-anim="fade-in" data-delay="3">`
- `.fill` — 2장 — 예: `<button class="bt fill" data-act="step">`
- `.hd` — 2·3·4·5·6장 — 예: `<h2 class="hd" data-anim="fade-up" data-delay="2">`
- `.img` — 2장 — 예: `<figure class="img" data-anim="fade-in" data-delay="3">`
- `.li` — 2장 — 예: `<div class="li" data-s="1">`
- `.list` — 2장 — 예: `<div class="list" data-anim="fade-up" data-delay="4">`
- `.note` — 2장 — 예: `<p class="note" data-anim="fade-in" data-delay="6">`
- `.top` — 2·3·4·5·6장 — 예: `<div class="bar top">`

**3장(숫자와 그래프)에서 처음 나오는 부품**
- `.mono` — 3장 — 예: `<span class="mono">`
- `.mx` — 3장 — 예: `<span class="pf mx">`
- `.pf` — 3장 — 예: `<span class="pf">`
- `.sl` — 3장 — 예: `<span class="sl">`
- `.stat` — 3장 — 예: `<div class="stat" data-anim="fade-up" data-delay="2">`
- `.stats` — 3장 — 예: `<div class="stats">`
- `.sv` — 3장 — 예: `<span class="sv">`
- `.tbl` — 3·4장 — 예: `<div class="tbl" data-anim="fade-in" data-delay="5">`
- `.th` — 3·4장 — 예: `<div class="tr th">`
- `.tr` — 3·4장 — 예: `<div class="tr th">`
- `.track` — 3장 — 예: `<span class="track">`

**4장(비교와 표)에서 처음 나오는 부품**
- `.bl` — 4·5·6장 — 예: `<p class="bl">`
- `.cmp` — 4장 — 예: `<div class="cmp" data-anim="fade-up" data-delay="3">`
- `.cp` — 4장 — 예: `<div class="cp">`
- `.kk` — 4·5·6장 — 예: `<span class="kk">`
- `.light` — 4·6장 — 예: `<section class="slide light">`
- `.lst` — 4장 — 예: `<div class="lst" data-anim="fade-up" data-delay="4">`
- `.qm` — 4장 — 예: `<b class="qm">`
- `.qt` — 4장 — 예: `<blockquote class="qt" data-anim="fade-up" data-delay="5">`
- `.t4` — 4장 — 예: `<div class="tbl t4" data-anim="fade-in" data-delay="4">`

**5장(확인(퀴즈·빈칸))에서 처음 나오는 부품**
- `.blank` — 5장 — 예: `<button class="blank" data-blank aria-pressed="false">`
- `.blk` — 5장 — 예: `<div class="blk" data-anim="fade-up" data-delay="4">`
- `.fb` — 5장 — 예: `<p class="fb" data-fb>`
- `.opt` — 5장 — 예: `<button class="opt" data-opt="0">`
- `.opts` — 5장 — 예: `<div class="opts">`
- `.ot` — 5장 — 예: `<span class="ot">`
- `.q` — 5장 — 예: `<h3 class="q">`
- `.quiz` — 5장 — 예: `<div class="quiz" data-quiz data-answer="1" data-right="정답! 태양 고도가 가장 높을 때(남중) 그림자가 가장 짧아요" data-wrong="다시 생각해…`
- `.rt` — 5장 — 예: `<button class="bt rt" data-act="retry">`
- `.wl` — 5장 — 예: `<span class="kk wl">`
- `.words` — 5장 — 예: `<p class="words">`

**6장(디자인 안내(시연))에서 처음 나오는 부품**
- `.box6` — 6장 — 예: `<div class="box6 pan6" id="gv-pan">`
- `.d6` — 6장 — 예: `<div class="d6">`
- `.dh` — 6장 — 예: `<div class="dh">`
- `.dk` — 6장 — 예: `<div class="pp dk">`
- `.em6` — 6장 — 예: `<div class="box6 em6" id="gv-em">`
- `.g6` — 6장 — 예: `<div class="g6" data-anim="fade-up" data-delay="3">`
- `.k` — 6장 — 예: `<span class="k">`
- `.lt` — 6장 — 예: `<div class="pp lt">`
- `.pan6` — 6장 — 예: `<div class="box6 pan6" id="gv-pan">`
- `.pp` — 6장 — 예: `<div class="pp dk">`
- `.r` — 6장 — 예: `<i class="r">`
- `.sig6` — 6장 — 예: `<div class="sig6" data-anim="fade-up" data-delay="4">`
- `.sw6` — 6장 — 예: `<span class="sw6">`
- `.trk` — 6장 — 예: `<div class="trk">`
- `.up6` — 6장 — 예: `<div class="box6 up6" id="gv-up">`

**직접 쓰지 말 것**: 엔진이 상태를 나타내려고 붙이는 클래스(`.on` `.now` `.open` `.right` `.wrong` `.play` `.solved` `.tried` `.active` `.visible` `.flash` `.in-frame`)와 내비 클래스(`.dk-…`)는 배치용으로 쓰지 마세요(뼈대의 내비 부분은 그대로 둡니다).

---

## 4. HTML 뼈대 (예시 덱 6장 — 부품 견본입니다. 장 구성은 따라 하지 말고, 공통 파일 두 줄은 그대로 두며, 장은 내용에 맞게 새로 구성)

```html
<!DOCTYPE html>
<html lang="ko" data-template="grove">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>계절의 변화</title>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;1,400&family=Jost:wght@300;400&family=JetBrains+Mono:wght@300;400&display=swap">
<!-- 이 디자인의 공통 CSS(글씨체·색·부품 모양). 이 줄은 지우거나 바꾸지 마세요 -->
<link rel="stylesheet" href="https://k-slide.web.app/kit/v1/grove.css">
<style>
/* === 추가 부품: 새 클래스가 꼭 필요할 때만 이 줄 아래에 정의하세요(공통 CSS는 위 <link>가 이미 불러옴 — 여기에 옮겨 쓰지 않음) === */
</style>
</head>
<body>
<div class="deck-viewport">
  <main class="deck-stage" id="deckStage">
<section class="slide dark">
  <div class="wm">03</div>
  <div class="kick k1" data-anim="fade-up" data-delay="0">초등 과학 · 6학년 2학기 · 1단원 · 3~4차시</div><i class="crule c1" data-anim="reveal-right" data-delay="1"></i>
  <p class="unit" data-anim="fade-up" data-delay="2">계절의 변화<span>Sun, Shadow & <em>Temperature</em></span></p>
  <div class="cnum" data-anim="fade-in" data-delay="3">학습 목표</div>
  <h1 class="t1" data-anim="fade-up" data-delay="3">하루 동안 <em class="gk">태양 고도</em>,<br><em class="gk">그림자 길이</em>, <em class="gk">기온</em>의 관계</h1>
  <div class="sig" data-anim="fade-in" data-delay="5"><span>00초등학교</span><span>2026</span></div>
</section>
<section class="slide dark" data-steps="4">
  <div class="bar top"><span>계절의 변화</span><span>측정 준비</span></div>
  <div class="kick" data-anim="fade-up" data-delay="0">측정 준비</div><i class="crule" data-anim="reveal-right" data-delay="1"></i>
  <h2 class="hd" data-anim="fade-up" data-delay="2">태양 고도 측정기 <em>만들기</em></h2>
  <figure class="img" data-anim="fade-in" data-delay="3"><figcaption>[ FIG. 01 — 태양 고도 측정기 ]</figcaption><svg class="gauge" viewBox="0 0 1000 660" role="img" aria-label="태양 고도 측정기: 태양 빛, 수수깡, 실, 태양 고도, 그림자 길이, 자, 각도기">
  <line class="g-ground" x1="20" x2="980" y1="580" y2="580"/>
  <rect class="g-board" x="80" y="540" width="860" height="40"/>
  <g class="g-part" data-s="1"><rect class="g-ruler" x="110" y="524" width="800" height="16"/><line class="g-tick" x1="120" x2="120" y1="524" y2="536"/><line class="g-tick" x1="140" x2="140" y1="524" y2="530"/><line class="g-tick" x1="160" x2="160" y1="524" y2="530"/><line class="g-tick" x1="180" x2="180" y1="524" y2="530"/><line class="g-tick" x1="200" x2="200" y1="524" y2="530"/><line class="g-tick" x1="220" x2="220" y1="524" y2="536"/><line class="g-tick" x1="240" x2="240" y1="524" y2="530"/><line class="g-tick" x1="260" x2="260" y1="524" y2="530"/><line class="g-tick" x1="280" x2="280" y1="524" y2="530"/><line class="g-tick" x1="300" x2="300" y1="524" y2="530"/><line class="g-tick" x1="320" x2="320" y1="524" y2="536"/><line class="g-tick" x1="340" x2="340" y1="524" y2="530"/><line class="g-tick" x1="360" x2="360" y1="524" y2="530"/><line class="g-tick" x1="380" x2="380" y1="524" y2="530"/><line class="g-tick" x1="400" x2="400" y1="524" y2="530"/><line class="g-tick" x1="420" x2="420" y1="524" y2="536"/><line class="g-tick" x1="440" x2="440" y1="524" y2="530"/><line class="g-tick" x1="460" x2="460" y1="524" y2="530"/><line class="g-tick" x1="480" x2="480" y1="524" y2="530"/><line class="g-tick" x1="500" x2="500" y1="524" y2="530"/><line class="g-tick" x1="520" x2="520" y1="524" y2="536"/><line class="g-tick" x1="540" x2="540" y1="524" y2="530"/><line class="g-tick" x1="560" x2="560" y1="524" y2="530"/><line class="g-tick" x1="580" x2="580" y1="524" y2="530"/><line class="g-tick" x1="600" x2="600" y1="524" y2="530"/><line class="g-tick" x1="620" x2="620" y1="524" y2="536"/><line class="g-tick" x1="640" x2="640" y1="524" y2="530"/><line class="g-tick" x1="660" x2="660" y1="524" y2="530"/><line class="g-tick" x1="680" x2="680" y1="524" y2="530"/><line class="g-tick" x1="700" x2="700" y1="524" y2="530"/><line class="g-tick" x1="720" x2="720" y1="524" y2="536"/><line class="g-tick" x1="740" x2="740" y1="524" y2="530"/><line class="g-tick" x1="760" x2="760" y1="524" y2="530"/><line class="g-tick" x1="780" x2="780" y1="524" y2="530"/><line class="g-tick" x1="800" x2="800" y1="524" y2="530"/><line class="g-tick" x1="820" x2="820" y1="524" y2="536"/><line class="g-tick" x1="840" x2="840" y1="524" y2="530"/><line class="g-tick" x1="860" x2="860" y1="524" y2="530"/><line class="g-tick" x1="880" x2="880" y1="524" y2="530"/><line class="g-tick" x1="900" x2="900" y1="524" y2="530"/><text class="g-lb" x="922" y="516" text-anchor="start">자</text></g>
  <g class="g-meas"><rect class="g-shadow" x="220" y="524" width="446" height="16"/>
    <path class="g-dim" d="M220 600v24M666 600v24M220 612H666"/><text class="g-lb" x="443" y="652" text-anchor="middle">그림자 길이</text></g>
  <g class="g-part" data-s="2"><rect class="g-stick" x="196" y="150" width="24" height="374"/><path class="g-mark" d="M220 504h20v20"/>
    <path class="g-lead" d="M182 330H196"/><text class="g-lb" x="174" y="342" text-anchor="end">수수깡</text></g>
  <g class="g-part" data-s="4"><path class="g-prot" d="M546 524A120 120 0 0 1 786 524Z"/><path class="g-prot2" d="M588 524A78 78 0 0 1 744 524"/><line class="g-tick" x1="570" y1="524" x2="546" y2="524"/><line class="g-tick" x1="561.6" y1="505.6" x2="547.8" y2="503.2"/><line class="g-tick" x1="566.4" y1="487.7" x2="553.2" y2="483"/><line class="g-tick" x1="582.9" y1="476" x2="562.1" y2="464"/><line class="g-tick" x1="584.8" y1="455.9" x2="574.1" y2="446.9"/><line class="g-tick" x1="597.9" y1="442.8" x2="588.9" y2="432.1"/><line class="g-tick" x1="618" y1="440.9" x2="606" y2="420.1"/><line class="g-tick" x1="629.7" y1="424.4" x2="625" y2="411.2"/><line class="g-tick" x1="647.6" y1="419.6" x2="645.2" y2="405.8"/><line class="g-tick" x1="666" y1="428" x2="666" y2="404"/><line class="g-tick" x1="684.4" y1="419.6" x2="686.8" y2="405.8"/><line class="g-tick" x1="702.3" y1="424.4" x2="707" y2="411.2"/><line class="g-tick" x1="714" y1="440.9" x2="726" y2="420.1"/><line class="g-tick" x1="734.1" y1="442.8" x2="743.1" y2="432.1"/><line class="g-tick" x1="747.2" y1="455.9" x2="757.9" y2="446.9"/><line class="g-tick" x1="749.1" y1="476" x2="769.9" y2="464"/><line class="g-tick" x1="765.6" y1="487.7" x2="778.8" y2="483"/><line class="g-tick" x1="770.4" y1="505.6" x2="784.2" y2="503.2"/><line class="g-tick" x1="762" y1="524" x2="786" y2="524"/><circle class="g-hole" cx="666" cy="524" r="5"/><text class="g-lb" x="800" y="456" text-anchor="start">각도기</text></g>
  <g class="g-part" data-s="3"><line class="g-string" x1="220" y1="150" x2="666" y2="524"/><text class="g-lb" x="478" y="344" text-anchor="start">실</text></g>
  <g class="g-meas"><path class="g-angle" d="M616 524A50 50 0 0 1 627.7 491.9"/><path class="g-lead" d="M542 498L615 506.9"/><text class="g-lb" x="536" y="508" text-anchor="end">태양 고도</text>
    <line class="g-ray" x1="74.5" y1="27.9" x2="187.8" y2="123"/><polygon class="g-rayh" points="209.3,141 179.5,133 196.2,113"/><text class="g-lb" x="150" y="60" text-anchor="start">태양 빛</text></g>
</svg></figure>
  <p class="def" data-anim="fade-in" data-delay="3">태양 고도 — 태양이 지표면과 이루는 각</p>
  <div class="list" data-anim="fade-up" data-delay="4"><div class="li" data-s="1"><span class="dash">—</span><div><h3>자 붙이기</h3><p>우드록 한쪽에 양면테이프로 자를 붙여요</p></div></div><div class="li" data-s="2"><span class="dash">—</span><div><h3>수수깡 꽂기</h3><p>수수깡을 10cm 정도 잘라 자의 '0' 위치에 수직으로 꽂아요</p></div></div><div class="li" data-s="3"><span class="dash">—</span><div><h3>실 묶기</h3><p>수수깡 끝에 실을 묶어요</p></div></div><div class="li" data-s="4"><span class="dash">—</span><div><h3>각도기 끼우기</h3><p>실의 다른 끝을 각도기 가운데 구멍에 끼워요</p></div></div></div>
  <div class="btns" data-anim="fade-in" data-delay="5"><button class="bt fill" data-act="step">다음 단계 →</button><button class="bt" data-act="reset">처음부터</button><p class="cap" data-caps="[&quot;버튼을 눌러 순서대로 만들어 보세요&quot;,&quot;01 — 자 붙이기&quot;,&quot;02 — 수수깡 꽂기&quot;,&quot;03 — 실 묶기&quot;,&quot;04 — 각도기 끼우기&quot;]">버튼을 눌러 순서대로 만들어 보세요</p></div>
  <p class="note" data-anim="fade-in" data-delay="6"><span class="dash">—</span>막대기의 그림자와 실이 이루는 각 = 태양 고도</p>
  <div class="bar bot"><span>버튼 규칙 없음 → 1px 헤어라인 칸으로 표현</span><span>02 / 06</span></div>
</section>
<section class="slide dark">
  <div class="bar top"><span>계절의 변화</span><span>측정 결과</span></div>
  <div class="kick" data-anim="fade-up" data-delay="0">측정 결과</div><i class="crule" data-anim="reveal-right" data-delay="1"></i>
  <h2 class="hd" data-anim="fade-up" data-delay="2">하루 동안 무엇이 <em>어떻게</em> 변할까요?</h2>
  <div class="stats"><div class="stat" data-anim="fade-up" data-delay="2"><span class="sv">12:30</span><span class="sl">남중 시각</span><p>태양 고도가 가장 높아요 무렵 · 남중</p></div><div class="stat" data-anim="fade-up" data-delay="3"><span class="sv">정남</span><span class="sl">남중 방향</span><p>남중할 때 태양의 위치 쪽</p></div><div class="stat" data-anim="fade-up" data-delay="4"><span class="sv">14:30</span><span class="sl">최고 기온</span><p>기온이 가장 높아요 무렵</p></div><div class="stat" data-anim="fade-up" data-delay="5"><span class="sv">9:30</span><span class="sl">가장 긴 그림자</span><p>그림자가 가장 길어요 · 15:30</p></div></div>
  <div class="tbl" data-anim="fade-in" data-delay="5">
    <div class="tr th"><span>시각 · 예시 값</span><span>태양 고도(°)</span><span></span><span>그림자 길이(cm)</span><span>기온(℃)</span></div>
    <div class="tr"><span class="mono">9:30</span><span class="pf">38°</span><span class="track"><i style="width:63%"></i></span><span>12.9</span><span>22</span></div><div class="tr"><span class="mono">10:30</span><span class="pf">48°</span><span class="track"><i style="width:80%"></i></span><span>9.1</span><span>23.8</span></div><div class="tr"><span class="mono">11:30</span><span class="pf">55°</span><span class="track"><i style="width:92%"></i></span><span>7</span><span>25.2</span></div><div class="tr"><span class="mono">12:30</span><span class="pf mx">57°</span><span class="track"><i style="width:95%"></i></span><span>6.4</span><span>26.3</span></div><div class="tr"><span class="mono">13:30</span><span class="pf">54°</span><span class="track"><i style="width:90%"></i></span><span>7.2</span><span>27</span></div><div class="tr"><span class="mono">14:30</span><span class="pf">47°</span><span class="track"><i style="width:78%"></i></span><span>9.4</span><span>27.4</span></div><div class="tr"><span class="mono">15:30</span><span class="pf">37°</span><span class="track"><i style="width:62%"></i></span><span>13.4</span><span>26.9</span></div>
  </div>
  <div class="bar bot"><span>예시 값 — 태양 고도·그림자(10cm 막대)는 9월 중순 북위 36° 기준 계산, 기온은 교과서 그래프 모양을 따른 값</span><span>03 / 06</span></div>
</section>
<section class="slide light">
  <div class="bar top"><span>계절의 변화</span><span>관계 정리</span></div>
  <div class="kick" data-anim="fade-up" data-delay="0">관계 정리</div><i class="crule" data-anim="reveal-right" data-delay="1"></i>
  <h2 class="hd" data-anim="fade-up" data-delay="2">태양 고도가 바뀌면 무엇이 <em>달라질까요</em>?</h2>
  <div class="cmp" data-anim="fade-up" data-delay="3"><div class="cp"><h3>태양 고도가 높을 때</h3><p class="bl"><span class="dash">—</span>그림자 길이는 짧아져요</p><p class="bl"><span class="dash">—</span>기온은 높아져요</p></div><div class="cp"><h3>태양 고도가 낮을 때</h3><p class="bl"><span class="dash">—</span>그림자 길이는 길어져요</p><p class="bl"><span class="dash">—</span>기온은 낮아져요</p></div></div>
  <div class="tbl t4" data-anim="fade-in" data-delay="4"><div class="tr th"><span>시각</span><span>태양 고도</span><span>그림자 길이</span><span>기온</span></div><div class="tr"><span>오전 9:30</span><span>낮아요</span><span>길어요</span><span>낮아요</span></div><div class="tr"><span>낮 12:30</span><span>가장 높아요</span><span>가장 짧아요</span><span>높아지는 중</span></div><div class="tr"><span>오후 14:30</span><span>낮아지는 중</span><span>길어지는 중</span><span>가장 높아요</span></div></div>
  <div class="lst" data-anim="fade-up" data-delay="4"><span class="kk">남중</span><p class="bl"><span class="dash">—</span>태양이 정남쪽에 있을 때 '남중했다'고 해요</p><p class="bl"><span class="dash">—</span>이때의 태양 고도를 남중 고도라고 해요</p></div>
  <blockquote class="qt" data-anim="fade-up" data-delay="5"><b class="qm">“</b><p>태양 고도가 가장 높은 시각과 기온이 가장 높은 시각에는 차이가 있어요.</p><span class="kk">오늘의 정리</span></blockquote>
  <div class="bar bot"><span>표 규칙 없음 → 1px 헤어라인 줄으로 표현</span><span>04 / 06</span></div>
</section>
<section class="slide dark">
  <div class="bar top"><span>계절의 변화</span><span>확인하기</span></div>
  <div class="kick" data-anim="fade-up" data-delay="0">확인하기</div><i class="crule" data-anim="reveal-right" data-delay="1"></i>
  <h2 class="hd" data-anim="fade-up" data-delay="2">배운 내용을 <em>확인해</em> 볼까요?</h2>
  <div class="quiz" data-quiz data-answer="1" data-right="정답! 태양 고도가 가장 높을 때(남중) 그림자가 가장 짧아요" data-wrong="다시 생각해 볼까요? 태양 고도와 그림자 길이의 관계를 떠올려 보세요" data-anim="fade-up" data-delay="3">
    <span class="kk">QUESTION</span>
    <h3 class="q">하루 중 그림자 길이가 가장 짧을 때는?</h3>
    <div class="opts"><button class="opt" data-opt="0"><span class="dash">—</span><span class="ot">오전 9시 30분</span></button><button class="opt" data-opt="1"><span class="dash">—</span><span class="ot">낮 12시 30분 무렵</span></button><button class="opt" data-opt="2"><span class="dash">—</span><span class="ot">오후 3시 30분</span></button></div>
    <p class="fb" data-fb></p>
    <button class="bt rt" data-act="retry">다시 풀기 ↺</button>
  </div>
  <div class="blk" data-anim="fade-up" data-delay="4">
    <span class="kk">빈칸을 눌러 확인해요</span>
    <p class="bl"><span class="dash">—</span><span><button class="blank" data-blank aria-pressed="false"><span>태양 고도</span></button>는 점점 높아졌다가 다시 낮아진다</span></p><p class="bl"><span class="dash">—</span><span><button class="blank" data-blank aria-pressed="false"><span>그림자 길이</span></button>는 점점 짧아졌다가 다시 길어진다</span></p><p class="bl"><span class="dash">—</span><span><button class="blank" data-blank aria-pressed="false"><span>기온</span></button>은 점점 높아졌다가 다시 낮아진다</span></p>
    <span class="kk wl">오늘의 낱말</span>
    <p class="words"><em>태양 고도</em> · <em>남중 고도</em> · <em>그림자 길이</em> · <em>기온</em></p>
  </div>
  <div class="bar bot"><span>빈칸 규칙 없음 → 1px 밑줄 칸으로 표현</span><span>05 / 06</span></div>
</section>
<section class="slide light">
  <div class="bar top"><span>계절의 변화</span><span>디자인 안내</span></div>
  <div class="kick" data-anim="fade-up" data-delay="0">디자인 안내</div><i class="crule" data-anim="reveal-right" data-delay="1"></i>
  <h2 class="hd" data-anim="fade-up" data-delay="2">이 디자인 사용 <em>설명서</em></h2>
  <div class="g6" data-anim="fade-up" data-delay="3">
    <span class="kk">한눈에</span><p class="bl"><span class="dash">—</span><span>조용한 모노그래프·브랜드북. <b>깊은 숲 초록 + 따뜻한 크림 글자</b>, 밝은 장은 양피지. 포인트는 테라코타 코랄 하나뿐이에요.</span></p>
    <span class="kk">색</span><p class="bl"><span class="dash">—</span><span><span class="sw6"><span><i style="background:#192b1b"></i>숲 초록</span><span><i style="background:#d4cfbf"></i>크림 글자</span><span><i style="background:#e8e4d6"></i>양피지</span><span><i style="background:#c8524a"></i>코랄</span></span></span></p>
    <span class="kk">글꼴</span><p class="bl"><span class="dash">—</span><span>제목 Playfair Display 400(굵게 금지) → Grandiflora One</span></p><p class="bl"><span class="dash">—</span><span>본문 Jost 300 → Noto Sans KR Thin · 라벨 JetBrains Mono</span></p>
    <span class="kk">이렇게 쓰면 좋아요</span><p class="bl"><span class="dash">—</span><span>천천히 읽는 설명·감상 수업. 장마다 한 낱말만 <em>코랄</em>로.</span></p>
  </div>
  <div class="sig6" data-anim="fade-up" data-delay="4">
    <span class="kk">대표 장치 — 눌러 보세요</span>
    <div class="d6"><div class="dh"><h3>가로 팬 넘김</h3><button class="bt" data-demo="#gv-pan">다시 보기</button></div><p>장이 0.9초 동안 옆으로 미끄러져요(처음·끝이 느린 곡선).</p><div class="box6 pan6" id="gv-pan"><div class="trk"><div class="pp dk">01<em>.</em></div><div class="pp dk">02<em>.</em></div><div class="pp lt">03<em>.</em></div></div></div></div>
    <div class="d6"><div class="dh"><h3>계단 등장</h3><button class="bt" data-demo="#gv-up">다시 보기</button></div><p>킥커 → 코랄 선 → 제목 → 본문이 0.08~0.78초 간격으로 떠올라요.</p><div class="box6 up6" id="gv-up"><span class="k">KICKER</span><i class="r"></i><b>제목이 떠올라요</b></div></div>
    <div class="d6"><div class="dh"><h3>코랄 이탤릭 강조</h3><button class="bt" data-demo="#gv-em" data-mode="toggle" aria-pressed="false">켜기 / 끄기</button></div><p>&lt;em&gt;은 굵게가 아니라 코랄 이탤릭으로 — 한글은 색만 바꿔요.</p><div class="box6 em6" id="gv-em">Sun, Shadow &amp; <i>Temperature</i></div></div>
  </div>
  <div class="bar bot"><span>계절의 변화</span><span>06 / 06</span></div>
</section>
<nav class="dk-nav"><button class="dk-prev" aria-label="이전">←</button><span class="dk-dots"><button class="dk-dot" aria-label="1장"></button><button class="dk-dot" aria-label="2장"></button><button class="dk-dot" aria-label="3장"></button><button class="dk-dot" aria-label="4장"></button><button class="dk-dot" aria-label="5장"></button><button class="dk-dot" aria-label="6장"></button></span><button class="dk-next" aria-label="다음">→</button></nav>
  </main>
</div>

<!-- 슬라이드 넘김·단계 버튼·퀴즈 엔진. 이 줄은 지우거나 바꾸지 마세요 -->
<script src="https://k-slide.web.app/kit/v1/grove.js"></script>
</body>
</html>
```

---

## 5. 참고: 공통 CSS와 엔진 사용법 (뼈대가 이미 불러옴 — HTML에 옮겨 쓰지 마세요)

아래는 `https://k-slide.web.app/kit/v1/grove.css`의 내용과 엔진(`https://k-slide.web.app/kit/v1/grove.js`)의 사용법 주석입니다. 클래스의 모양·크기·배치를 이해하는 데에만 쓰세요. 이 내용을 `<style>`이나 `<script>`에 넣으면 안 됩니다.

```css
/* grove — k-slide 공통 CSS v1 (채팅 AI용 받기 .md가 불러옴, 고치지 말 것) */
/* === KOREAN FONTS === */
@font-face{font-family:'kp-24';font-display:swap;src:url("https://maker-ssam.github.io/korean-ofl-fonts/fonts/grandiflora-one/GrandifloraOne-Regular.woff2") format("woff2");font-weight:400}
@font-face{font-family:'kp-25';font-display:swap;src:url("https://maker-ssam.github.io/korean-ofl-fonts/fonts/noto-sans-kr/NotoSansKR-Thin.woff2") format("woff2");font-weight:100}
/* === THEME === */
:root{--stage-bg:#192b1b;--slide-bg:#192b1b;
  --kt:'kp-24';--kb:'kp-25';--ka:'kp-25';
  --wt:400;--wb:100}
*{margin:0;padding:0;box-sizing:border-box}
body{font-synthesis:none;word-break:keep-all;letter-spacing:0}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
button:disabled{cursor:default}
/* === VIEWPORT BASE (frontend-slides, mandatory) === */
/* ===========================================
   FIXED 16:9 STAGE: MANDATORY BASE STYLES
   Include this ENTIRE file in every presentation.
   Slides are authored at 1920×1080 and scaled as a whole.
   =========================================== */

/* 1. Lock the browser viewport */
html,
body {
    width: 100%;
    height: 100%;
    margin: 0;
    overflow: hidden;
    background: var(--stage-bg, #000);
}

/* 2. Full-window deck viewport */
.deck-viewport {
    position: fixed;
    inset: 0;
    overflow: hidden;
    background: var(--stage-bg, #000);
}

/* 3. Fixed 16:9 design canvas.
   JavaScript sets transform: translate(...) scale(...). */
.deck-stage {
    position: absolute;
    left: 0;
    top: 0;
    width: 1920px;
    height: 1080px;
    overflow: hidden;
    transform-origin: 0 0;
    background: var(--slide-bg, #fff);
}

/* 4. Slides stack inside the fixed stage.
   Content must be laid out at 1920×1080, not reflowed per device. */
.slide {
    position: absolute;
    inset: 0;
    width: 1920px;
    height: 1080px;
    overflow: hidden;
    display: block;
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
    background: var(--slide-bg, #fff);
}

.slide.active,
.slide.visible {
    visibility: visible;
    opacity: 1;
    pointer-events: auto;
    z-index: 1;
}

/* 5. Keep media inside authored slide bounds */
img,
video,
canvas,
svg {
    max-width: 100%;
    max-height: 100%;
}

/* 6. Presentation chrome stays outside the slide design system */
.deck-controls {
    position: fixed;
    left: 50%;
    bottom: 22px;
    transform: translateX(-50%);
    z-index: 1000;
}

/* 7. Print one fixed-size slide per page */
@media print {
    html,
    body {
        width: 1920px;
        height: auto;
        overflow: visible;
        background: #fff;
    }

    .deck-viewport {
        position: static;
        overflow: visible;
        background: #fff;
    }

    .deck-stage {
        position: static;
        width: auto;
        height: auto;
        transform: none !important;
        background: none;
    }

    .slide {
        position: relative;
        display: block !important;
        visibility: visible !important;
        opacity: 1 !important;
        pointer-events: auto !important;
        width: 1920px;
        height: 1080px;
        break-after: page;
        page-break-after: always;
    }

    .slide:last-child {
        break-after: auto;
        page-break-after: auto;
    }

    .deck-controls {
        display: none !important;
    }
}

/* 8. Reduced motion */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.2s !important;
    }
}

/* 동작 줄이기(Windows '애니메이션 효과' 끔 등): 위 규칙이 길이를 0.01ms로 줄이면 반복 애니메이션이 깜빡이듯 빨라짐 → 반복은 한 번만 */
@media (prefers-reduced-motion: reduce){*,*::before,*::after{animation-iteration-count:1 !important}}
.dk-nav{position:absolute;z-index:50}


/* === 측정기 그림(figure-gauge.js) 기본 === */
.gauge{display:block;width:100%;height:auto;overflow:visible;--g-sw:2.5}
.gauge .g-lb{font-family:var(--g-font,var(--kb));font-size:40px;font-weight:700;fill:var(--g-ink,currentColor)}
.gauge .g-ground{stroke:var(--g-ink,currentColor);stroke-width:var(--g-sw);opacity:.35}
.gauge .g-board{fill:var(--g-board,none);stroke:var(--g-ink,currentColor);stroke-width:var(--g-sw)}
.gauge .g-ruler{fill:var(--g-ruler,none);stroke:var(--g-ink,currentColor);stroke-width:var(--g-sw)}
.gauge .g-tick{stroke:var(--g-ink,currentColor);stroke-width:1.5}
.gauge .g-stick{fill:var(--g-stick,var(--g-ink,currentColor));stroke:var(--g-ink,currentColor);stroke-width:var(--g-sw)}
.gauge .g-mark,.gauge .g-lead,.gauge .g-dim,.gauge .g-angle{fill:none;stroke:var(--g-ink,currentColor);stroke-width:var(--g-sw)}
.gauge .g-angle{stroke-width:4}
.gauge .g-shadow{fill:var(--g-shadow,var(--g-ink,currentColor));opacity:.6}
.gauge .g-string{stroke:var(--g-acc,var(--g-ink,currentColor));stroke-width:4}
.gauge .g-prot{fill:var(--g-prot,none);stroke:var(--g-ink,currentColor);stroke-width:var(--g-sw)}
.gauge .g-prot2{fill:none;stroke:var(--g-ink,currentColor);stroke-width:1.5}
.gauge .g-hole{fill:var(--g-ink,currentColor)}
.gauge .g-ray{stroke:var(--g-acc,var(--g-ink,currentColor));stroke-width:7}
.gauge .g-rayh{fill:var(--g-acc,var(--g-ink,currentColor))}
/* 만드는 중(1~3단계): 아직 안 만든 부품과 측정 표시는 흐리게 / 0·4단계는 모두 또렷 */
.slide[data-step="1"] .gauge .g-part:not(.on),.slide[data-step="2"] .gauge .g-part:not(.on),.slide[data-step="3"] .gauge .g-part:not(.on),
.slide[data-step="1"] .gauge .g-meas,.slide[data-step="2"] .gauge .g-meas,.slide[data-step="3"] .gauge .g-meas{opacity:var(--g-dim,.2)}

/* === TEMPLATE STYLES === */

/* --- 토큰(8vw = 154px, 6.5vh = 70px) --- */
:root{--bg:#192b1b;--bga:#1e3221;--fg:#d4cfbf;--fg2:rgba(212,207,191,.66);--fg3:rgba(212,207,191,.32);--bgl:#e8e4d6;--fgl:#192b1b;--fgl2:rgba(25,43,27,.62);--fgl3:rgba(25,43,27,.3);
  --acc:#c8524a;--bd:rgba(212,207,191,.14);--bdl:rgba(25,43,27,.16);--px:154px;--spring:cubic-bezier(.16,1,.3,1)}
.slide{font-family:'Jost',var(--kb),sans-serif;font-weight:var(--wb);overflow:hidden}
.slide>*{position:absolute}
.dark{background:var(--bg);color:var(--fg);--m:var(--fg2);--h:var(--fg3);--b:var(--bd)}
.light{background:var(--bgl);color:var(--fgl);--m:var(--fgl2);--h:var(--fgl3);--b:var(--bdl)}
.slide em{font-style:normal;color:var(--acc)}
.unit span em,.q em{font-family:'Playfair Display',serif;font-style:italic}
.kick,.bar,.kk,.mono,.sig,.cnum,.sl,figcaption,.th{font-family:'JetBrains Mono',var(--kb),monospace;font-weight:300;text-transform:uppercase}
.dash{font:300 1em/1 'JetBrains Mono',monospace;color:var(--acc)}
h3,.hd,.t1,.unit,.q,.qt p,.sv,.pf{font-family:'Playfair Display',var(--kt),serif;font-weight:var(--wt);letter-spacing:0}

/* --- 1px 헤어라인 상단·하단 바(내용 장) --- */
.bar{left:var(--px);right:var(--px);display:flex;justify-content:space-between;gap:40px;font-size:22px;letter-spacing:.12em;color:var(--m)}
.bar span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:1100px}
.bar.top{top:60px;padding-bottom:14px;border-bottom:1px solid var(--b)}
.bar.bot{bottom:60px;padding-top:14px;border-top:1px solid var(--b)}
.kick{left:var(--px);top:150px;font-size:26px;letter-spacing:.2em;color:var(--acc)}
.crule{left:var(--px);top:196px;width:36px;height:1px;background:var(--acc)}
.hd{left:var(--px);right:var(--px);top:216px;font-size:68px;line-height:1.2;white-space:nowrap}
.kk{display:block;font-family:'JetBrains Mono',var(--kb),monospace;font-weight:300;font-size:24px;letter-spacing:.2em;color:var(--acc)}
.bt{border:1px solid var(--m);padding:14px 30px;font:400 30px/1 var(--kb);color:inherit;background:transparent}
.bt.fill{background:var(--fg);border-color:var(--fg);color:var(--bg)}
.light .bt.fill{background:var(--fgl);color:var(--bgl)}
.bt:disabled{opacity:.3}

/* --- 1. 표지(크롬 없음): 킥커·코랄 선 → 단원(+ 이탤릭 코랄 em 영문) → 장 번호 라벨 → 학습 목표 h1(낱말 = 코랄) → 서명, 오른쪽 아래 워터마크 --- */
.wm{right:var(--px);bottom:-70px;font:400 346px/1 'Playfair Display',serif;color:var(--fg);opacity:.06;pointer-events:none}
.k1{top:200px}.c1{top:248px}
.unit{left:var(--px);top:290px;display:flex;align-items:baseline;gap:30px;font-size:76px;line-height:1.15;white-space:nowrap}
.unit span{font:400 52px/1 'Playfair Display',serif;color:var(--fg2)}
.cnum{left:var(--px);top:450px;font-size:24px;letter-spacing:.2em;color:var(--acc)}
.t1{left:146px;top:496px;font-size:118px;line-height:1.24;white-space:nowrap}
.t1 .gk{font-style:normal}
.sig{left:var(--px);bottom:90px;display:flex;gap:40px;font-size:24px;letter-spacing:.16em;color:var(--fg3)}

/* --- 2. 흐름(어둠): 킥커 묶음 → 제목 → 이미지 자리(bg-alt) 속 측정기 그림 | 정의 + em-dash 4단계(켜지면 또렷) + 헤어라인 버튼 + em-dash 메모 --- */
.img{left:var(--px);top:330px;width:840px;background:var(--bga);padding:16px 30px 10px;
  --g-ink:var(--fg);--g-acc:var(--acc);--g-stick:var(--fg2);--g-board:var(--bg);--g-ruler:var(--bga);--g-prot:rgba(212,207,191,.08);--g-shadow:var(--fg2);--g-font:'Jost',var(--kb),sans-serif;--g-sw:1.5}
.img figcaption{font-size:20px;letter-spacing:.12em;color:var(--fg3)}
.def{left:1060px;right:var(--px);top:330px;font-size:28px;line-height:1.5;color:var(--m)}
.list{left:1060px;right:var(--px);top:390px}
.li{display:grid;grid-template-columns:50px 1fr;align-items:baseline;padding:12px 0;border-bottom:1px solid var(--b)}
.slide[data-step]:not([data-step="0"]) .li:not(.on){color:var(--h)}
.slide[data-step]:not([data-step="0"]) .li:not(.on) .dash{opacity:.3}
.li .dash{font-size:32px}
.li h3{font-size:40px;line-height:1.2}
.li p{margin-top:4px;font-size:27px;line-height:1.5;color:var(--m)}
.slide[data-step]:not([data-step="0"]) .li:not(.on) p{color:var(--h)}
.btns{left:1060px;right:var(--px);top:850px;display:flex;flex-wrap:wrap;gap:12px 18px;align-items:center}
.cap{flex:1;min-width:260px;font-size:26px;color:var(--m)}
.note{left:var(--px);width:840px;top:900px;display:flex;gap:18px;font-size:30px;line-height:1.35}

/* --- 3. 숫자와 그래프(어둠): grove-stat 4개 + 1px 줄 표(가는 크림 막대, 가장 높은 값 = 코랄) --- */
.stats{left:var(--px);right:var(--px);top:320px;display:grid;grid-template-columns:repeat(4,1fr);gap:56px}
.stat{padding-bottom:16px;border-bottom:1px solid var(--b);display:flex;flex-direction:column;gap:8px}
.sv{font-size:76px;line-height:1;color:var(--acc)}
.sl{font-size:22px;letter-spacing:.12em;color:var(--m)}
.stat p{font-size:26px;line-height:1.45;color:var(--m)}
.tbl{left:var(--px);right:var(--px);top:548px}
.tr{display:grid;grid-template-columns:200px 170px 1fr 240px 180px;gap:24px;align-items:center;padding:2px 0;border-bottom:1px solid var(--b);font-size:28px}
.tr.th{font-size:20px;letter-spacing:.1em;color:var(--m);padding:8px 0}
.mono{font-size:24px;letter-spacing:.06em}
.pf{font-size:30px}
.pf.mx{color:var(--acc)}
.track{height:6px;background:var(--b)}
.track i{display:block;height:100%;background:var(--fg2)}

/* --- 4. 비교와 표(양피지): 비교 두 칸(1px 세로선, em-dash) + 1px 줄 표 / 남중 em-dash 목록 + 코랄 따옴표 인용 --- */
.cmp{left:var(--px);width:1000px;top:330px;display:grid;grid-template-columns:1fr 1fr}
.cp{padding:0 40px 0 0}
.cp+.cp{padding:0 0 0 40px;border-left:1px solid var(--b)}
.cp h3{font-size:42px;line-height:1.2;margin-bottom:10px}
.bl{display:grid;grid-template-columns:44px 1fr;align-items:baseline;font-size:28px;line-height:1.55}
.t4{left:var(--px);width:1000px;top:600px}
.t4 .tr{grid-template-columns:1.2fr 1fr 1fr 1fr;padding:12px 0}
.t4 .tr span:first-child{font-family:'Playfair Display',var(--kt),serif}
.t4 .th span:first-child{font-family:'JetBrains Mono',var(--kb),monospace}
.lst{left:1240px;right:var(--px);top:330px;display:flex;flex-direction:column;gap:10px}
.qt{left:1240px;right:var(--px);top:600px}
.qm{display:block;font:400 150px/.6 'Playfair Display',serif;color:var(--acc)}
.qt p{margin-top:14px;font-size:40px;line-height:1.35}
.qt .kk{margin-top:14px}

/* --- 5. 확인하기(어둠): 1px 줄 퀴즈(em-dash 보기, 정답 = 코랄) | em-dash 빈칸(1px 밑줄 칸) + 낱말(코랄) --- */
.quiz{left:var(--px);width:900px;top:330px;bottom:150px;display:flex;flex-direction:column}
.q{margin-top:14px;font-size:52px;line-height:1.25}
.opts{margin-top:22px;border-top:1px solid var(--b)}
.opt{width:100%;display:grid;grid-template-columns:50px 1fr;align-items:baseline;text-align:left;padding:16px 0;border-bottom:1px solid var(--b)}
.opt .dash{font-size:32px}
.opt .ot{font-size:36px}
.opt.right{color:var(--acc)}
.opt.right .ot::after{content:'  ✓'}
.opt.wrong{color:var(--h)}
.opt:disabled:not(.right){opacity:.6}
.fb{margin-top:18px;min-height:84px;font-size:28px;line-height:1.55;color:var(--m)}
.rt{align-self:flex-start;margin-top:auto}
.blk{left:1140px;right:var(--px);top:330px;bottom:150px;padding-left:56px;border-left:1px solid var(--b);display:flex;flex-direction:column;gap:14px}
.blk .bl{font-size:30px}
.blank{position:relative;min-width:170px;margin-right:10px;padding:0 10px;border-bottom:1px solid var(--acc);font-family:'Playfair Display',var(--kt),serif;font-size:30px;color:var(--acc);text-align:center}
.blank span{visibility:hidden}
.blank::after{content:'?';position:absolute;left:0;right:0}
.blank.open span{visibility:visible}
.blank.open::after{content:none}
.wl{margin-top:auto}
.words{font-family:'Playfair Display',var(--kt),serif;font-size:42px;line-height:1.3}

/* --- 요소 등장(명세): [data-anim] + [data-delay] 계단 --- */
[data-anim]{opacity:0;transition:opacity .7s var(--spring),transform .7s var(--spring),clip-path .7s var(--spring)}
[data-anim="fade-up"]{transform:translateY(28px)}
[data-anim="scale-in"]{transform:scale(.94)}
[data-anim="reveal-right"]{opacity:1;clip-path:inset(0 100% 0 0)}
.visible [data-anim]{opacity:1;transform:none;clip-path:inset(0 0 0 0)}
[data-delay="1"]{transition-delay:.08s}[data-delay="2"]{transition-delay:.18s}[data-delay="3"]{transition-delay:.3s}[data-delay="4"]{transition-delay:.44s}[data-delay="5"]{transition-delay:.6s}[data-delay="6"]{transition-delay:.78s}

/* --- 내비: 아래 가운데 5px 점(2배 무대 = 10px, 비활성 22% · 활성 80% 1.4배) + 가는 화살표 --- */
.dk-nav{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);display:flex;align-items:center;gap:22px;--dot:255,255,255}
[data-slide="4"] .dk-nav{--dot:25,43,27}
.dk-dots{display:flex;gap:14px}
.dk-dot{width:10px;height:10px;border-radius:50%;background:rgba(var(--dot),.22)}
.dk-dot.on{background:rgba(var(--dot),.8);transform:scale(1.4)}
.dk-prev,.dk-next{width:40px;height:40px;font:300 26px/1 'JetBrains Mono',monospace;color:rgba(var(--dot),.5)}
.dk-prev:disabled,.dk-next:disabled{opacity:.25}
/* --- 6. 디자인 안내(양피지): em-dash 설명 | 1px 헤어라인 칸 시연 3개 --- */
.g6{left:var(--px);width:860px;top:320px;display:flex;flex-direction:column;gap:4px}
.g6 .kk{margin-top:8px;font-size:22px}
.g6 .bl{font-size:25px;line-height:1.45}
.g6 b{font-weight:400;color:var(--acc)}
.sw6{display:flex;flex-wrap:wrap;gap:6px 20px}
.sw6 i{display:inline-block;width:24px;height:24px;margin-right:8px;vertical-align:-5px;border:1px solid var(--fgl3)}
.sig6{left:1080px;right:var(--px);top:330px;display:flex;flex-direction:column;gap:18px}
.d6{border-top:1px solid var(--b);padding-top:12px}
.d6 .dh{display:flex;justify-content:space-between;align-items:center;gap:16px}
.d6 h3{font-size:36px}
.d6 .bt{font-size:22px;padding:8px 18px}
.d6 .bt[aria-pressed="true"]{background:var(--fgl);color:var(--bgl)}
.d6 p{margin-top:4px;font-size:22px;line-height:1.45;color:var(--m)}
.box6{margin-top:10px;height:84px;overflow:hidden;border:1px solid var(--b)}
.pan6 .trk{display:flex;width:300%;height:100%}
.pp{flex:1;display:flex;align-items:center;padding:0 24px;font:400 48px/1 'Playfair Display',serif}
.pp.dk{background:var(--bg);color:var(--fg)}.pp.lt{background:var(--bgl);color:var(--fgl)}
.pan6.play .trk{animation:gv-pan 3s both}
@keyframes gv-pan{0%{transform:none;animation-timing-function:cubic-bezier(.77,0,.175,1)}30%,50%{transform:translateX(-33.333%);animation-timing-function:cubic-bezier(.77,0,.175,1)}80%,100%{transform:translateX(-66.666%)}}
.up6{display:flex;align-items:center;gap:20px;padding:0 22px;background:var(--bg);color:var(--fg)}
.up6 .k{font:300 20px 'JetBrains Mono',monospace;letter-spacing:.2em;color:var(--acc)}
.up6 .r{display:block;width:36px;height:1px;background:var(--acc)}
.up6 b{font:400 36px 'Playfair Display',var(--kt),serif}
.up6.play .k{animation:gv-up .7s var(--spring) both}.up6.play .r{animation:gv-rv .7s var(--spring) .08s both}.up6.play b{animation:gv-up .7s var(--spring) .18s both}
@keyframes gv-up{from{opacity:0;transform:translateY(28px)}}
@keyframes gv-rv{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
.em6{display:flex;align-items:center;padding:0 24px;font:400 40px 'Playfair Display',serif}
.em6 i{font-style:normal}
.em6.play i{font-style:italic;color:var(--acc)}
[data-slide="6"] .dk-nav{--dot:25,43,27}

/* === DECK ENGINE (slide-refs 공용, 덱 HTML에 그대로 인라인됨) ===
   window.DECK_CFG = { type: 'x' | 'y' | 'fade' | 'shift' | 'rise', dur: ms, ease: css, dist: px, wheel: bool }
   window.DECK_FX  = { enter(slide, i), step(slide, k), answer(quiz, ok, btn) }  // 템플릿별 효과 훅(선택)
   - 넘김: → ↓ Space PageDown Enter = 다음(단계가 남았으면 단계 먼저), ← ↑ PageUp = 이전, Home/End
   - 내비: .dk-dot(점) .dk-count(번호, data-pad면 01 형식) .dk-prev .dk-next
   - 단계: <section data-steps="4"> 안의 [data-s="k"] 요소가 k단계부터 .on, 현재 단계는 .now
           [data-caps='["...",...]'] 요소의 글이 단계마다 바뀜, 버튼 [data-act="step"] [data-act="reset"]
   - 퀴즈: [data-quiz data-answer data-right data-wrong] 안의 버튼 [data-opt="i"], 결과 글 [data-fb], 다시 풀기 [data-act="retry"]
   - 빈칸: [data-blank] 를 누르면 .open (답 보이기/숨기기)
   - 시연(6장 디자인 안내): 버튼 [data-demo="선택자"] 를 누르면 같은 장 안의 그 요소에 .play
       data-mode="toggle" 이면 .play 켜기/끄기(aria-pressed), 아니면 .play를 뺐다 다시 붙여 애니메이션을 처음부터 다시 재생 */
```

(끝. 사용자의 수업 내용을 받아 위 지시대로 HTML을 만드세요.)
