careermate 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -10
  3. package/THIRD_PARTY_NOTICES.md +15 -0
  4. package/apps/mcp/src/index.ts +4 -5
  5. package/apps/web/demo/demo-shim.js +536 -0
  6. package/apps/web/demo/demo.css +75 -0
  7. package/apps/web/demo/seed.js +269 -0
  8. package/apps/web/public/app.js +34 -2
  9. package/apps/web/public/index.html +10 -2
  10. package/apps/web/public/lib.js +134 -22
  11. package/apps/web/public/pages/applications.js +2 -9
  12. package/apps/web/public/pages/documents.js +73 -52
  13. package/apps/web/public/pages/home.js +16 -18
  14. package/apps/web/public/pages/interview.js +9 -12
  15. package/apps/web/public/pages/jobs.js +40 -41
  16. package/apps/web/public/pages/profile.js +69 -89
  17. package/apps/web/public/pages/settings.js +15 -10
  18. package/apps/web/public/styles.css +63 -4
  19. package/apps/web/src/http.ts +31 -4
  20. package/apps/web/src/info.ts +3 -3
  21. package/apps/web/src/routes.ts +2 -1
  22. package/apps/web/src/server.ts +1 -4
  23. package/dist/doctor.mjs +72715 -697
  24. package/dist/install-page/analytics.js +97 -0
  25. package/dist/install-page/index.html +94 -112
  26. package/dist/install-page/landing.js +54 -0
  27. package/dist/install-page/og.png +0 -0
  28. package/dist/install-page/robots.txt +4 -0
  29. package/dist/install-page/sitemap.xml +21 -0
  30. package/dist/install-page/start.html +26 -46
  31. package/dist/install-page/style.css +105 -37
  32. package/dist/mcp.mjs +74044 -2062
  33. package/dist/migrate.mjs +110 -90
  34. package/dist/pdf.worker.min.mjs +28 -0
  35. package/dist/public/app.js +34 -2
  36. package/dist/public/index.html +10 -2
  37. package/dist/public/lib.js +134 -22
  38. package/dist/public/pages/applications.js +2 -9
  39. package/dist/public/pages/documents.js +73 -52
  40. package/dist/public/pages/home.js +16 -18
  41. package/dist/public/pages/interview.js +9 -12
  42. package/dist/public/pages/jobs.js +40 -41
  43. package/dist/public/pages/profile.js +69 -89
  44. package/dist/public/pages/settings.js +15 -10
  45. package/dist/public/styles.css +63 -4
  46. package/dist/web.mjs +39740 -326
  47. package/package.json +6 -3
  48. package/packages/db/src/paths.ts +11 -0
  49. package/packages/db/src/repositories.ts +18 -1
  50. package/packages/mcp-tools/src/bridge.ts +55 -32
  51. package/packages/mcp-tools/src/index.ts +9 -0
  52. package/packages/mcp-tools/src/tools.ts +198 -75
  53. package/packages/mcp-tools/src/update.ts +330 -0
  54. package/packages/parsers/src/documents.ts +533 -0
  55. package/packages/parsers/src/index.ts +8 -5
  56. package/packages/prompts/src/onboarding.ts +2 -2
  57. package/packages/shared/src/build-info.ts +22 -0
  58. package/packages/shared/src/index.ts +1 -0
  59. package/packages/shared/src/schemas.ts +138 -83
  60. package/packages/workflows/src/definitions.ts +4 -2
  61. package/scripts/build-demo.mjs +130 -0
  62. package/scripts/build-dist.mjs +26 -1
  63. package/scripts/doctor.ts +16 -3
  64. package/scripts/gen-og.mjs +1 -1
  65. package/scripts/test.ts +25 -0
  66. package/scripts/ui-smoke.ts +32 -4
  67. package/docs/ARCHITECTURE.md +0 -208
  68. package/docs/CHANGES_V1.md +0 -103
  69. package/docs/DATA_MODEL.md +0 -460
  70. package/docs/DECISIONS.md +0 -277
  71. package/docs/DEMO.md +0 -242
  72. package/docs/MCP_TOOLS.md +0 -252
  73. package/docs/ROADMAP.md +0 -134
  74. package/docs/RUNTIME_DISTRIBUTION_RESEARCH.md +0 -135
  75. package/docs/START_WORKFLOW.md +0 -131
  76. package/docs/SUPPORTED_AI_APPS.md +0 -112
  77. package/docs/TODO.md +0 -57
  78. package/docs/UX_NOTES.md +0 -247
  79. package/docs/WORKFLOWS.md +0 -200
