careermate 0.0.1

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 (137) hide show
  1. package/AGENTS.md +33 -0
  2. package/INSTALL.md +230 -0
  3. package/LICENSE +21 -0
  4. package/README.md +532 -0
  5. package/THIRD_PARTY_NOTICES.md +57 -0
  6. package/bin/careermate.mjs +77 -0
  7. package/dist/career-os/README.md +78 -0
  8. package/dist/career-os/eop/cover-letter.md +46 -0
  9. package/dist/career-os/eop/fit-analysis.md +48 -0
  10. package/dist/career-os/eop/interview-prep.md +39 -0
  11. package/dist/career-os/eop/job-analysis.md +42 -0
  12. package/dist/career-os/eop/profile-extraction.md +37 -0
  13. package/dist/career-os/knowledge/ats.md +131 -0
  14. package/dist/career-os/knowledge/company-research.md +137 -0
  15. package/dist/career-os/knowledge/cover-letter.md +135 -0
  16. package/dist/career-os/knowledge/fit-matching.md +121 -0
  17. package/dist/career-os/knowledge/human-writing.md +118 -0
  18. package/dist/career-os/knowledge/interview-behavioral.md +132 -0
  19. package/dist/career-os/knowledge/interview-technical.md +126 -0
  20. package/dist/career-os/knowledge/linkedin-profile.md +152 -0
  21. package/dist/career-os/knowledge/networking-referrals.md +166 -0
  22. package/dist/career-os/knowledge/offer-evaluation-decision.md +121 -0
  23. package/dist/career-os/knowledge/onboarding-first-90-days.md +126 -0
  24. package/dist/career-os/knowledge/portfolio.md +121 -0
  25. package/dist/career-os/knowledge/recruiter-screen.md +105 -0
  26. package/dist/career-os/knowledge/rejection-triage-iteration.md +115 -0
  27. package/dist/career-os/knowledge/resume.md +123 -0
  28. package/dist/career-os/knowledge/salary-negotiation.md +123 -0
  29. package/dist/career-os/knowledge/verifiers/ats-compat.md +64 -0
  30. package/dist/career-os/knowledge/verifiers/consistency.md +61 -0
  31. package/dist/career-os/knowledge/verifiers/human-voice.md +82 -0
  32. package/dist/career-os/knowledge/verifiers/recency-staleness.md +72 -0
  33. package/dist/career-os/knowledge/verifiers/responsiveness-on-target.md +82 -0
  34. package/dist/career-os/knowledge/verifiers/truthfulness.md +67 -0
  35. package/dist/doctor.mjs +79094 -0
  36. package/dist/mcp.mjs +96108 -0
  37. package/dist/migrate.mjs +4735 -0
  38. package/dist/pdf.worker.min.mjs +28 -0
  39. package/dist/public/app.js +150 -0
  40. package/dist/public/fonts/PretendardVariable.woff2 +0 -0
  41. package/dist/public/icon-180.png +0 -0
  42. package/dist/public/index.html +52 -0
  43. package/dist/public/lib.js +394 -0
  44. package/dist/public/pages/applications.js +107 -0
  45. package/dist/public/pages/documents.js +467 -0
  46. package/dist/public/pages/home.js +261 -0
  47. package/dist/public/pages/interview.js +227 -0
  48. package/dist/public/pages/jobs.js +493 -0
  49. package/dist/public/pages/profile.js +556 -0
  50. package/dist/public/pages/settings.js +240 -0
  51. package/dist/public/styles.css +498 -0
  52. package/dist/site/analytics.js +97 -0
  53. package/dist/site/favicon.svg +1 -0
  54. package/dist/site/fonts/PretendardVariable.woff2 +0 -0
  55. package/dist/site/icon-180.png +0 -0
  56. package/dist/site/icon-512.png +0 -0
  57. package/dist/site/index.html +598 -0
  58. package/dist/site/landing.js +54 -0
  59. package/dist/site/llms-install.txt +217 -0
  60. package/dist/site/robots.txt +4 -0
  61. package/dist/site/sitemap.xml +21 -0
  62. package/dist/site/start.html +326 -0
  63. package/dist/site/style.css +550 -0
  64. package/dist/site/vercel.json +20 -0
  65. package/dist/web.mjs +46852 -0
  66. package/docs/FAQ.md +101 -0
  67. package/docs/MCP_TOOLS.md +311 -0
  68. package/docs/README.md +28 -0
  69. package/docs/START_WORKFLOW.md +137 -0
  70. package/docs/SUPPORTED_AI_APPS.md +112 -0
  71. package/docs/WORKFLOWS.md +202 -0
  72. package/docs/career-os/README.md +78 -0
  73. package/docs/career-os/eop/cover-letter.md +46 -0
  74. package/docs/career-os/eop/fit-analysis.md +48 -0
  75. package/docs/career-os/eop/interview-prep.md +39 -0
  76. package/docs/career-os/eop/job-analysis.md +42 -0
  77. package/docs/career-os/eop/profile-extraction.md +37 -0
  78. package/docs/career-os/knowledge/ats.md +131 -0
  79. package/docs/career-os/knowledge/company-research.md +137 -0
  80. package/docs/career-os/knowledge/cover-letter.md +135 -0
  81. package/docs/career-os/knowledge/fit-matching.md +121 -0
  82. package/docs/career-os/knowledge/human-writing.md +118 -0
  83. package/docs/career-os/knowledge/interview-behavioral.md +132 -0
  84. package/docs/career-os/knowledge/interview-technical.md +126 -0
  85. package/docs/career-os/knowledge/linkedin-profile.md +152 -0
  86. package/docs/career-os/knowledge/networking-referrals.md +166 -0
  87. package/docs/career-os/knowledge/offer-evaluation-decision.md +121 -0
  88. package/docs/career-os/knowledge/onboarding-first-90-days.md +126 -0
  89. package/docs/career-os/knowledge/portfolio.md +121 -0
  90. package/docs/career-os/knowledge/recruiter-screen.md +105 -0
  91. package/docs/career-os/knowledge/rejection-triage-iteration.md +115 -0
  92. package/docs/career-os/knowledge/resume.md +123 -0
  93. package/docs/career-os/knowledge/salary-negotiation.md +123 -0
  94. package/docs/career-os/knowledge/verifiers/ats-compat.md +64 -0
  95. package/docs/career-os/knowledge/verifiers/consistency.md +61 -0
  96. package/docs/career-os/knowledge/verifiers/human-voice.md +82 -0
  97. package/docs/career-os/knowledge/verifiers/recency-staleness.md +72 -0
  98. package/docs/career-os/knowledge/verifiers/responsiveness-on-target.md +82 -0
  99. package/docs/career-os/knowledge/verifiers/truthfulness.md +67 -0
  100. package/package.json +89 -0
  101. package/packages/core/src/context.ts +74 -0
  102. package/packages/core/src/index.ts +8 -0
  103. package/packages/core/src/onboarding.ts +81 -0
  104. package/packages/core/src/services.ts +187 -0
  105. package/packages/core/src/summary.ts +104 -0
  106. package/packages/db/src/connection.ts +76 -0
  107. package/packages/db/src/index.ts +23 -0
  108. package/packages/db/src/paths.ts +52 -0
  109. package/packages/db/src/repositories.ts +891 -0
  110. package/packages/db/src/runtime.ts +58 -0
  111. package/packages/db/src/schema.ts +307 -0
  112. package/packages/exporters/src/html.ts +110 -0
  113. package/packages/exporters/src/index.ts +364 -0
  114. package/packages/exporters/src/markdown.ts +178 -0
  115. package/packages/knowledge/src/index.ts +168 -0
  116. package/packages/mcp-tools/src/bridge.ts +106 -0
  117. package/packages/mcp-tools/src/index.ts +17 -0
  118. package/packages/mcp-tools/src/result.ts +49 -0
  119. package/packages/mcp-tools/src/tools.ts +747 -0
  120. package/packages/mcp-tools/src/update.ts +330 -0
  121. package/packages/parsers/src/documents.ts +533 -0
  122. package/packages/parsers/src/html.ts +86 -0
  123. package/packages/parsers/src/index.ts +231 -0
  124. package/packages/parsers/src/keywords.ts +151 -0
  125. package/packages/prompts/src/humanize.ts +59 -0
  126. package/packages/prompts/src/index.ts +82 -0
  127. package/packages/prompts/src/install.ts +43 -0
  128. package/packages/prompts/src/onboarding.ts +35 -0
  129. package/packages/prompts/src/system.ts +57 -0
  130. package/packages/shared/src/build-info.ts +22 -0
  131. package/packages/shared/src/enums.ts +142 -0
  132. package/packages/shared/src/index.ts +19 -0
  133. package/packages/shared/src/schemas.ts +453 -0
  134. package/packages/workflows/src/definitions.ts +109 -0
  135. package/packages/workflows/src/index.ts +39 -0
  136. package/scripts/init.ts +371 -0
  137. package/tsconfig.json +32 -0
