返回 CodeWhale
home.css
根目录 / web / app / styles / home.css
1 /* ------------------------------------------------------------------ */
2 /* Home — paper above, sea below */
3 /* ------------------------------------------------------------------ */
4 /*
5 * Paper holds the promise and the install command. The live whale rests on
6 * one horizon line where the paper meets the sea; the real terminal floats
7 * in the deep water just under it. The reading sections return to paper,
8 * and the page ends in the sea with the install command, which runs on into
9 * the footer. Two sea moments, one horizon each; nothing else decorates.
10 */
11
12 .home { --home-measure: 36rem; }
13
14 /* ---------- paper: the hero ---------- */
15 .home-hero {
16 position: relative;
17 padding-block: clamp(3.5rem, 9vw, 7.5rem) 0;
18 background: var(--bg);
19 }
20
21 .home-hero-inner {
22 display: grid;
23 grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
24 align-items: end;
25 gap: var(--space-6) clamp(2rem, 6vw, 5rem);
26 width: var(--container);
27 margin-inline: auto;
28 }
29
30 .home-hero-copy {
31 display: grid;
32 gap: var(--space-5);
33 padding-bottom: clamp(3rem, 7vw, 5.5rem);
34 min-width: 0;
35 }
36
37 /* Type is the hero's structure: large, tight, set in the brand face. */
38 /* Measured in em, not ch, so CJK and long compound words wrap the same as
39 English; hyphenation lets German compounds break at syllables. */
40 .home-title {
41 max-width: 9.5em;
42 font-size: clamp(2.5rem, 1.4rem + 4.4vw, 5rem);
43 hyphens: auto;
44 font-weight: 650;
45 line-height: 0.98;
46 letter-spacing: -0.035em;
47 text-wrap: balance;
48 color: var(--text);
49 }
50
51 .home-lede {
52 max-width: var(--home-measure);
53 color: var(--muted);
54 font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.25rem);
55 line-height: 1.55;
56 text-wrap: pretty;
57 }
58 .home-lede strong { color: var(--text); font-weight: 600; }
59
60 /* CJK headlines carry more meaning per glyph: a step smaller, wider measure,
61 and no breaks inside words (Korean) or mid-phrase where avoidable. */
62 :lang(zh) .home-title,
63 :lang(ja) .home-title,
64 :lang(ko) .home-title {
65 max-width: 11em;
66 font-size: clamp(2.125rem, 1.2rem + 3.4vw, 4rem);
67 line-height: 1.12;
68 letter-spacing: -0.01em;
69 word-break: keep-all;
70 line-break: strict;
71 }
72
73 .home-hero-copy .hero-install { max-width: 32rem; margin-top: var(--space-2); }
74 .home-hero-copy .actions { margin-top: calc(var(--space-2) * -1); }
75
76 /* The whale rests on the horizon: its lowest point meets the line. */
77 .home-whale {
78 display: flex;
79 justify-content: center;
80 align-self: end;
81 min-width: 0;
82 }
83 .home-whale .whale-live {
84 --whale-size: clamp(18rem, 40vw, 34rem);
85 width: var(--whale-size);
86 /* The rig fills the middle 72% of the canvas; drop its belly onto the
87 waterline where the tide begins. */
88 margin-bottom: calc(var(--whale-size) * -0.26);
89 position: relative;
90 z-index: 2;
91 }
92
93 /* ---------- the live whale ---------- */
94 .whale-live {
95 position: relative;
96 aspect-ratio: 1;
97 }
98 .whale-live > .whale-live-poster,
99 .whale-live > .whale-live-canvas {
100 position: absolute;
101 inset: 0;
102 width: 100%;
103 height: 100%;
104 }
105 /* The phase, in a word: a small point of light and a label, above the
106 whale's back. Light means live work (DIRECTION.md). */
107 .whale-live-state {
108 position: absolute;
109 inset-inline-start: 50%;
110 top: 8%;
111 translate: -50% 0;
112 display: inline-flex;
113 align-items: center;
114 gap: var(--space-2);
115 padding: 0.3rem 0.75rem;
116 border: 1px solid var(--line);
117 border-radius: var(--radius-pill);
118 background: var(--panel);
119 color: var(--text);
120 font-size: var(--text-caption);
121 font-weight: 500;
122 white-space: nowrap;
123 box-shadow: 0 6px 18px -10px rgb(11 72 187 / 0.35);
124 }
125 .whale-live-dot {
126 width: 0.45rem;
127 height: 0.45rem;
128 border-radius: var(--radius-pill);
129 background: var(--brand-light);
130 }
131 .whale-live-state[data-state="done"] .whale-live-dot { background: var(--live); }
132 .whale-live-state[data-state="rest"] .whale-live-dot { background: var(--muted); }
133 @media (prefers-reduced-motion: no-preference) {
134 .whale-live-state:not([data-state="rest"]):not([data-state="done"]) .whale-live-dot {
135 animation: whale-pulse 1.4s var(--ease-out) infinite;
136 }
137 }
138 @keyframes whale-pulse {
139 0% { box-shadow: 0 0 0 0 rgb(30 143 216 / 0.55); }
140 100% { box-shadow: 0 0 0 0.5rem rgb(30 143 216 / 0); }
141 }
142 .whale-live-poster { --pose-size: 100%; padding: 14%; box-sizing: border-box; }
143 .whale-live-canvas { opacity: 0; }
144 .whale-live[data-ready] .whale-live-canvas { opacity: 1; }
145 .whale-live[data-ready] .whale-live-poster { visibility: hidden; }
146
147 /* ---------- the sea: the terminal ---------- */
148 /* The tide: paper fades through light aqua into cobalt and the deep, like
149 the merch's Tide ombré (light at the top, navy at the hem). No hard edge;
150 the whale rests where the water begins, and the terminal floats in calm,
151 dark water below. */
152 .home-terminal {
153 position: relative;
154 background: var(--tide), var(--ocean-floor);
155 }
156
157 /* The install band ends the page in the sea: one line of light at its top,
158 and water that reaches the floor colour by its own bottom edge, so it runs
159 into the footer without a seam. */
160 .home-install {
161 position: relative;
162 background: var(--sea-band), var(--ocean-floor);
163 }
164 .home-install::before {
165 content: "";
166 position: absolute;
167 inset: 0 0 auto;
168 height: 1px;
169 background: var(--horizon-light);
170 }
171
172 .home-terminal-inner {
173 display: grid;
174 gap: var(--space-6);
175 width: min(var(--container), 64rem);
176 margin-inline: auto;
177 padding-block: clamp(9rem, 16vw, 13rem) clamp(4rem, 8vw, 6.5rem);
178 }
179
180 .home-terminal-title {
181 max-width: 22ch;
182 font-size: var(--text-section);
183 font-weight: 600;
184 line-height: 1.1;
185 letter-spacing: -0.02em;
186 color: var(--text);
187 text-wrap: balance;
188 }
189
190 .home-shot .figure-frame {
191 border: 0;
192 box-shadow:
193 0 0 0 1px rgb(var(--gpui-dark-primary-rgb) / 0.14),
194 0 40px 80px -40px rgb(0 0 0 / 0.75);
195 }
196 .home-shot figcaption {
197 display: flex;
198 flex-wrap: wrap;
199 justify-content: space-between;
200 gap: var(--space-2) var(--space-4);
201 }
202
203 /* ---------- paper: the reading sections ---------- */
204 .home-body {
205 width: var(--container);
206 margin-inline: auto;
207 padding-block: clamp(4.5rem, 9vw, 7rem);
208 }
209 .home-body > .home-section { padding-block: 0; }
210 .home-body > .home-section + .home-section {
211 margin-top: clamp(2.5rem, 6vw, 5rem);
212 padding-top: clamp(2.5rem, 6vw, 5rem);
213 border-top: 1px solid var(--line);
214 }
215
216 /* Terms read as words, not as code: the brand face, a little weight. */
217 .home-body .ruled-list dt {
218 font-family: var(--font-body);
219 font-size: var(--text-body);
220 font-weight: 600;
221 letter-spacing: 0;
222 color: var(--text);
223 }
224 .home-modes dt { font-family: var(--font-body); }
225
226 .product-start-links { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-5); margin-top: var(--space-5); }
227
228 /* ---------- the sea: install, into the footer ---------- */
229 .home-install-inner {
230 display: grid;
231 gap: var(--space-6);
232 grid-template-columns: minmax(0, 1fr);
233 align-items: center;
234 width: var(--container);
235 margin-inline: auto;
236 padding-block: clamp(4.5rem, 9vw, 7rem) clamp(2.5rem, 5vw, 4rem);
237 }
238 @media (min-width: 900px) {
239 .home-install-inner { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); }
240 }
241 .home-install-channels { color: var(--muted); font-size: var(--text-ui); line-height: 1.6; }
242
243 /* ---------- narrow screens ---------- */
244 @media (max-width: 900px) {
245 .home-hero-inner { grid-template-columns: minmax(0, 1fr); }
246 .home-hero-copy { padding-bottom: var(--space-4); }
247 .home-whale { justify-content: flex-end; }
248 .home-whale .whale-live { --whale-size: clamp(14rem, 70vw, 20rem); margin-inline: auto; }
249 }
250
251 @media (forced-colors: active) {
252 .home-install::before { background: CanvasText; }
253 }
254
254 lines CSS