@@ -6,7 +6,7 @@
6
6
  <script>
7
7
  (function () {
8
8
  try {
9
- var t = window.localStorage.getItem('cf-theme');
9
+ var t = window.localStorage.getItem('careermate-theme') || window.localStorage.getItem('cf-theme');
10
10
  document.documentElement.setAttribute('data-theme', t === 'light' || t === 'dark' ? t : 'dark');
11
11
  } catch (e) { document.documentElement.setAttribute('data-theme', 'dark'); }
12
12
  })();
@@ -19,6 +19,8 @@
19
19
  <meta name="theme-color" content="#14130f" media="(prefers-color-scheme: dark)" />
20
20
  <link rel="preload" href="fonts/PretendardVariable.woff2" as="font" type="font/woff2" crossorigin />
21
21
  <link rel="stylesheet" href="style.css" />
22
+ <!-- Privacy-friendly analytics for the marketing site only (never the local 127.0.0.1 app). -->
23
+ <script src="/analytics.js" defer></script>
22
24
  <style>
23
25
  /* 선택형 위저드 — start.html 전용. style.css 토큰 재사용. */
24
26
  .wizard { max-width: 760px; margin: 0 auto; }
@@ -70,13 +72,14 @@
70
72
  </nav>
71
73
 
72
74
  <main id="top">
73
- <header class="hero">
74
- <span class="pill-badge"><span class="pill-badge__dot" aria-hidden="true"></span> 처음이세요?</span>
75
- <h1 class="hero__title">터미널이 뭔지 몰라도<br /><span class="accent">괜찮아요</span></h1>
76
- <p class="hero__lede">
77
- <strong>쓰는 AI</strong>와 <strong>컴퓨터</strong>만 고르면,<br />
78
- 당신에게 맞는 설치 길 <strong>하나</strong>를 처음부터 알려드립니다.
79
- </p>
75
+ <header class="hero hero--solo">
76
+ <div class="hero__main">
77
+ <span class="pill-badge"><span class="pill-badge__dot" aria-hidden="true"></span> 처음이세요?</span>
78
+ <h1 class="hero__title">터미널이 뭔지 몰라도 괜찮아요</h1>
79
+ <p class="hero__lede">
80
+ <strong>쓰는 AI</strong>와 <strong>컴퓨터</strong>만 고르면, 당신에게 맞는 설치 길 <strong>하나</strong>를 처음부터 알려드립니다.
81
+ </p>
82
+ </div>
80
83
  </header>
81
84
 
82
85
  <section class="section" style="padding-top:0">
@@ -112,7 +115,7 @@
112
115
  <!-- ===== Claude 유료 → Code 탭 ===== -->
113
116
  <div class="path" data-sub="claude">
114
117
  <span class="path__tag">당신에겐 — Claude "Code" 탭</span>
115
- <h2>Claude 데스크톱 앱의 <span class="accent">Code 탭</span>으로</h2>
118
+ <h2>Claude 데스크톱 앱의 Code 탭으로</h2>
116
119
  <p>이미 Claude를 구독 중이라 추가 비용 없이 가장 매끄럽습니다.<br />앱 안에서 끝나 별도 터미널이 필요 없습니다.</p>
117
120
  <ol class="steps">
118
121
  <li class="step"><div class="step__no">1</div><div class="step__body">
@@ -129,13 +132,15 @@
129
132
  </div></li>
130
133
  <li class="step"><div class="step__no">4</div><div class="step__body">
131
134
  <h3>이 문장 한 번 붙여넣기</h3>
135
+ <p class="paste-note">💬 Code 탭의 <strong>대화창에</strong> 붙여넣으세요 — 터미널 명령이 아니에요. 이 문장은 공개 안내 페이지만 읽어 설치하며, <strong>당신의 이력서·개인정보는 외부로 올라가지 않아요.</strong></p>
132
136
  <div class="code"><button class="copy-btn" type="button" data-copy-target="p-claude">복사</button>
133
137
  <pre id="p-claude">https://careermate.life/llms-install.txt 를 읽고, 그 안내대로 CareerMate를 이 폴더에 설치하고 연결해줘. 데이터도 이 폴더의 .cm/data 안에 저장하고, Node 설치처럼 시스템에 바꿀 게 있으면 실행 전에 먼저 알려줘. 설치가 끝나면 앱 재시작 방법과 이력서를 주는 법, 대시보드 주소(http://127.0.0.1:4319)도 알려줘.</pre></div>