@@ -0,0 +1,550 @@
1
+ /* =============================================================================
2
+ CareerMate — public landing / install page.
3
+ Self-contained: no external fonts, no CDN, no network (works fully offline).
4
+
5
+ Design language synthesized from established install/registry sites
6
+ (Homebrew, Bun, npm, MCP docs), not copied from any one:
7
+ · restraint over decoration — one flat accent, warm-neutral paper, no
8
+ gradients / glows / gradient-text
9
+ · the two install paths (.mcpb one-click / terminal) are the focal point
10
+ · system sans for prose, monospace for commands → reads as a real tool
11
+ · trust through whitespace, hairlines and consistency
12
+ Light + dark (system preference or manual toggle).
13
+ ========================================================================== */
14
+
15
+ /* Self-hosted brand font — the SAME face the dashboard ships (apps/web), so the
16
+ landing prose and the embedded dashboard screenshots read as one product.
17
+ Without this the page silently falls back to Segoe UI / 맑은 고딕 on machines
18
+ that don't have Pretendard installed. */
19
+ @font-face {
20
+ font-family: 'Pretendard Variable';
21
+ font-weight: 45 920;
22
+ font-style: normal;
23
+ font-display: swap;
24
+ /* relative URL: resolves under the site root (Vercel) AND under /install/ when
25
+ the local dashboard serves this same page. */
26
+ src: local('Pretendard Variable'),
27
+ url('fonts/PretendardVariable.woff2') format('woff2');
28
+ }
29
+
30
+ :root {
31
+ /* warm paper neutrals (light) */
32
+ --bg: #faf8f4;
33
+ --bg-elev: #ffffff;
34
+ --bg-subtle: #f2efe8;
35
+ --bg-sunken: #ece8df;
36
+ --surface: #ffffff;
37
+ --surface-hover: #f5f2ec;
38
+ --border: #e6e1d6;
39
+ --border-strong: #d6d0c2;
40
+ --text: #1c1a16;
41
+ --text-secondary: #595449;
42
+ --text-tertiary: #8b867a;
43
+
44
+ /* single flat accent — deep emerald (growth / "proceed"), no gradient */
45
+ --accent: #0f7256;
46
+ --accent-hover: #0b5b44;
47
+ --accent-soft: #e7f1ec;
48
+ --accent-border: #bcdccd;
49
+ --text-on-accent: #ffffff;
50
+
51
+ --code-bg: #f4f1ea;
52
+ --code-text: #2a2722;
53
+ --warn-bg: #fbf3e3;
54
+ --warn-border: #ecd9ac;
55
+ --warn-text: #876214;
56
+
57
+ --shadow-sm: 0 1px 2px rgba(28,26,22,.05);
58
+ --shadow-md: 0 4px 14px rgba(28,26,22,.07);
59
+
60
+ --radius-sm: 6px;
61
+ --radius: 9px;
62
+ --radius-lg: 12px;
63
+ --maxw: 1000px;
64
+
65
+ --font: 'Pretendard Variable', Pretendard, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Malgun Gothic',
66
+ 'Apple SD Gothic Neo', 'Noto Sans KR', Roboto, sans-serif;
67
+ --mono: 'Cascadia Code', 'SFMono-Regular', 'JetBrains Mono', Consolas, 'D2Coding', ui-monospace, monospace;
68
+ }
69
+
70
+ @media (prefers-color-scheme: dark) {
71
+ :root:not([data-theme='light']) { color-scheme: dark; }
72
+ }
73
+ :root[data-theme='dark'] { color-scheme: dark; }
74
+
75
+ @media (prefers-color-scheme: dark) {
76
+ :root:not([data-theme='light']) {
77
+ --bg: #14130f; --bg-elev: #1b1915; --bg-subtle: #201d17; --bg-sunken: #100f0b;
78
+ --surface: #1b1915; --surface-hover: #262219;
79
+ --border: #2e2a22; --border-strong: #3e3930;
80
+ --text: #f2eee5; --text-secondary: #aaa599; --text-tertiary: #79746a;
81
+ --accent: #34d399; --accent-hover: #6ee7b7; --accent-soft: #11281d; --accent-border: #1f4f3c; --text-on-accent: #05241a;
82
+ --code-bg: #100f0b; --code-text: #d6d1c6;
83
+ --warn-bg: #292008; --warn-border: #574510; --warn-text: #e6c578;
84
+ --shadow-sm: 0 1px 2px rgba(0,0,0,.4); --shadow-md: 0 4px 14px rgba(0,0,0,.45);
85
+ }
86
+ }
87
+ :root[data-theme='dark'] {
88
+ --bg: #14130f; --bg-elev: #1b1915; --bg-subtle: #201d17; --bg-sunken: #100f0b;
89
+ --surface: #1b1915; --surface-hover: #262219;
90
+ --border: #2e2a22; --border-strong: #3e3930;
91
+ --text: #f2eee5; --text-secondary: #aaa599; --text-tertiary: #79746a;
92
+ --accent: #34d399; --accent-hover: #6ee7b7; --accent-soft: #11281d; --accent-border: #1f4f3c;
93
+ --code-bg: #100f0b; --code-text: #d6d1c6;
94
+ --warn-bg: #292008; --warn-border: #574510; --warn-text: #e6c578;
95
+ --shadow-sm: 0 1px 2px rgba(0,0,0,.4); --shadow-md: 0 4px 14px rgba(0,0,0,.45);
96
+ }
97
+
98
+ * { box-sizing: border-box; }
99
+ html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
100
+ body {
101
+ margin: 0; background: var(--bg); color: var(--text);
102
+ font-family: var(--font); font-size: 16px; line-height: 1.65;
103
+ word-break: keep-all; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
104
+ }
105
+ h1, h2, h3, h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; line-height: 1.25; }
106
+ p { margin: 0; }
107
+ a { color: var(--accent); text-decoration: none; }
108
+ a:hover { text-decoration: underline; }
109
+ code { font-family: var(--mono); }
110
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
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); }
125
+
126
+ .accent { color: var(--accent); }
127
+ .nowrap { white-space: nowrap; }
128
+
129
+ /* ----------------------------------------------------------------- nav */
130
+ .nav {
131
+ position: sticky; top: 0; z-index: 50;
132
+ display: flex; align-items: center; gap: 18px;
133
+ max-width: var(--maxw); margin: 0 auto; padding: 15px 24px;
134
+ background: color-mix(in srgb, var(--bg) 85%, transparent);
135
+ backdrop-filter: saturate(120%) blur(8px);
136
+ border-bottom: 1px solid var(--border);
137
+ }
138
+ .nav__brand { display: inline-flex; align-items: center; gap: 9px; color: var(--text); font-weight: 700; }
139
+ .nav__brand:hover { text-decoration: none; }
140
+ .nav__logo {
141
+ width: 24px; height: 24px; border-radius: 6px; flex: none;
142
+ background: var(--accent); color: var(--text-on-accent); font-weight: 800; font-size: 13px;
143
+ display: grid; place-items: center;
144
+ }
145
+ .nav__name { font-size: 15.5px; font-family: var(--mono); letter-spacing: -0.02em; }
146
+ .nav__links { display: flex; gap: 2px; margin-left: 12px; }
147
+ .nav__links a {
148
+ color: var(--text-secondary); font-size: 14px; font-weight: 500;
149
+ padding: 6px 10px; border-radius: var(--radius-sm);
150
+ }
151
+ .nav__links a:hover { color: var(--text); background: var(--surface-hover); text-decoration: none; }
152
+ .nav__actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
153
+
154
+ .theme-toggle {
155
+ width: 34px; height: 34px; display: grid; place-items: center;
156
+ border: 1px solid var(--border-strong); background: var(--surface); border-radius: var(--radius-sm);
157
+ color: var(--text-secondary); cursor: pointer; transition: background .15s, color .15s;
158
+ }
159
+ .theme-toggle:hover { background: var(--surface-hover); color: var(--text); }
160
+ .theme-toggle svg { width: 16px; height: 16px; }
161
+ .theme-toggle .i-moon { display: none; }
162
+ .theme-toggle .i-sun { display: block; }
163
+ :root[data-theme='dark'] .theme-toggle .i-moon { display: block; }
164
+ :root[data-theme='dark'] .theme-toggle .i-sun { display: none; }
165
+ @media (prefers-color-scheme: dark) {
166
+ :root:not([data-theme='light']) .theme-toggle .i-moon { display: block; }
167
+ :root:not([data-theme='light']) .theme-toggle .i-sun { display: none; }
168
+ }
169
+
170
+ /* ----------------------------------------------------------------- buttons */
171
+ .btn {
172
+ display: inline-flex; align-items: center; justify-content: center; gap: 8px;
173
+ height: 38px; padding: 0 15px; border-radius: var(--radius-sm);
174
+ border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
175
+ font-family: inherit; font-size: 14px; font-weight: 600; cursor: pointer; white-space: nowrap;
176
+ transition: background .15s, border-color .15s;
177
+ }
178
+ .btn:hover { background: var(--surface-hover); text-decoration: none; }
179
+ .btn svg { width: 16px; height: 16px; flex: none; }
180
+ .btn--ghost { background: transparent; border-color: var(--border); color: var(--text-secondary); }
181
+ .btn--ghost:hover { color: var(--text); background: var(--surface-hover); }
182
+ .btn--primary { background: var(--accent); border-color: var(--accent); color: var(--text-on-accent); }
183
+ .btn--primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
184
+
185
+ /* ----------------------------------------------------------------- hero */
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. */
189
+ .hero {
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;
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; }
196
+ /* gentle staggered entrance on load */
197
+ @keyframes riseIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
198
+ .hero__main > *, .hero__demo { animation: riseIn .55s cubic-bezier(.22,.61,.36,1) both; }
199
+ .hero__title { animation-delay: .04s; }
200
+ .hero__lede { animation-delay: .10s; }
201
+ .hero__cta { animation-delay: .18s; }
202
+ .hero__note { animation-delay: .24s; }
203
+ .hero__demo { animation-delay: .16s; }
204
+ .pill-badge {
205
+ display: inline-flex; align-items: center; gap: 8px;
206
+ padding: 5px 13px; border-radius: 999px; font-size: 12.5px; font-weight: 600;
207
+ color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border);
208
+ }
209
+ .pill-badge__dot { width: 6px; height: 6px; border-radius: 999px; background: var(--accent); }
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; }
212
+ .hero__lede {
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;
215
+ }
216
+ .hero__lede strong { color: var(--text); font-weight: 650; }
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
+
231
+ /* hero call-to-action — two install paths */
232
+ .hero__cta { margin: 26px 0 0; display: flex; flex-wrap: wrap; gap: 10px; }
233
+ .hero__cta .btn { height: 42px; padding: 0 18px; font-size: 14.5px; }
234
+
235
+ .hero__note {
236
+ margin: 20px 0 0; max-width: 36rem; font-size: 12.5px; line-height: 1.65;
237
+ color: var(--text-tertiary);
238
+ }
239
+ .hero__note strong { color: var(--text-secondary); font-weight: 600; }
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); }
241
+
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 {
247
+ background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
248
+ box-shadow: var(--shadow-md); overflow: hidden;
249
+ }
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;
262
+ }
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; }
290
+ .feature code { font-family: var(--mono); font-size: .85em; background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; }
291
+
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; }
294
+ .check {
295
+ display: flex; gap: 12px; align-items: flex-start;
296
+ padding: 16px 0; border-bottom: 1px solid var(--border);
297
+ }
298
+ .check > svg { width: 18px; height: 18px; flex: none; color: var(--accent); margin-top: 1px; }
299
+ .check strong { display: block; font-size: 14.5px; font-weight: 650; }
300
+ .check span { display: block; color: var(--text-secondary); font-size: 13px; margin-top: 2px; }
301
+
302
+ /* ----------------------------------------------------------- dashboard preview */
303
+ /* A carousel of real dashboard screenshots, one browser-framed slide at a time.
304
+ Prev/next arrows + dots; light/dark variants both in the DOM, CSS shows the one
305
+ matching the page theme. Degrades to the first slide if JS is off. */
306
+ /* side padding reserves space for the arrows so they sit OUTSIDE the frame
307
+ (never covering the screenshot). The frame shrinks to fit; on small screens
308
+ the padding collapses and arrows overlap the edges instead. */
309
+ .carousel { position: relative; max-width: 936px; margin: 0 auto; padding: 0 54px; }
310
+ .carousel__viewport { overflow: hidden; border-radius: var(--radius-lg); }
311
+ .carousel__track { display: flex; transition: transform .42s cubic-bezier(.22,.61,.36,1); }
312
+ @media (prefers-reduced-motion: reduce) { .carousel__track { transition: none; } }
313
+ .slide { flex: 0 0 100%; min-width: 0; margin: 0; } /* reset <figure> default margin so each slide is exactly 100% (translateX math) */
314
+ .slide figcaption { margin-top: 14px; text-align: center; font-size: 13.5px; color: var(--text-secondary); }
315
+ .slide figcaption strong { color: var(--text); font-weight: 650; }
316
+
317
+ .browser {
318
+ border: 1px solid var(--border-strong); border-radius: var(--radius-lg); overflow: hidden;
319
+ background: var(--surface); box-shadow: var(--shadow-md);
320
+ }
321
+ .browser__bar {
322
+ display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 14px;
323
+ background: var(--bg-subtle); border-bottom: 1px solid var(--border);
324
+ }
325
+ .browser__dots { display: inline-flex; gap: 6px; flex: none; }
326
+ .browser__dots i { width: 10px; height: 10px; border-radius: 999px; background: var(--border-strong); }
327
+ .browser__url {
328
+ flex: 1; max-width: 320px; margin: 0 auto;
329
+ font-family: var(--mono); font-size: 12px; color: var(--text-tertiary); text-align: center;
330
+ background: var(--surface); border: 1px solid var(--border); border-radius: 999px; padding: 3px 12px;
331
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
332
+ }
333
+ .browser__body { display: block; background: var(--bg); }
334
+ .browser__body img { width: 100%; height: auto; }
335
+
336
+ /* show the screenshot that matches the current theme (default page theme = dark) */
337
+ .shot__img { display: none; }
338
+ :root[data-theme='dark'] .shot__img--dark { display: block; }
339
+ :root:not([data-theme='dark']) .shot__img--light { display: block; }
340
+
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. */
345
+ .carousel__arrow {
346
+ position: absolute; top: 0; bottom: 36px; margin-block: auto;
347
+ width: 44px; height: 44px; border-radius: 999px; display: grid; place-items: center;
348
+ background: var(--surface); border: 1px solid var(--border-strong); color: var(--text);
349
+ box-shadow: var(--shadow-md); cursor: pointer; z-index: 2;
350
+ transition: background .15s, opacity .15s;
351
+ }
352
+ .carousel__arrow:hover { background: var(--surface-hover); }
353
+ .carousel__arrow:disabled { opacity: .3; cursor: default; box-shadow: none; }
354
+ .carousel__arrow svg { width: 20px; height: 20px; }
355
+ .carousel__arrow--prev { left: 0; }
356
+ .carousel__arrow--next { right: 0; }
357
+
358
+ /* dots */
359
+ .carousel__dots { display: flex; justify-content: center; gap: 8px; margin-top: 18px; }
360
+ .carousel__dot {
361
+ width: 8px; height: 8px; padding: 0; border: 0; border-radius: 999px;
362
+ background: var(--border-strong); cursor: pointer; transition: background .15s, width .15s;
363
+ }
364
+ .carousel__dot:hover { background: var(--text-tertiary); }
365
+ .carousel__dot.is-active { width: 22px; background: var(--accent); }
366
+
367
+ @media (max-width: 640px) {
368
+ .carousel { padding: 0; }
369
+ .carousel__arrow--prev { left: 6px; }
370
+ .carousel__arrow--next { right: 6px; }
371
+ }
372
+
373
+ /* ----------------------------------------------------------------- install */
374
+ /* install track (A: .mcpb / B: terminal) */
375
+ .track {
376
+ max-width: 720px; margin: 0 auto;
377
+ padding: 26px 0 0;
378
+ }
379
+ .track + .track { margin-top: 14px; padding-top: 30px; border-top: 1px solid var(--border); }
380
+ .track__head { max-width: 720px; margin: 0 auto 18px; }
381
+ .track__tag {
382
+ display: inline-block; font-size: 11.5px; font-weight: 700; letter-spacing: .04em;
383
+ color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border);
384
+ padding: 3px 10px; border-radius: 999px; margin-bottom: 10px;
385
+ }
386
+ .track__head h3 { font-size: 1.15rem; }
387
+ .track__head h3 code { font-family: var(--mono); font-size: .82em; background: var(--bg-sunken); padding: 1px 6px; border-radius: 4px; font-weight: 600; }
388
+ .track__head > p { margin-top: 7px; color: var(--text-secondary); font-size: 14px; }
389
+ .track .steps, .track .note, .track .acc { margin-left: 0; margin-right: 0; }
390
+ .track .step__body .btn { margin-top: 4px; }
391
+
392
+ .note {
393
+ border: 1px solid var(--border); border-left: 3px solid var(--accent);
394
+ background: var(--surface); border-radius: var(--radius);
395
+ padding: 13px 17px; margin: 0 auto 24px; max-width: 720px;
396
+ font-size: 14px; color: var(--text-secondary); line-height: 1.6;
397
+ }
398
+ .note strong { color: var(--text); }
399
+ .note code { font-family: var(--mono); background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; font-size: .88em; color: var(--text); }
400
+
401
+ .steps { list-style: none; margin: 0 auto; padding: 0; max-width: 720px; }
402
+ .step { display: flex; gap: 15px; padding: 2px 0 24px; position: relative; }
403
+ .step__no {
404
+ width: 30px; height: 30px; flex: none; border-radius: 999px;
405
+ display: grid; place-items: center; font-weight: 700; font-size: 14px;
406
+ color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border); z-index: 1;
407
+ }
408
+ .step:not(:last-of-type)::before {
409
+ content: ''; position: absolute; left: 15px; top: 34px; bottom: 6px; width: 1px;
410
+ background: var(--border); z-index: 0;
411
+ }
412
+ .step__body { flex: 1; min-width: 0; padding-top: 3px; }
413
+ .step__body h3 { font-size: 16px; margin-bottom: 5px; }
414
+ .step__body > p { color: var(--text-secondary); font-size: 14px; margin-bottom: 11px; }
415
+ .step__body code { font-family: var(--mono); background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; font-size: .86em; }
416
+ .step__tip { margin-top: 11px; font-size: 13px; color: var(--text-secondary); }
417
+ .step__tip code { font-family: var(--mono); background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; }
418
+
419
+ kbd {
420
+ font-family: inherit; font-size: .82em; background: var(--bg-sunken);
421
+ border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: 4px; padding: 1px 6px;
422
+ }
423
+
424
+ /* code blocks */
425
+ .code {
426
+ position: relative; margin: 11px 0; background: var(--code-bg);
427
+ border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
428
+ }
429
+ .code__label {
430
+ display: block; font-family: var(--mono); font-size: 11px; letter-spacing: .03em;
431
+ color: var(--text-tertiary); padding: 9px 13px 0;
432
+ }
433
+ .code pre {
434
+ margin: 0; padding: 11px 64px 13px 13px; overflow-x: auto;
435
+ font-family: var(--mono); font-size: 12.5px; line-height: 1.6; color: var(--code-text);
436
+ white-space: pre-wrap; overflow-wrap: anywhere;
437
+ }
438
+ .copy-btn {
439
+ position: absolute; top: 7px; right: 7px; font-family: inherit; font-size: 12px; font-weight: 600;
440
+ color: var(--text-secondary); background: var(--surface); border: 1px solid var(--border-strong);
441
+ border-radius: 6px; padding: 3px 10px; cursor: pointer; transition: background .12s, color .12s, border-color .12s;
442
+ }
443
+ .copy-btn:hover { border-color: var(--accent); color: var(--accent); }
444
+ .copy-btn.copied { color: var(--text-on-accent); background: var(--accent); border-color: var(--accent); }
445
+
446
+ /* tabs */
447
+ .tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin: 4px 0 0; flex-wrap: wrap; }
448
+ .tab {
449
+ font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--text-secondary);
450
+ background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px;
451
+ padding: 8px 12px; cursor: pointer; transition: color .12s;
452
+ }
453
+ .tab:hover { color: var(--text); }
454
+ .tab.is-active { color: var(--accent); border-bottom-color: var(--accent); }
455
+ .panel { display: none; padding-top: 11px; }
456
+ .panel[data-active='true'] { display: block; }
457
+ .path-note { font-size: 12.5px; color: var(--text-tertiary); margin-bottom: 6px; line-height: 1.6; }
458
+ .path-note code { font-family: var(--mono); font-size: .9em; background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; }
459
+
460
+ /* troubleshooting accordion */
461
+ .acc {
462
+ max-width: 720px; margin: 4px auto 0; border: 1px solid var(--border);
463
+ border-radius: var(--radius); background: var(--surface); overflow: hidden;
464
+ }
465
+ .acc > summary {
466
+ cursor: pointer; list-style: none; padding: 15px 17px; font-weight: 650; font-size: 14.5px;
467
+ display: flex; align-items: center; gap: 10px;
468
+ }
469
+ .acc > summary::-webkit-details-marker { display: none; }
470
+ .acc > summary::after { content: '+'; margin-left: auto; color: var(--text-tertiary); font-weight: 400; }
471
+ .acc[open] > summary::after { content: '-'; }
472
+ .acc > summary:hover { background: var(--surface-hover); }
473
+ .acc__hint { font-weight: 500; font-size: 12.5px; color: var(--text-tertiary); }
474
+ .acc__body { padding: 0 17px 16px; }
475
+ .acc + .acc { margin-top: 10px; }
476
+ .track + .acc { margin-top: 16px; }
477
+ /* track-as-accordion: pill in summary + intro paragraph in body */
478
+ .acc > summary .track__tag { margin: 0; flex: none; }
479
+ .acc > summary { flex-wrap: wrap; }
480
+ .track-desc { color: var(--text-secondary); font-size: 14px; line-height: 1.6; margin: 0 0 12px; }
481
+ .trouble { display: grid; grid-template-columns: repeat(2, 1fr); gap: 13px; }
482
+ .trouble__item h4 { font-size: 14px; margin-bottom: 4px; }
483
+ .trouble__item p { color: var(--text-secondary); font-size: 13px; }
484
+ .trouble__item code { font-family: var(--mono); background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; font-size: .86em; }
485
+
486
+ /* privacy */
487
+ .privacy-card {
488
+ max-width: 800px; margin: 0 auto; display: flex; gap: 20px; align-items: flex-start;
489
+ background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
490
+ padding: 28px 30px;
491
+ }
492
+ .privacy-card__icon {
493
+ width: 50px; height: 50px; flex: none; border-radius: 12px; display: grid; place-items: center;
494
+ color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border);
495
+ }
496
+ .privacy-card__icon svg { width: 26px; height: 26px; }
497
+ .privacy-card__text h2 { font-size: 1.3rem; }
498
+ .privacy-card__text > p { color: var(--text-secondary); margin-top: 9px; font-size: 14.5px; }
499
+ .privacy-card__list { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 7px; }
500
+ .privacy-card__list li { font-size: 13.5px; color: var(--text-secondary); }
501
+ .privacy-card__list strong { color: var(--text); margin-right: 8px; }
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; }
504
+
505
+ /* footer */
506
+ .foot {
507
+ max-width: var(--maxw); margin: 104px auto 0; padding: 32px 24px 56px;
508
+ border-top: 1px solid var(--border); text-align: center; color: var(--text-tertiary);
509
+ }
510
+ .foot__brand { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; color: var(--text); font-size: 15px; font-family: var(--mono); }
511
+ .foot p { margin: 11px 0 0; font-size: 13px; }
512
+ .foot__links { display: flex; justify-content: center; gap: 16px; margin-top: 14px; font-size: 13px; }
513
+ .foot__links a { color: var(--text-secondary); }
514
+
515
+ /* a11y */
516
+ .sr-only {
517
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
518
+ overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
519
+ }
520
+
521
+ /* scroll reveal — JS adds .reveal only when motion is allowed, so no-JS /
522
+ reduced-motion shows everything immediately (no hidden content risk) */
523
+ .reveal { opacity: 0; transform: translateY(14px); transition: opacity .55s ease, transform .55s ease; }
524
+ .reveal.is-visible { opacity: 1; transform: none; }
525
+ .cards-3 .feature:nth-child(2).reveal { transition-delay: .07s; }
526
+ .cards-3 .feature:nth-child(3).reveal { transition-delay: .14s; }
527
+ .checks .check:nth-child(even).reveal { transition-delay: .05s; }
528
+
529
+ /* responsive */
530
+ @media (max-width: 820px) {
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; }
534
+ .checks { grid-template-columns: 1fr; }
535
+ .trouble { grid-template-columns: 1fr; }
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; }
542
+ }
543
+ @media (max-width: 540px) {
544
+ .hero { padding-top: 46px; }
545
+ .privacy-card { flex-direction: column; gap: 15px; padding: 22px; }
546
+ }
547
+ @media (prefers-reduced-motion: reduce) {
548
+ html { scroll-behavior: auto; }
549
+ * { animation: none !important; transition: none !important; }
550
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://openapi.vercel.sh/vercel.json",
3
+ "cleanUrls": true,
4
+ "trailingSlash": false,
5
+ "headers": [
6
+ {
7
+ "source": "/style.css",
8
+ "headers": [
9
+ { "key": "Cache-Control", "value": "public, max-age=3600, must-revalidate" }
10
+ ]
11
+ },
12
+ {
13
+ "source": "/(.*)",
14
+ "headers": [
15
+ { "key": "X-Content-Type-Options", "value": "nosniff" },
16
+ { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }
17
+ ]
18
+ }
19
+ ]
20
+ }