134
138
  <p class="step__tip"><strong>Node가 없어도 됩니다.</strong> 필요하면 Claude가 먼저 확인하고 설치까지 도와줍니다.<br />직접 받으려면 아래 <strong>'필요한 프로그램 받기'</strong>의 Node.js를 보세요. 설치 중 뜨는 허용 창은 눌러 주세요.</p>
135
139
  </div></li>
136
140
  <li class="step"><div class="step__no">5</div><div class="step__body">
137
141
  <h3>앱 재시작 → 확인</h3>
138
- <p>Claude 앱을 완전히 껐다 켜고(처음 한 번 MCP 승인을 허용), <strong>"커리어메이트 연결됐는지 확인해줘"</strong>라고 말하면 됩니다.</p>
142
+ <p>Claude 앱을 완전히 껐다 켜고(처음 한 번 MCP 승인 허용), <strong>"커리어메이트 연결됐는지 확인해줘"</strong>라고 말하면 끝.</p>
143
+ <p class="recover"><strong>도구가 안 보이면?</strong> 앱을 트레이까지 <strong>완전히 종료</strong>했다 다시 켜고, 처음 뜬 승인 창을 허용했는지 확인하세요. 그래도 안 되면 <a href="index.html#install">파일로 설치(.mcpb)</a>나 설치 전체의 <a href="index.html#install">문제가 생겼을 때</a>를 보세요.</p>
139
144
  </div></li>
140
145
  </ol>
141
146
  <div class="filenote">📎 <strong>이력서 넣는 법:</strong> 가장 쉬운 방법은 <strong>이력서 내용을 대화창에 붙여넣기</strong>입니다. 또는 작업 폴더에 파일을 넣고 <em>"이력서.pdf 읽어서 등록해줘"</em>라고 하거나 채팅에 직접 첨부하세요. (스캔본·보안 PDF는 안 읽힐 수 있습니다 → 그땐 텍스트로 붙여넣으세요.)</div>
@@ -145,7 +150,7 @@
145
150
  <!-- ===== ChatGPT 유료 → Codex CLI ===== -->
146
151
  <div class="path" data-sub="chatgpt">
147
152
  <span class="path__tag">당신에겐 — Codex CLI</span>
148
- <h2>ChatGPT 구독으로 <span class="accent">Codex</span>에서</h2>
153
+ <h2>ChatGPT 구독으로 Codex에서</h2>
149
154
  <p>ChatGPT는 <strong>Codex를 터미널(CLI)에서</strong> 씁니다 — 명령을 몇 줄 입력합니다.<br />Codex <strong>데스크톱 앱은 등록이 사라질 수 있어</strong> 권장하지 않습니다. 터미널이 부담되면 <a href="#" data-goto-free>터미널 없이 가능한 무료 길</a>도 있습니다.</p>
150
155
  <ol class="steps">
151
156
  <li class="step"><div class="step__no">1</div><div class="step__body">
@@ -163,13 +168,15 @@
163
168
  </div></li>
164
169
  <li class="step"><div class="step__no">3</div><div class="step__body">
165
170
  <h3>빈 폴더에서 codex 실행 → 이 문장</h3>
171
+ <p class="paste-note">💬 실행한 <strong>codex 안에</strong> 붙여넣으세요 — AI에게 보내는 메시지예요(쉘 명령이 아님). 공개 안내만 읽어 설치하며, <strong>당신의 데이터는 외부로 올라가지 않아요.</strong></p>
166
172
  <div class="code"><button class="copy-btn" type="button" data-copy-target="p-codex">복사</button>
167
173
  <pre id="p-codex">https://careermate.life/llms-install.txt 를 읽고, 그 안내대로 CareerMate를 이 폴더에 설치하고 연결해줘. 데이터도 이 폴더의 .cm/data 안에 저장하고, Node 설치처럼 시스템에 바꿀 게 있으면 실행 전에 먼저 알려줘. 설치가 끝나면 앱 재시작 방법과 이력서를 주는 법, 대시보드 주소(http://127.0.0.1:4319)도 알려줘.</pre></div>
168
174
  <p class="step__tip">설치 중 <strong>네트워크 허용</strong>을 물으면 한 번 허용하세요(npm 다운로드 때문).</p>
169
175
  </div></li>
170
176
  <li class="step"><div class="step__no">4</div><div class="step__body">
171
177
  <h3>확인</h3>
172
- <p>Codex 안에서 <code>/mcp</code>를 입력해 <code>careermate</code>가 보이면 연결된 것입니다.</p>
178
+ <p>Codex 안에서 <code>/mcp</code>를 입력해 <code>careermate</code>가 보이면 연결 성공.</p>
179
+ <p class="recover"><strong>안 보이면?</strong> <code>node --version</code>이 v22.5 이상인지, Codex를 껐다 켰는지 확인하세요. 그래도 안 되면 설치 전체의 <a href="index.html#install">문제가 생겼을 때</a>를 참고하세요.</p>
173
180
  </div></li>
174
181
  </ol>
175
182
  <div class="filenote">📎 <strong>이력서 넣는 법:</strong> 첨부 기능이 없으니, 작업 폴더에 파일을 넣고 <em>"./이력서.pdf 읽어서 등록해줘"</em>라고 하세요. PDF가 안 읽히면 내용을 <strong>텍스트로 붙여넣으세요.</strong></div>
@@ -178,12 +185,12 @@
178
185
  <!-- ===== 무료/없음 → .mcpb 1순위, Antigravity 2순위 ===== -->
179
186
  <div class="path" data-sub="free">
180
187
  <span class="path__tag">당신에겐 — 무료로 시작</span>
181
- <h2>유료 구독 없이 <span class="accent">무료로</span></h2>
188
+ <h2>유료 구독 없이 무료로</h2>
182
189
  <p>유료 구독이 없어도 됩니다. 터미널이 필요 없는, 가장 쉬운 길부터 안내합니다.</p>
183
190
 
184
191
  <div class="subcard">
185
192
  <strong>① 1순위 — Claude 데스크톱 + 파일 설치(터미널 없이)</strong>
186
- <p class="hint" style="margin:.3rem 0 .8rem">Claude 무료 계정이면 됩니다. 터미널·Node 없이 파일 하나로 끝납니다.</p>
193
+ <p class="hint" style="margin:.3rem 0 .8rem">Claude 무료 계정이면 됩니다. 터미널·Node 없이 파일 하나로 끝.</p>
187
194
  <ol class="steps">
188
195
  <li class="step"><div class="step__no">1</div><div class="step__body">
189
196
  <h3>Claude 데스크톱 설치 → 로그인(무료 OK)</h3>
@@ -199,11 +206,11 @@
199
206
  </div></li>
200
207
  <li class="step"><div class="step__no">4</div><div class="step__body">
201
208
  <h3>재시작 → 확인</h3>
202
- <p>앱을 껐다 켜고 <strong>"커리어메이트 연결됐는지 확인해줘"</strong>라고 말하세요. 도구가 보이면 됩니다.</p>
209
+ <p>앱을 껐다 켜고 <strong>"커리어메이트 연결됐는지 확인해줘"</strong>. 도구가 보이면 끝.</p>
203
210
  </div></li>
204
211
  </ol>
205
212
  <div class="filenote">📎 <strong>이력서 넣는 법:</strong> Claude 채팅창에 파일을 <strong>직접 첨부</strong>하면 됩니다(가장 쉬움).</div>
206
- <p class="hint" style="margin-top:.7rem">⚠️ 만약 <strong>도구가 안 뜨면</strong>(Claude 내장 Node 버전 문제일 수 있음) → 아래 ②를 쓰세요.</p>
213
+ <p class="hint" style="margin-top:.7rem">⚠️ <strong>도구가 안 뜨면</strong>(Claude 내장 Node 버전 문제일 수 있습니다) → 아래 ②를 쓰세요.</p>
207
214
  </div>
208
215
 
209
216
  <div class="subcard">
@@ -216,12 +223,14 @@
216
223
  </div></li>
217
224
  <li class="step"><div class="step__no">2</div><div class="step__body">
218
225
  <h3>빈 폴더 열고 → 이 문장</h3>
226
+ <p class="paste-note">💬 Antigravity의 <strong>대화창에</strong> 붙여넣으세요 — 터미널 명령이 아니에요. 공개 안내만 읽어 설치하며, <strong>당신의 데이터는 외부로 올라가지 않아요.</strong></p>
219
227
  <div class="code"><button class="copy-btn" type="button" data-copy-target="p-anti">복사</button>
220
228
  <pre id="p-anti">https://careermate.life/llms-install.txt 를 읽고, 그 안내대로 CareerMate를 이 폴더에 설치하고 연결해줘. 데이터도 이 폴더의 .cm/data 안에 저장하고, Node 설치처럼 시스템에 바꿀 게 있으면 실행 전에 먼저 알려줘. 설치가 끝나면 앱 재시작 방법과 이력서를 주는 법, 대시보드 주소(http://127.0.0.1:4319)도 알려줘.</pre></div>
221
229
  </div></li>
222
230
  <li class="step"><div class="step__no">3</div><div class="step__body">
223
231
  <h3>확인</h3>
224
232
  <p>연결 상태를 확인하고(보통 <code>/mcp</code> 또는 "연결됐는지 확인해줘") 끝.</p>
233
+ <p class="recover"><strong>안 보이면?</strong> 앱을 완전히 껐다 켜고 다시 시도하세요. 막히면 설치 전체의 <a href="index.html#install">문제가 생겼을 때</a>를 보세요.</p>
225
234
  </div></li>
226
235
  </ol>
227
236
  <div class="filenote">📎 <strong>이력서 넣는 법:</strong> 작업 폴더에 파일을 넣고 파일명으로 지시. 안 읽히면 텍스트로 붙여넣기.</div>
@@ -275,39 +284,10 @@
275
284
 
276
285
  <div class="sr-only" role="status" aria-live="polite" id="copy-status"></div>
277
286
 
287
+ <script src="landing.js"></script>
278
288
  <script>
279
289
  (function () {
280
290
  'use strict';
281
- var status = document.getElementById('copy-status');
282
- // --- clipboard ---
283
- function legacyCopy(text) {
284
- var ta = document.createElement('textarea');
285
- ta.value = text; ta.setAttribute('readonly', ''); ta.style.position = 'absolute'; ta.style.left = '-9999px';
286
- document.body.appendChild(ta); ta.select();
287
- var ok = false; try { ok = document.execCommand('copy'); } catch (e) { ok = false; }
288
- document.body.removeChild(ta); return ok;
289
- }
290
- function copyText(text, onok) {
291
- if (navigator.clipboard && navigator.clipboard.writeText) {
292
- navigator.clipboard.writeText(text).then(onok, function () { if (legacyCopy(text)) onok(); });
293
- } else if (legacyCopy(text)) { onok(); }
294
- }
295
- document.querySelectorAll('.copy-btn').forEach(function (btn) {
296
- btn.addEventListener('click', function () {
297
- var src = document.getElementById(btn.getAttribute('data-copy-target'));
298
- if (!src) return;
299
- copyText(src.textContent || '', function () {
300
- var o = btn.textContent; btn.textContent = '복사됨'; btn.classList.add('copied');
301
- if (status) status.textContent = '클립보드에 복사되었습니다.';
302
- window.setTimeout(function () { btn.textContent = o; btn.classList.remove('copied'); }, 1600);
303
- });
304
- });
305
- });
306
-
307
- // --- theme toggle ---
308
- var root = document.documentElement, toggle = document.getElementById('theme-toggle');
309
- function isDark() { var t = root.getAttribute('data-theme'); if (t === 'dark') return true; if (t === 'light') return false; return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; }
310
- if (toggle) toggle.addEventListener('click', function () { var n = isDark() ? 'light' : 'dark'; root.setAttribute('data-theme', n); try { window.localStorage.setItem('cf-theme', n); } catch (e) {} });
311
291
 
312
292
  // --- wizard ---
313
293
  var result = document.getElementById('result');
@@ -109,6 +109,19 @@ a:hover { text-decoration: underline; }
109
109
  code { font-family: var(--mono); }
110
110
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
111
111
  ::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); }
112
+ /* skip link — hidden until focused, then drops into the top-left */
113
+ .skip-link { position: fixed; top: 8px; left: 8px; z-index: 200; transform: translateY(-200%);
114
+ background: var(--accent); color: var(--text-on-accent); padding: 8px 14px; border-radius: var(--radius-sm);
115
+ font-weight: 600; transition: transform .15s; }
116
+ .skip-link:focus { transform: none; outline: 2px solid var(--text-on-accent); outline-offset: 2px; }
117
+ /* agent-prompt paste boxes: a caption so they read as "message to the AI", not a shell command,
118
+ plus the privacy reassurance at the highest-anxiety paste moment */
119
+ .paste-note { font-size: .86rem; line-height: 1.55; color: var(--text-secondary);
120
+ background: var(--accent-soft); border: 1px solid var(--accent-border); border-radius: var(--radius-sm);
121
+ padding: .55rem .8rem; margin: 0 0 .5rem; }
122
+ .paste-note strong { color: var(--text); font-weight: 650; }
123
+ .recover { font-size: .88rem; line-height: 1.55; color: var(--text-secondary); margin: .5rem 0 0; }
124
+ .recover strong { color: var(--text); }
112
125
 
113
126
  .accent { color: var(--accent); }
114
127
  .nowrap { white-space: nowrap; }
@@ -170,73 +183,117 @@ code { font-family: var(--mono); }
170
183
  .btn--primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
171
184
 
172
185
  /* ----------------------------------------------------------------- hero */
173
- /* single centered column — headline on top, the two install CTAs right below. */
186
+ /* Asymmetric two columns: headline + CTAs on the left, the "한 문장" install
187
+ demo (the signature element) on the right. Left-aligned, not centered.
188
+ Stacks to one column on narrow screens. */
174
189
  .hero {
175
- max-width: 720px; margin: 0 auto; padding: 66px 24px 8px; text-align: center;
190
+ max-width: var(--maxw); margin: 0 auto; padding: 72px 24px 8px;
191
+ display: grid; grid-template-columns: 1.02fr 0.98fr; gap: 52px; align-items: start;
176
192
  }
193
+ .hero__main { min-width: 0; }
194
+ /* solo variant (start.html): no demo column — one left-aligned column */
195
+ .hero--solo { grid-template-columns: 1fr; max-width: 760px; }
177
196
  /* gentle staggered entrance on load */
178
197
  @keyframes riseIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
179
- .hero > * { animation: riseIn .55s cubic-bezier(.22,.61,.36,1) both; }
198
+ .hero__main > *, .hero__demo { animation: riseIn .55s cubic-bezier(.22,.61,.36,1) both; }
180
199
  .hero__title { animation-delay: .04s; }
181
200
  .hero__lede { animation-delay: .10s; }
182
201
  .hero__cta { animation-delay: .18s; }
183
202
  .hero__note { animation-delay: .24s; }
203
+ .hero__demo { animation-delay: .16s; }
184
204
  .pill-badge {
185
205
  display: inline-flex; align-items: center; gap: 8px;
186
206
  padding: 5px 13px; border-radius: 999px; font-size: 12.5px; font-weight: 600;
187
207
  color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border);
188
208
  }
189
209
  .pill-badge__dot { width: 6px; height: 6px; border-radius: 999px; background: var(--accent); }
190
- .hero__title { font-size: clamp(2.05rem, 5vw, 3rem); margin: 20px 0 0; letter-spacing: -0.028em; }
210
+ /* heavy display weight + tight tracking — strong contrast against the 400 lede */
211
+ .hero__title { font-size: clamp(2.15rem, 4.6vw, 3.1rem); font-weight: 800; margin: 20px 0 0; letter-spacing: -0.035em; line-height: 1.1; }
191
212
  .hero__lede {
192
- font-size: clamp(1rem, 2vw, 1.12rem); color: var(--text-secondary);
193
- margin: 18px auto 0; max-width: 37rem;
213
+ font-size: clamp(1.02rem, 1.7vw, 1.18rem); color: var(--text-secondary);
214
+ margin: 18px 0 0; max-width: 34rem; line-height: 1.6;
194
215
  }
195
216
  .hero__lede strong { color: var(--text); font-weight: 650; }
196
217
 
218
+ /* trust strip — the four anxiety answers, scannable, restrained (accent only on the ✓) */
219
+ .trust-strip {
220
+ list-style: none; margin: 20px 0 0; padding: 0; max-width: 38rem;
221
+ display: flex; flex-wrap: wrap; gap: 8px;
222
+ }
223
+ .trust-strip li {
224
+ display: inline-flex; align-items: center; gap: 6px;
225
+ font-size: 12.5px; font-weight: 500; color: var(--text-secondary);
226
+ background: var(--surface); border: 1px solid var(--border);
227
+ padding: 5px 11px; border-radius: 999px;
228
+ }
229
+ .trust-strip li::before { content: "✓"; color: var(--accent); font-weight: 700; }
230
+
197
231
  /* hero call-to-action — two install paths */
198
- .hero__cta { margin: 28px auto 0; display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
232
+ .hero__cta { margin: 26px 0 0; display: flex; flex-wrap: wrap; gap: 10px; }
199
233
  .hero__cta .btn { height: 42px; padding: 0 18px; font-size: 14.5px; }
200
234
 
201
235
  .hero__note {
202
- margin: 18px auto 0; max-width: 38rem; font-size: 12.5px; line-height: 1.6;
236
+ margin: 20px 0 0; max-width: 36rem; font-size: 12.5px; line-height: 1.65;
203
237
  color: var(--text-tertiary);
204
238
  }
205
239
  .hero__note strong { color: var(--text-secondary); font-weight: 600; }
206
240
  .hero__note code { font-family: var(--mono); font-size: .92em; background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; color: var(--text-secondary); }
207
241
 
208
- /* ----------------------------------------------------------------- sections */
209
- .section { max-width: var(--maxw); margin: 0 auto; padding: 112px 24px 0; scroll-margin-top: 80px; }
210
- .section__head { max-width: 46rem; margin: 0 auto 34px; text-align: center; }
211
- .eyebrow {
212
- display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: .09em;
213
- text-transform: uppercase; color: var(--accent); margin-bottom: 9px;
214
- }
215
- .section__head h2 { font-size: clamp(1.4rem, 3vw, 1.9rem); }
216
- .section__head p { margin-top: 11px; color: var(--text-secondary); font-size: 1rem; }
217
-
218
- /* how-it-works */
219
- .cards-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
220
- .feature {
242
+ /* signature — the "내 AI에게 한 문장" install demo (the one bold idea on the page).
243
+ nudged down so its top sits around the headline, not the small pill kicker. */
244
+ .hero__demo { min-width: 0; margin-top: 40px; }
245
+ .hero__demo-note { margin: 12px 2px 0; font-size: 12.5px; line-height: 1.55; color: var(--text-tertiary); }
246
+ .chat {
221
247
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
222
- padding: 22px 20px; transition: border-color .15s;
248
+ box-shadow: var(--shadow-md); overflow: hidden;
223
249
  }
224
- .feature:hover { border-color: var(--border-strong); }
225
- .feature__icon {
226
- width: 40px; height: 40px; border-radius: 9px; display: grid; place-items: center; margin-bottom: 14px;
227
- color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border);
250
+ .chat__head {
251
+ display: flex; align-items: center; gap: 8px; padding: 10px 15px;
252
+ font-family: var(--mono); font-size: 12px; color: var(--text-tertiary);
253
+ background: var(--bg-subtle); border-bottom: 1px solid var(--border);
254
+ }
255
+ .chat__dot { width: 8px; height: 8px; border-radius: 999px; background: var(--accent); flex: none; }
256
+ .chat__msg { padding: 15px 16px; }
257
+ .chat__msg + .chat__msg { border-top: 1px solid var(--border); }
258
+ .chat__who { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 7px; }
259
+ .chat__msg--me p {
260
+ font-family: var(--mono); font-size: 12.5px; line-height: 1.6; color: var(--code-text);
261
+ background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 11px 13px;
228
262
  }
229
- .feature__icon svg { width: 20px; height: 20px; }
230
- .feature h3 { font-size: 16px; margin-bottom: 6px; }
231
- .feature p { color: var(--text-secondary); font-size: 14px; }
263
+ .chat__msg--ai p { color: var(--text-secondary); font-size: 14px; }
264
+ .chat__steps { list-style: none; margin: 11px 0 0; padding: 0; display: grid; gap: 6px; }
265
+ .chat__steps li { display: flex; gap: 8px; font-size: 12.5px; color: var(--text-secondary); }
266
+ .chat__steps li::before { content: "✓"; color: var(--accent); font-weight: 700; flex: none; }
267
+ .chat__steps code { font-family: var(--mono); font-size: .92em; background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; color: var(--text); }
268
+
269
+ /* ----------------------------------------------------------------- sections */
270
+ .section { max-width: var(--maxw); margin: 0 auto; padding: 112px 24px 0; scroll-margin-top: 80px; }
271
+ /* left-aligned section heads (centered heads + uppercase eyebrows read as a template) */
272
+ .section__head { max-width: 50rem; margin: 0 0 36px; }
273
+ .section__head h2 { font-size: clamp(1.55rem, 3vw, 2.1rem); font-weight: 750; letter-spacing: -0.02em; }
274
+ .section__head p { margin-top: 12px; color: var(--text-secondary); font-size: 1.02rem; max-width: 42rem; }
275
+ .section__cta { margin-top: 22px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
276
+ .section__cta-note { font-size: 13px; color: var(--text-tertiary, var(--text-secondary)); }
277
+ /* Centered head for showcase/guide sections whose body is itself centered (the big
278
+ carousel screenshot, the reading-column install guide) — a left head would misalign
279
+ with the centered media below it. Left-aligned heads stay on the content sections above. */
280
+ .section__head--center { text-align: center; margin-left: auto; margin-right: auto; }
281
+ .section__head--center p { margin-left: auto; margin-right: auto; }
282
+ .section__head--center .section__cta { align-items: center; }
283
+
284
+ /* how-it-works — a quiet numbered sequence, not an icon-card grid */
285
+ .cards-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; }
286
+ .feature { padding-top: 16px; border-top: 1px solid var(--border-strong); }
287
+ .feature__no { display: block; font-family: var(--mono); font-size: 13px; font-weight: 700; letter-spacing: .06em; color: var(--accent); }
288
+ .feature h3 { font-size: 16px; margin: 12px 0 6px; }
289
+ .feature p { color: var(--text-secondary); font-size: 14px; line-height: 1.6; }
232
290
  .feature code { font-family: var(--mono); font-size: .85em; background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; }
233
291
 
234
- /* checklist */
235
- .checks { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; max-width: 840px; margin: 0 auto; }
292
+ /* capability list — hairline rows, not boxes ("use fewer borders") */
293
+ .checks { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 40px; max-width: 880px; margin: 0; }
236
294
  .check {
237
295
  display: flex; gap: 12px; align-items: flex-start;
238
- background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
239
- padding: 15px 17px;
296
+ padding: 16px 0; border-bottom: 1px solid var(--border);
240
297
  }
241
298
  .check > svg { width: 18px; height: 18px; flex: none; color: var(--accent); margin-top: 1px; }
242
299
  .check strong { display: block; font-size: 14.5px; font-weight: 650; }
@@ -281,9 +338,12 @@ code { font-family: var(--mono); }
281
338
  :root[data-theme='dark'] .shot__img--dark { display: block; }
282
339
  :root:not([data-theme='dark']) .shot__img--light { display: block; }
283
340
 
284
- /* prev / next arrows — overlap the frame edges, vertically centred on the frame */
341
+ /* prev / next arrows — overlap the frame edges, vertically centred on the FRAME
342
+ (not the frame+caption). top:0 / bottom:caption-height + margin-block:auto centers
343
+ the arrow within the image area regardless of the responsive frame height — no
344
+ eyeballed percentage. ~36px ≈ figcaption line (13.5px ×1.65) + its 14px margin-top. */
285
345
  .carousel__arrow {
286
- position: absolute; top: 44%; transform: translateY(-50%);
346
+ position: absolute; top: 0; bottom: 36px; margin-block: auto;
287
347
  width: 44px; height: 44px; border-radius: 999px; display: grid; place-items: center;
288
348
  background: var(--surface); border: 1px solid var(--border-strong); color: var(--text);
289
349
  box-shadow: var(--shadow-md); cursor: pointer; z-index: 2;
@@ -440,6 +500,7 @@ kbd {
440
500
  .privacy-card__list li { font-size: 13.5px; color: var(--text-secondary); }
441
501
  .privacy-card__list strong { color: var(--text); margin-right: 8px; }
442
502
  .privacy-card__list code { font-family: var(--mono); font-size: .86em; background: var(--bg-sunken); padding: 1px 6px; border-radius: 4px; color: var(--text); }
503
+ .privacy-card__note { margin: 16px 0 0; padding-top: 14px; border-top: 1px solid var(--border); font-size: 12.5px; color: var(--text-tertiary); line-height: 1.6; }
443
504
 
444
505
  /* footer */
445
506
  .foot {
@@ -467,10 +528,17 @@ kbd {
467
528
 
468
529
  /* responsive */
469
530
  @media (max-width: 820px) {
470
- .cards-3 { grid-template-columns: 1fr; }
531
+ .hero { grid-template-columns: 1fr; gap: 32px; padding-top: 56px; }
532
+ .hero__demo { max-width: 480px; margin-top: 0; }
533
+ .cards-3 { grid-template-columns: 1fr; gap: 24px; }
471
534
  .checks { grid-template-columns: 1fr; }
472
535
  .trouble { grid-template-columns: 1fr; }
473
- .nav__links { display: none; }
536
+ /* Logo-only brand on mobile frees room to keep the guided wizard ('처음이세요?')
537
+ in the sticky nav without overflow; section anchors collapse, and '설치하기'
538
+ stays in nav__actions so install is never lost. */
539
+ .nav__name { display: none; }
540
+ .nav__links a { display: none; }
541
+ .nav__links a[href="start.html"] { display: inline-flex; }
474
542
  }
475
543
  @media (max-width: 540px) {
476
544
  .hero { padding-top: 46px; }