chengzeyi commited on
Commit
b842027
·
1 Parent(s): 7ac522b

Rewrite as a model reference page; drop auto-redirect and ad copy

Browse files
Files changed (4) hide show
  1. README.md +18 -6
  2. index.html +197 -117
  3. style.css +488 -318
  4. tabs.js +15 -0
README.md CHANGED
@@ -1,11 +1,23 @@
1
  ---
2
- title: Wan2.2
3
- emoji: 👀
4
- colorFrom: indigo
5
- colorTo: pink
6
  sdk: static
7
  pinned: false
8
- short_description: wan 2.2 alibaba
 
9
  ---
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Wan 2.2
3
+ emoji: 🎞️
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: static
7
  pinned: false
8
+ license: apache-2.0
9
+ short_description: Reference for Wan 2.2, Alibaba Tongyi Lab's open video models
10
  ---
11
 
12
+ # Wan 2.2
13
+
14
+ A reference page for **Wan 2.2**, the open large-scale video generative model family
15
+ released by Alibaba's Tongyi Lab: what changed relative to 2.1, which checkpoints
16
+ were published, and how to call the hosted endpoint.
17
+
18
+ - Weights: [Wan-AI on Hugging Face](https://huggingface.co/Wan-AI)
19
+ - Code: [Wan-Video/Wan2.2](https://github.com/Wan-Video/Wan2.2)
20
+ - Hosted inference: [wavespeed.ai/collections/wan-2-2](https://wavespeed.ai/collections/wan-2-2)
21
+
22
+ Wan 2.2 is developed and released by Alibaba Tongyi Lab. This Space is maintained by
23
+ WaveSpeed AI, which provides hosted inference for the model.
index.html CHANGED
@@ -1,123 +1,203 @@
1
- <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Wan 2.2 - Advanced Video Generation by Alibaba Tongyi Lab</title>
7
- <meta name="description" content="Wan 2.2: Open and Advanced Large-Scale Video Generative Models by Alibaba Tongyi Lab. Featuring MoE architecture, cinematic-level aesthetics, and complex motion generation." />
8
- <meta name="keywords" content="Wan 2.2, Alibaba, Tongyi Lab, Video Generation, AI Models, Text-to-Video, Image-to-Video, WaveSpeedAI, MoE, Video AI" />
9
-
10
- <!-- Open Graph / Social Media Meta Tags -->
11
- <meta property="og:title" content="Wan 2.2 - Advanced Video Generation by Alibaba Tongyi Lab" />
12
- <meta property="og:description" content="Experience the next generation of video AI with Wan 2.2, developed by Alibaba Tongyi Lab" />
13
- <meta property="og:type" content="website" />
14
- <meta property="og:url" content="https://wavespeed.ai/collections/wan-2-2" />
15
-
16
- <!-- Additional Meta Information -->
17
- <meta name="author" content="Alibaba Tongyi Lab" />
18
- <meta name="robots" content="index, follow" />
19
- <link rel="canonical" href="https://wavespeed.ai/collections/wan-2-2" />
20
-
21
- <link rel="stylesheet" href="style.css" />
22
- <meta http-equiv="refresh" content="10;url=https://wavespeed.ai/collections/wan-2-2" />
23
  </head>
24
  <body>
25
- <nav class="top-nav">
26
- <div class="nav-content">
27
- <div class="nav-logo">WAN</div>
28
- <div class="nav-links">
29
- <a href="https://wavespeed.ai/collections/wan-2-2" class="nav-link" target="_blank" rel="noopener noreferrer">Home</a>
30
- <a href="https://wavespeed.ai/docs" class="nav-link" target="_blank" rel="noopener noreferrer">Documentation</a>
31
- <a href="https://wavespeed.ai/blog" class="nav-link" target="_blank" rel="noopener noreferrer">Blog</a>
32
- <a href="https://wavespeed.ai/collections/wan-2-2" class="nav-button" target="_blank" rel="noopener noreferrer">Visit WaveSpeedAI →</a>
33
- </div>
34
- </div>
35
- </nav>
36
-
37
- <div class="container">
38
- <div class="content">
39
- <div class="logo-section">
40
- <h1>Wan 2.2</h1>
41
- <p class="subtitle">By Alibaba Tongyi Lab</p>
42
- </div>
43
-
44
- <div class="announcement-section">
45
- <p class="announcement">Wan 2.2 is now available on WaveSpeedAI!</p>
46
- <div class="divider"></div>
47
- <p class="description">Open and Advanced Large-Scale Video Generative Models</p>
48
- </div>
49
-
50
- <div class="features-section">
51
- <div class="feature">
52
- <h3>🚀 Effective MoE Architecture</h3>
53
- <p>Advanced Mixture-of-Experts architecture separates denoising process with specialized expert models, enlarging model capacity while maintaining computational cost</p>
54
- </div>
55
- <div class="feature">
56
- <h3>🎯 Cinematic-level Aesthetics</h3>
57
- <p>Meticulously curated aesthetic data with detailed labels for lighting, composition, contrast, and color tone for precise and controllable cinematic style generation</p>
58
- </div>
59
- <div class="feature">
60
- <h3>🌟 Complex Motion Generation</h3>
61
- <p>Trained on +65.6% more images and +83.2% more videos, achieving superior performance in motions, semantics, and aesthetics</p>
62
- </div>
63
- </div>
64
-
65
- <div class="models-section">
66
- <h3 class="models-title">Model Downloads</h3>
67
- <div class="models-grid">
68
- <div class="model-category">
69
- <h4>Text to Video Models</h4>
70
- <div class="model-links">
71
- <a href="https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B" target="_blank" rel="noopener noreferrer" class="model-link">
72
- <span class="model-name">T2V-A14B</span>
73
- <span class="model-tag">Base</span>
74
- </a>
75
- <a href="https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B-Diffusers" target="_blank" rel="noopener noreferrer" class="model-link">
76
- <span class="model-name">T2V-A14B</span>
77
- <span class="model-tag">Diffusers</span>
78
- </a>
79
- </div>
80
- </div>
81
- <div class="model-category">
82
- <h4>Image to Video Models</h4>
83
- <div class="model-links">
84
- <a href="https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B" target="_blank" rel="noopener noreferrer" class="model-link">
85
- <span class="model-name">I2V-A14B</span>
86
- <span class="model-tag">Base</span>
87
- </a>
88
- <a href="https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B-Diffusers" target="_blank" rel="noopener noreferrer" class="model-link">
89
- <span class="model-name">I2V-A14B</span>
90
- <span class="model-tag">Diffusers</span>
91
- </a>
92
- </div>
93
- </div>
94
- </div>
95
-
96
- <div class="model-highlight">
97
- <h4>Efficient High-Definition Hybrid TI2V</h4>
98
- <p>5B model with advanced Wan2.2-VAE achieving 16×16×4 compression ratio. Supports both text-to-video and image-to-video generation at 720P resolution with 24fps.</p>
99
- <div class="highlight-links">
100
- <a href="https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B" target="_blank" rel="noopener noreferrer" class="highlight-link">
101
- <span>TI2V-5B Base</span>
102
- </a>
103
- <a href="https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B-Diffusers" target="_blank" rel="noopener noreferrer" class="highlight-link">
104
- <span>TI2V-5B Diffusers</span>
105
- </a>
106
- </div>
107
- </div>
108
- </div>
109
-
110
- <div class="redirect-section">
111
- <p class="redirect-text">Redirecting to WaveSpeedAI...</p>
112
- <div class="progress-bar">
113
- <div class="progress"></div>
114
- </div>
115
- <a href="https://wavespeed.ai/collections/wan-2-2" class="cta-button" target="_blank" rel="noopener noreferrer">Visit Now </a>
116
- <p class="huggingface-link">
117
- Also available on <a href="https://huggingface.co/Wan-AI" target="_blank" rel="noopener noreferrer">Hugging Face</a>
118
- </p>
119
- </div>
120
- </div>
121
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  </body>
123
  </html>
 
1
+ <!doctype html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Wan 2.2 open large-scale video generative models</title>
7
+ <meta name="description" content="Reference for Wan 2.2, the open video generation model family from Alibaba&#x27;s Tongyi Lab: architecture, released checkpoints, and hosted API usage." />
8
+ <link rel="canonical" href="https://wavespeed.ai/collections/wan-2-2" />
9
+
10
+ <meta property="og:type" content="website" />
11
+ <meta property="og:title" content="Wan 2.2 open large-scale video generative models" />
12
+ <meta property="og:description" content="Reference for Wan 2.2, the open video generation model family from Alibaba&#x27;s Tongyi Lab: architecture, released checkpoints, and hosted API usage." />
13
+ <meta property="og:url" content="https://wavespeed.ai/collections/wan-2-2" />
14
+ <meta name="twitter:card" content="summary_large_image" />
15
+
16
+ <link rel="stylesheet" href="style.css" />
 
 
 
 
 
 
17
  </head>
18
  <body>
19
+ <header class="site-header">
20
+ <div class="wrap">
21
+ <a class="brand" href="https://wavespeed.ai" target="_blank" rel="noopener">
22
+ <span class="brand-mark" aria-hidden="true"></span>
23
+ <span>WaveSpeed AI</span>
24
+ </a>
25
+ <nav class="header-nav">
26
+ <a class="jump opt" href="#architecture">Architecture</a>
27
+ <a class="jump opt" href="#checkpoints">Checkpoints</a>
28
+ <a class="jump" href="#run">Run it</a>
29
+ <a class="jump" href="#resources">Resources</a>
30
+ <a href="https://wavespeed.ai/collections/wan-2-2" target="_blank" rel="noopener">wavespeed.ai &#8599;</a>
31
+ </nav>
32
+ </div>
33
+ </header>
34
+
35
+ <main class="wrap">
36
+ <div class="hero">
37
+ <p class="eyebrow">Alibaba · Tongyi Lab</p>
38
+ <h1>Wan 2.2</h1>
39
+ <p class="lede">An open family of large-scale video generative models covering text-to-video, image-to-video and a compact hybrid text/image-to-video checkpoint. Weights are published on Hugging Face under Apache-2.0.</p>
40
+ <ul class="meta">
41
+ <li><b>Developer</b> Alibaba Tongyi Lab</li>
42
+ <li><b>Task</b> text-to-video · image-to-video</li>
43
+ <li><b>License</b> Apache-2.0</li>
44
+ <li><b>Released</b> July 2025</li>
45
+ </ul>
46
+ </div>
47
+
48
+ <section id="architecture">
49
+ <h2>What changed in 2.2</h2>
50
+ <p class="section-note">Wan 2.2 revises the 2.1 architecture in three places. The claims below are the authors' own, taken from the model cards and technical report.</p>
51
+ <div class="grid">
52
+ <div class="card">
53
+ <h3>Mixture-of-experts denoiser</h3>
54
+ <p>The denoising trajectory is split across specialised expert models rather than one monolithic network, which raises total parameter count without a matching rise in per-step inference cost.</p>
55
+ </div>
56
+ <div class="card">
57
+ <h3>Curated aesthetic supervision</h3>
58
+ <p>Training data carries explicit labels for lighting, composition, contrast and colour tone, so cinematographic attributes can be steered from the prompt instead of emerging by chance.</p>
59
+ </div>
60
+ <div class="card">
61
+ <h3>Larger motion corpus</h3>
62
+ <p>The authors report training on 65.6% more images and 83.2% more video than Wan 2.1, aimed primarily at motion fidelity and prompt adherence.</p>
63
+ </div>
64
+ <div class="card">
65
+ <h3>High-compression VAE</h3>
66
+ <p>The TI2V-5B checkpoint pairs with a Wan2.2-VAE at a 16×16×4 compression ratio, which is what makes 720p/24fps generation practical at that model size.</p>
67
+ </div>
68
+ </div>
69
+ </section>
70
+ <section id="checkpoints">
71
+ <h2>Released checkpoints</h2>
72
+ <p class="section-note">All weights are on the Hugging Face Hub under the Wan-AI organisation.</p>
73
+ <div class="table-scroll">
74
+ <table>
75
+ <thead><tr><th>Checkpoint</th><th>Task</th><th>Params</th><th>Weights</th></tr></thead>
76
+ <tbody>
77
+ <tr><td><code>Wan2.2-T2V-A14B</code></td><td>Text-to-video</td><td>14B (MoE)</td><td><a href="https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B" target="_blank" rel="noopener">base</a> · <a href="https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B-Diffusers" target="_blank" rel="noopener">diffusers</a></td></tr>
78
+ <tr><td><code>Wan2.2-I2V-A14B</code></td><td>Image-to-video</td><td>14B (MoE)</td><td><a href="https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B" target="_blank" rel="noopener">base</a> · <a href="https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B-Diffusers" target="_blank" rel="noopener">diffusers</a></td></tr>
79
+ <tr><td><code>Wan2.2-TI2V-5B</code></td><td>Text + image-to-video, 720p/24fps</td><td>5B</td><td><a href="https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B" target="_blank" rel="noopener">base</a> · <a href="https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B-Diffusers" target="_blank" rel="noopener">diffusers</a></td></tr>
80
+ </tbody>
81
+ </table>
82
+ </div>
83
+ </section>
84
+ <section id="run">
85
+ <h2>Run it</h2>
86
+ <p class="section-note">If you would rather not provision GPUs, the same checkpoints are served as a hosted endpoint. Available variants: <code>wan-2.2/t2v-480p</code>, <code>t2v-720p</code>, <code>i2v-480p</code> and <code>i2v-720p</code>.</p>
87
+ <div class="code">
88
+ <div class="code-tabs" role="tablist">
89
+ <button type="button" role="tab" aria-selected="true" data-panel="run-0">cURL</button>
90
+ <button type="button" role="tab" aria-selected="false" data-panel="run-1">Python</button>
91
+ <button type="button" role="tab" aria-selected="false" data-panel="run-2">JavaScript</button>
92
+ </div>
93
+ <pre id="run-0" role="tabpanel"><code># 1. submit the job
94
+ curl -X POST &quot;https://api.wavespeed.ai/api/v3/wavespeed-ai/wan-2.2/t2v-480p&quot; \
95
+ -H &quot;Authorization: Bearer $WAVESPEED_API_KEY&quot; \
96
+ -H &quot;Content-Type: application/json&quot; \
97
+ -d &#x27;{
98
+ &quot;prompt&quot;: &quot;A paper boat drifting down a rain-slicked gutter at dusk, shallow depth of field, warm street lights&quot;,
99
+ &quot;duration&quot;: 5,
100
+ &quot;enable_sync_mode&quot;: false
101
+ }&#x27;
102
+
103
+ # -&gt; {&quot;code&quot;: 200, &quot;data&quot;: {&quot;id&quot;: &quot;&lt;request-id&gt;&quot;, &quot;status&quot;: &quot;created&quot;, ...}}
104
+
105
+ # 2. poll until status is &quot;completed&quot;
106
+ curl &quot;https://api.wavespeed.ai/api/v3/predictions/&lt;request-id&gt;/result&quot; \
107
+ -H &quot;Authorization: Bearer $WAVESPEED_API_KEY&quot;
108
+
109
+ # -&gt; {&quot;code&quot;: 200, &quot;data&quot;: {&quot;status&quot;: &quot;completed&quot;, &quot;outputs&quot;: [&quot;https://...&quot;]}}</code></pre>
110
+ <pre id="run-1" role="tabpanel" hidden><code>import os, time, requests
111
+
112
+ API = &quot;https://api.wavespeed.ai/api/v3&quot;
113
+ KEY = os.environ[&quot;WAVESPEED_API_KEY&quot;]
114
+ HEADERS = {&quot;Authorization&quot;: f&quot;Bearer {KEY}&quot;}
115
+
116
+ # submit
117
+ res = requests.post(
118
+ f&quot;{API}/wavespeed-ai/wan-2.2/t2v-480p&quot;,
119
+ headers={**HEADERS, &quot;Content-Type&quot;: &quot;application/json&quot;},
120
+ json={
121
+ &quot;prompt&quot;: &quot;A paper boat drifting down a rain-slicked gutter at dusk, shallow depth of field, warm street lights&quot;,
122
+ &quot;duration&quot;: 5,
123
+ &quot;enable_sync_mode&quot;: false
124
+ },
125
+ timeout=30,
126
+ )
127
+ res.raise_for_status()
128
+ request_id = res.json()[&quot;data&quot;][&quot;id&quot;]
129
+
130
+ # poll
131
+ while True:
132
+ data = requests.get(
133
+ f&quot;{API}/predictions/{request_id}/result&quot;,
134
+ headers=HEADERS,
135
+ timeout=30,
136
+ ).json()[&quot;data&quot;]
137
+
138
+ if data[&quot;status&quot;] == &quot;completed&quot;:
139
+ print(data[&quot;outputs&quot;][0])
140
+ break
141
+ if data[&quot;status&quot;] == &quot;failed&quot;:
142
+ raise RuntimeError(data.get(&quot;error&quot;, &quot;generation failed&quot;))
143
+ time.sleep(1.5)</code></pre>
144
+ <pre id="run-2" role="tabpanel" hidden><code>const API = &quot;https://api.wavespeed.ai/api/v3&quot;;
145
+ const KEY = process.env.WAVESPEED_API_KEY;
146
+ const headers = { Authorization: `Bearer ${KEY}` };
147
+
148
+ // submit
149
+ const submit = await fetch(`${API}/wavespeed-ai/wan-2.2/t2v-480p`, {
150
+ method: &quot;POST&quot;,
151
+ headers: { ...headers, &quot;Content-Type&quot;: &quot;application/json&quot; },
152
+ body: JSON.stringify({
153
+ &quot;prompt&quot;: &quot;A paper boat drifting down a rain-slicked gutter at dusk, shallow depth of field, warm street lights&quot;,
154
+ &quot;duration&quot;: 5,
155
+ &quot;enable_sync_mode&quot;: false
156
+ }),
157
+ });
158
+ const { data: { id } } = await submit.json();
159
+
160
+ // poll
161
+ for (;;) {
162
+ const res = await fetch(`${API}/predictions/${id}/result`, { headers });
163
+ const { data } = await res.json();
164
+
165
+ if (data.status === &quot;completed&quot;) {
166
+ console.log(data.outputs[0]);
167
+ break;
168
+ }
169
+ if (data.status === &quot;failed&quot;) throw new Error(data.error ?? &quot;generation failed&quot;);
170
+ await new Promise((r) =&gt; setTimeout(r, 1500));
171
+ }</code></pre>
172
+ </div>
173
+ <div class="callout"><p>Requests are asynchronous: <code>POST</code> returns a request id, then you poll <code>/predictions/&lt;id&gt;/result</code> until <code>status</code> is <code>completed</code>. Set <code>enable_sync_mode: true</code> to have the call block and return outputs directly.</p><p>API keys are created in the <a href="https://wavespeed.ai/dashboard" target="_blank" rel="noopener">WaveSpeed dashboard</a>.</p></div>
174
+ <div class="btn-row">
175
+ <a class="btn" href="https://wavespeed.ai/collections/wan-2-2" target="_blank" rel="noopener">Open Wan 2.2 on WaveSpeed</a>
176
+ <a class="btn secondary" href="https://wavespeed.ai/docs" target="_blank" rel="noopener">API reference</a>
177
+ </div>
178
+ </section>
179
+ <section id="resources">
180
+ <h2>Resources</h2>
181
+ <ul class="links">
182
+ <li><a href="https://huggingface.co/Wan-AI" target="_blank" rel="noopener"><span>Wan-AI on Hugging Face</span><span class="host">huggingface.co</span></a></li>
183
+ <li><a href="https://github.com/Wan-Video/Wan2.2" target="_blank" rel="noopener"><span>Wan2.2 on GitHub</span><span class="host">github.com</span></a></li>
184
+ <li><a href="https://wavespeed.ai/collections/wan-2-2" target="_blank" rel="noopener"><span>Hosted endpoints</span><span class="host">wavespeed.ai</span></a></li>
185
+ <li><a href="https://wavespeed.ai/docs" target="_blank" rel="noopener"><span>WaveSpeed API docs</span><span class="host">wavespeed.ai</span></a></li>
186
+ </ul>
187
+ </section>
188
+ </main>
189
+
190
+ <footer class="site-footer">
191
+ <div class="wrap">
192
+ <p>This page is a model reference maintained by WaveSpeed AI. The model itself is developed and released by its respective authors; trademarks belong to them. WaveSpeed AI provides hosted inference for it.</p>
193
+ <div class="footer-links">
194
+ <a href="https://wavespeed.ai" target="_blank" rel="noopener">WaveSpeed AI</a>
195
+ <a href="https://wavespeed.ai/docs" target="_blank" rel="noopener">Docs</a>
196
+ <a href="https://huggingface.co/wavespeed" target="_blank" rel="noopener">Hugging Face</a>
197
+ </div>
198
+ </div>
199
+ </footer>
200
+
201
+ <script src="tabs.js"></script>
202
  </body>
203
  </html>
style.css CHANGED
@@ -1,405 +1,575 @@
1
- /* Navigation Styles */
2
- .top-nav {
3
- position: fixed;
4
- top: 0;
5
- left: 0;
6
- right: 0;
7
- background: rgba(26, 26, 26, 0.95);
8
- backdrop-filter: blur(10px);
9
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
10
- z-index: 1000;
11
- padding: 1rem 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  }
13
 
14
- .nav-content {
15
- max-width: 1200px;
16
- margin: 0 auto;
17
- padding: 0 2rem;
18
- display: flex;
19
- justify-content: space-between;
20
- align-items: center;
 
21
  }
22
 
23
- .nav-logo {
24
- font-size: 1.5rem;
25
- font-weight: bold;
26
- color: white;
27
- letter-spacing: 2px;
28
  }
29
 
30
- .nav-links {
31
- display: flex;
32
- gap: 2rem;
33
- align-items: center;
34
  }
35
 
36
- .nav-link {
37
- color: #aaa;
38
- text-decoration: none;
39
- font-size: 0.95rem;
40
- transition: color 0.3s ease;
41
  }
42
 
43
- .nav-link:hover {
44
- color: white;
 
 
45
  }
46
 
47
- .nav-button {
48
- background: #2196F3;
49
- color: white;
50
- padding: 0.5rem 1.2rem;
51
- border-radius: 20px;
52
- text-decoration: none;
53
- font-size: 0.95rem;
54
- font-weight: 500;
55
- transition: all 0.3s ease;
56
  }
57
 
58
- .nav-button:hover {
59
- background: #1976D2;
60
- transform: translateY(-1px);
 
 
 
 
 
61
  }
62
 
63
- /* Content Styles */
64
- body {
65
- margin: 0;
66
- padding: 0;
67
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
68
- background: linear-gradient(135deg, #1a1a1a, #2a2a2a);
69
- color: white;
70
- min-height: 100vh;
71
- display: flex;
72
- align-items: center;
73
- justify-content: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
- .container {
77
- text-align: center;
78
- padding: 2rem;
79
- max-width: 800px;
80
- width: 100%;
81
- margin-top: 4rem;
82
  }
83
 
84
- .content {
85
- animation: fadeIn 1s ease-in;
 
 
86
  }
87
 
88
- .logo-section {
89
- margin-bottom: 2rem;
 
 
 
 
90
  }
91
 
92
- h1, h2 {
93
- margin: 0.5rem 0;
94
- font-size: 3.5rem;
95
- font-weight: bold;
96
- background: linear-gradient(45deg, #fff, #e0e0e0);
97
- -webkit-background-clip: text;
98
- -webkit-text-fill-color: transparent;
99
- text-shadow: 0 0 30px rgba(255,255,255,0.1);
100
  }
101
 
102
- .announcement-section {
103
- margin: 2rem 0;
 
 
 
104
  }
105
 
106
- .announcement {
107
- font-size: 2rem;
108
- color: #2196F3;
109
- margin: 1rem 0;
110
- font-weight: bold;
111
- text-shadow: 0 0 10px rgba(33,150,243,0.3);
 
 
 
 
 
112
  }
113
 
114
- .divider {
115
- height: 2px;
116
- background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
117
- margin: 2rem auto;
118
- width: 60%;
119
  }
120
 
121
- .features-section {
122
- display: grid;
123
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
124
- gap: 2rem;
125
- margin: 3rem 0;
126
  }
127
 
128
- .feature {
129
- padding: 1.5rem;
130
- background: rgba(255,255,255,0.05);
131
- border-radius: 12px;
132
- transition: transform 0.3s ease;
133
  }
134
 
135
- .feature:hover {
136
- transform: translateY(-5px);
137
- background: rgba(255,255,255,0.08);
 
 
 
 
 
138
  }
139
 
140
- .feature h3 {
141
- font-size: 1.2rem;
142
- margin: 0 0 1rem 0;
143
- color: #fff;
144
  }
145
 
146
- .feature p {
147
- color: #aaa;
148
- font-size: 0.95rem;
149
- line-height: 1.5;
150
- margin: 0;
 
151
  }
152
 
153
- .models-section {
154
- margin: 3rem 0;
155
- padding: 2rem;
156
- background: rgba(255,255,255,0.03);
157
- border-radius: 16px;
158
  }
159
 
160
- .models-title {
161
- font-size: 1.5rem;
162
- color: #fff;
163
- margin-bottom: 2rem;
164
- text-align: center;
165
  }
166
 
167
- .models-grid {
168
- display: grid;
169
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
170
- gap: 2rem;
171
  }
172
 
173
- .model-category {
174
- text-align: left;
 
 
 
 
175
  }
176
 
177
- .model-category h4 {
178
- color: #aaa;
179
- font-size: 1.1rem;
180
- margin-bottom: 1rem;
181
- padding-bottom: 0.5rem;
182
- border-bottom: 1px solid rgba(255,255,255,0.1);
183
  }
184
 
185
- .model-links {
186
- display: flex;
187
- flex-direction: column;
188
- gap: 1rem;
 
189
  }
190
 
191
- .model-link {
192
- display: flex;
193
- justify-content: space-between;
194
- align-items: center;
195
- padding: 1rem;
196
- background: rgba(255,255,255,0.05);
197
- border-radius: 8px;
198
- text-decoration: none;
199
- transition: all 0.3s ease;
200
  }
201
 
202
- .model-link:hover {
203
- background: rgba(255,255,255,0.1);
204
- transform: translateY(-2px);
205
  }
206
 
207
- .model-name {
208
- color: #fff;
209
- font-weight: 500;
210
  }
211
 
212
- .model-tag {
213
- font-size: 0.8rem;
214
- padding: 0.2rem 0.6rem;
215
- background: #2196F3;
216
- color: white;
217
- border-radius: 12px;
218
- font-weight: 500;
 
219
  }
220
 
221
- .model-highlight {
222
- margin-top: 2rem;
223
- padding: 1.5rem;
224
- background: linear-gradient(135deg, rgba(33,150,243,0.1), rgba(33,150,243,0.05));
225
- border-radius: 12px;
226
- border: 1px solid rgba(33,150,243,0.2);
227
  }
228
 
229
- .model-highlight h4 {
230
- color: #2196F3;
231
- font-size: 1.2rem;
232
- margin: 0 0 1rem 0;
 
 
 
 
 
 
 
233
  }
234
 
235
- .model-highlight p {
236
- color: #aaa;
237
- font-size: 0.95rem;
238
- line-height: 1.6;
239
- margin: 0;
240
  }
241
 
242
- .redirect-section {
243
- margin-top: 3rem;
 
 
 
244
  }
245
 
246
- .redirect-text {
247
- color: #888;
248
- font-size: 1rem;
249
- margin: 1rem 0;
250
- animation: pulse 2s infinite;
 
 
 
251
  }
252
 
253
- .progress-bar {
254
- width: 200px;
255
- height: 4px;
256
- background: rgba(255,255,255,0.1);
257
- border-radius: 2px;
258
- margin: 1rem auto;
259
- overflow: hidden;
260
  }
261
 
262
- .progress {
263
- height: 100%;
264
- background: #2196F3;
265
- animation: progress 10s linear;
266
  }
267
 
268
- .cta-button {
269
- display: inline-block;
270
- padding: 0.8rem 1.5rem;
271
- background: #2196F3;
272
- color: white;
273
- text-decoration: none;
274
- border-radius: 25px;
275
- font-weight: bold;
276
- margin-top: 1rem;
277
- transition: all 0.3s ease;
278
  }
279
 
280
- .cta-button:hover {
281
- background: #1976D2;
282
- transform: scale(1.05);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  }
284
 
285
- .huggingface-link {
286
- margin-top: 2rem;
287
- font-size: 0.9rem;
288
- color: #888;
 
 
289
  }
290
 
291
- .huggingface-link a {
292
- color: #2196F3;
293
- text-decoration: none;
294
- transition: color 0.3s ease;
295
  }
296
 
297
- .huggingface-link a:hover {
298
- color: #1976D2;
299
- text-decoration: underline;
300
  }
301
 
302
- .highlight-links {
303
- display: flex;
304
- gap: 1rem;
305
- margin-top: 1.5rem;
306
- justify-content: center;
307
- }
308
-
309
- .highlight-link {
310
- display: inline-block;
311
- padding: 0.6rem 1.2rem;
312
- background: rgba(33,150,243,0.1);
313
- border: 1px solid rgba(33,150,243,0.3);
314
- border-radius: 8px;
315
- color: #2196F3;
316
- text-decoration: none;
317
- font-size: 0.9rem;
318
- font-weight: 500;
319
- transition: all 0.3s ease;
320
- }
321
-
322
- .highlight-link:hover {
323
- background: rgba(33,150,243,0.2);
324
- transform: translateY(-2px);
325
- }
326
-
327
- @keyframes fadeIn {
328
- from { opacity: 0; transform: translateY(-20px); }
329
- to { opacity: 1; transform: translateY(0); }
330
- }
331
-
332
- @keyframes pulse {
333
- 0% { opacity: 0.6; }
334
- 50% { opacity: 1; }
335
- 100% { opacity: 0.6; }
336
- }
337
-
338
- @keyframes progress {
339
- from { width: 0; }
340
- to { width: 100%; }
341
- }
342
-
343
- @media (max-width: 768px) {
344
- .nav-content {
345
- padding: 0 1rem;
346
- }
347
-
348
- .nav-links {
349
- gap: 1rem;
350
- }
351
-
352
- .nav-link {
353
- display: none;
354
- }
355
-
356
- .nav-button {
357
- display: block;
358
- }
359
- }
360
-
361
- @media (max-width: 600px) {
362
- h1, h2 {
363
- font-size: 2.5rem;
364
- }
365
-
366
- .announcement {
367
- font-size: 1.5rem;
368
- }
369
-
370
- .features-section {
371
- grid-template-columns: 1fr;
372
- gap: 1rem;
373
- }
374
-
375
- .container {
376
- padding: 1rem;
377
- }
378
-
379
- .models-section {
380
- padding: 1rem;
381
- }
382
-
383
- .models-grid {
384
- grid-template-columns: 1fr;
385
- gap: 1rem;
386
- }
387
-
388
- .model-link {
389
- padding: 0.8rem;
390
- }
391
-
392
- .model-highlight {
393
- margin-top: 1.5rem;
394
- padding: 1rem;
395
- }
396
-
397
- .highlight-links {
398
- flex-direction: column;
399
- gap: 0.8rem;
400
- }
401
-
402
- .highlight-link {
403
- text-align: center;
404
- }
405
  }
 
1
+ /* ==========================================================================
2
+ Shared stylesheet for wavespeed model reference pages.
3
+ Edit here, then run _shared/sync.sh to copy into each Space.
4
+ ========================================================================== */
5
+
6
+ :root {
7
+ --bg: #ffffff;
8
+ --bg-soft: #f7f8fa;
9
+ --bg-code: #f4f5f7;
10
+ --fg: #14161a;
11
+ --fg-muted: #5b6270;
12
+ --fg-faint: #868d9b;
13
+ --line: #e4e6eb;
14
+ --line-strong: #d2d6de;
15
+ --accent: #2f5bd7;
16
+ --accent-soft: #eef2fd;
17
+ --radius: 10px;
18
+ --radius-sm: 6px;
19
+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
20
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
21
+ "Noto Sans", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
22
+ --maxw: 880px;
23
+ }
24
+
25
+ @media (prefers-color-scheme: dark) {
26
+ :root {
27
+ --bg: #0e1014;
28
+ --bg-soft: #161920;
29
+ --bg-code: #14171d;
30
+ --fg: #e7e9ee;
31
+ --fg-muted: #99a1b0;
32
+ --fg-faint: #6f7788;
33
+ --line: #252a33;
34
+ --line-strong: #333a46;
35
+ --accent: #7ba1ff;
36
+ --accent-soft: #172136;
37
+ }
38
+ }
39
+
40
+ *,
41
+ *::before,
42
+ *::after {
43
+ box-sizing: border-box;
44
+ }
45
+
46
+ html {
47
+ -webkit-text-size-adjust: 100%;
48
+ scroll-behavior: smooth;
49
  }
50
 
51
+ body {
52
+ margin: 0;
53
+ background: var(--bg);
54
+ color: var(--fg);
55
+ font-family: var(--sans);
56
+ font-size: 16px;
57
+ line-height: 1.65;
58
+ -webkit-font-smoothing: antialiased;
59
  }
60
 
61
+ a {
62
+ color: var(--accent);
63
+ text-decoration: none;
 
 
64
  }
65
 
66
+ a:hover {
67
+ text-decoration: underline;
 
 
68
  }
69
 
70
+ img,
71
+ video {
72
+ max-width: 100%;
73
+ height: auto;
74
+ display: block;
75
  }
76
 
77
+ hr {
78
+ border: 0;
79
+ border-top: 1px solid var(--line);
80
+ margin: 3rem 0;
81
  }
82
 
83
+ .wrap {
84
+ max-width: var(--maxw);
85
+ margin: 0 auto;
86
+ padding: 0 20px;
 
 
 
 
 
87
  }
88
 
89
+ /* --- header ------------------------------------------------------------- */
90
+
91
+ .site-header {
92
+ border-bottom: 1px solid var(--line);
93
+ background: var(--bg);
94
+ position: sticky;
95
+ top: 0;
96
+ z-index: 20;
97
  }
98
 
99
+ .site-header .wrap {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: space-between;
103
+ gap: 16px;
104
+ height: 56px;
105
+ }
106
+
107
+ .brand {
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 9px;
111
+ font-weight: 600;
112
+ font-size: 0.9rem;
113
+ color: var(--fg);
114
+ letter-spacing: -0.01em;
115
+ }
116
+
117
+ .brand:hover {
118
+ text-decoration: none;
119
+ }
120
+
121
+ .brand-mark {
122
+ width: 20px;
123
+ height: 20px;
124
+ border-radius: 5px;
125
+ background: var(--accent);
126
+ flex: 0 0 auto;
127
+ position: relative;
128
+ }
129
+
130
+ .brand-mark::after {
131
+ content: "";
132
+ position: absolute;
133
+ inset: 6px 4px;
134
+ border-left: 2px solid var(--bg);
135
+ border-right: 2px solid var(--bg);
136
+ opacity: 0.85;
137
+ }
138
+
139
+ .header-nav {
140
+ display: flex;
141
+ align-items: center;
142
+ gap: 20px;
143
+ font-size: 0.86rem;
144
+ }
145
+
146
+ .header-nav a {
147
+ color: var(--fg-muted);
148
+ }
149
+
150
+ .header-nav a:hover {
151
+ color: var(--fg);
152
+ text-decoration: none;
153
+ }
154
+
155
+ .brand span:last-child {
156
+ white-space: nowrap;
157
+ }
158
+
159
+ @media (max-width: 760px) {
160
+ .header-nav a.opt {
161
+ display: none;
162
+ }
163
+ }
164
+
165
+ /* On phones the in-page anchors aren't worth the crowding — the page is short
166
+ enough to scroll, and the one outbound link needs the room. */
167
+ @media (max-width: 560px) {
168
+ .header-nav a.jump {
169
+ display: none;
170
+ }
171
+ }
172
+
173
+ /* --- hero --------------------------------------------------------------- */
174
+
175
+ .hero {
176
+ padding: 56px 0 12px;
177
+ }
178
+
179
+ .eyebrow {
180
+ font-size: 0.72rem;
181
+ font-weight: 600;
182
+ letter-spacing: 0.08em;
183
+ text-transform: uppercase;
184
+ color: var(--fg-faint);
185
+ margin: 0 0 14px;
186
+ }
187
+
188
+ h1 {
189
+ font-size: 2.6rem;
190
+ line-height: 1.15;
191
+ letter-spacing: -0.025em;
192
+ font-weight: 700;
193
+ margin: 0 0 14px;
194
+ }
195
+
196
+ .lede {
197
+ font-size: 1.12rem;
198
+ color: var(--fg-muted);
199
+ margin: 0;
200
+ max-width: 68ch;
201
+ }
202
+
203
+ @media (max-width: 620px) {
204
+ h1 {
205
+ font-size: 2rem;
206
+ }
207
+ .hero {
208
+ padding-top: 36px;
209
+ }
210
+ }
211
+
212
+ /* metadata strip under the title */
213
+ .meta {
214
+ display: flex;
215
+ flex-wrap: wrap;
216
+ gap: 8px 10px;
217
+ margin: 26px 0 0;
218
+ padding: 0;
219
+ list-style: none;
220
+ }
221
+
222
+ .meta li {
223
+ font-size: 0.78rem;
224
+ font-family: var(--mono);
225
+ color: var(--fg-muted);
226
+ border: 1px solid var(--line);
227
+ border-radius: 999px;
228
+ padding: 3px 11px;
229
+ white-space: nowrap;
230
  }
231
 
232
+ .meta li b {
233
+ font-weight: 500;
234
+ color: var(--fg-faint);
 
 
 
235
  }
236
 
237
+ /* --- sections ----------------------------------------------------------- */
238
+
239
+ section {
240
+ padding: 44px 0 0;
241
  }
242
 
243
+ h2 {
244
+ font-size: 1.35rem;
245
+ letter-spacing: -0.015em;
246
+ font-weight: 650;
247
+ margin: 0 0 6px;
248
+ scroll-margin-top: 76px;
249
  }
250
 
251
+ .section-note {
252
+ color: var(--fg-muted);
253
+ margin: 0 0 22px;
254
+ font-size: 0.95rem;
255
+ max-width: 70ch;
 
 
 
256
  }
257
 
258
+ h3 {
259
+ font-size: 1rem;
260
+ font-weight: 620;
261
+ margin: 0 0 6px;
262
+ letter-spacing: -0.005em;
263
  }
264
 
265
+ p {
266
+ margin: 0 0 1rem;
267
+ }
268
+
269
+ /* --- cards -------------------------------------------------------------- */
270
+
271
+ .grid {
272
+ display: grid;
273
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
274
+ gap: 14px;
275
+ margin: 0;
276
  }
277
 
278
+ .card {
279
+ border: 1px solid var(--line);
280
+ border-radius: var(--radius);
281
+ padding: 18px 18px 16px;
282
+ background: var(--bg);
283
  }
284
 
285
+ .card p {
286
+ margin: 0;
287
+ color: var(--fg-muted);
288
+ font-size: 0.9rem;
289
+ line-height: 1.6;
290
  }
291
 
292
+ .card p + p {
293
+ margin-top: 0.6rem;
 
 
 
294
  }
295
 
296
+ /* --- figures ------------------------------------------------------------ */
297
+
298
+ figure {
299
+ margin: 0 0 18px;
300
+ border: 1px solid var(--line);
301
+ border-radius: var(--radius);
302
+ overflow: hidden;
303
+ background: var(--bg-soft);
304
  }
305
 
306
+ figure img,
307
+ figure video {
308
+ width: 100%;
309
+ display: block;
310
  }
311
 
312
+ figcaption {
313
+ font-size: 0.82rem;
314
+ color: var(--fg-muted);
315
+ padding: 10px 14px;
316
+ border-top: 1px solid var(--line);
317
+ background: var(--bg);
318
  }
319
 
320
+ figcaption b {
321
+ color: var(--fg);
322
+ font-weight: 600;
 
 
323
  }
324
 
325
+ .figure-grid {
326
+ display: grid;
327
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
328
+ gap: 14px;
 
329
  }
330
 
331
+ .figure-grid figure {
332
+ margin: 0;
 
 
333
  }
334
 
335
+ /* --- tables ------------------------------------------------------------- */
336
+
337
+ .table-scroll {
338
+ overflow-x: auto;
339
+ border: 1px solid var(--line);
340
+ border-radius: var(--radius);
341
  }
342
 
343
+ table {
344
+ border-collapse: collapse;
345
+ width: 100%;
346
+ font-size: 0.88rem;
347
+ min-width: 480px;
 
348
  }
349
 
350
+ th,
351
+ td {
352
+ text-align: left;
353
+ padding: 10px 14px;
354
+ border-bottom: 1px solid var(--line);
355
  }
356
 
357
+ th {
358
+ font-weight: 600;
359
+ font-size: 0.76rem;
360
+ letter-spacing: 0.05em;
361
+ text-transform: uppercase;
362
+ color: var(--fg-faint);
363
+ background: var(--bg-soft);
364
+ white-space: nowrap;
 
365
  }
366
 
367
+ tbody tr:last-child td {
368
+ border-bottom: 0;
 
369
  }
370
 
371
+ td code {
372
+ font-size: 0.85em;
 
373
  }
374
 
375
+ /* --- code --------------------------------------------------------------- */
376
+
377
+ .code {
378
+ border: 1px solid var(--line);
379
+ border-radius: var(--radius);
380
+ overflow: hidden;
381
+ background: var(--bg-code);
382
+ margin: 0 0 14px;
383
  }
384
 
385
+ .code-tabs {
386
+ display: flex;
387
+ gap: 2px;
388
+ padding: 6px 6px 0;
389
+ border-bottom: 1px solid var(--line);
390
+ background: var(--bg-soft);
391
  }
392
 
393
+ .code-tabs button {
394
+ appearance: none;
395
+ border: 0;
396
+ background: transparent;
397
+ font-family: var(--mono);
398
+ font-size: 0.78rem;
399
+ color: var(--fg-muted);
400
+ padding: 7px 12px;
401
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
402
+ cursor: pointer;
403
+ line-height: 1;
404
  }
405
 
406
+ .code-tabs button:hover {
407
+ color: var(--fg);
 
 
 
408
  }
409
 
410
+ .code-tabs button[aria-selected="true"] {
411
+ background: var(--bg-code);
412
+ color: var(--fg);
413
+ font-weight: 600;
414
+ box-shadow: inset 0 -1px 0 var(--bg-code);
415
  }
416
 
417
+ .code pre {
418
+ margin: 0;
419
+ padding: 16px 18px;
420
+ overflow-x: auto;
421
+ font-family: var(--mono);
422
+ font-size: 0.815rem;
423
+ line-height: 1.65;
424
+ tab-size: 2;
425
  }
426
 
427
+ .code pre[hidden] {
428
+ display: none;
 
 
 
 
 
429
  }
430
 
431
+ code {
432
+ font-family: var(--mono);
433
+ font-size: 0.88em;
 
434
  }
435
 
436
+ p code,
437
+ li code {
438
+ background: var(--bg-code);
439
+ border: 1px solid var(--line);
440
+ border-radius: 4px;
441
+ padding: 1px 5px;
 
 
 
 
442
  }
443
 
444
+ /* --- links list --------------------------------------------------------- */
445
+
446
+ .links {
447
+ display: grid;
448
+ grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
449
+ gap: 10px;
450
+ list-style: none;
451
+ padding: 0;
452
+ margin: 0;
453
+ }
454
+
455
+ .links a {
456
+ display: flex;
457
+ align-items: baseline;
458
+ justify-content: space-between;
459
+ gap: 10px;
460
+ border: 1px solid var(--line);
461
+ border-radius: var(--radius-sm);
462
+ padding: 11px 14px;
463
+ color: var(--fg);
464
+ font-size: 0.9rem;
465
+ font-weight: 500;
466
+ }
467
+
468
+ .links a:hover {
469
+ border-color: var(--line-strong);
470
+ background: var(--bg-soft);
471
+ text-decoration: none;
472
+ }
473
+
474
+ .links .host {
475
+ font-family: var(--mono);
476
+ font-size: 0.72rem;
477
+ color: var(--fg-faint);
478
+ font-weight: 400;
479
+ }
480
+
481
+ /* --- run-it callout (the one place we ask for a click) ------------------- */
482
+
483
+ .callout {
484
+ border: 1px solid var(--line);
485
+ border-left: 3px solid var(--accent);
486
+ background: var(--bg-soft);
487
+ border-radius: var(--radius-sm);
488
+ padding: 16px 18px;
489
+ font-size: 0.92rem;
490
+ color: var(--fg-muted);
491
+ }
492
+
493
+ .callout p {
494
+ margin: 0;
495
+ }
496
+
497
+ .callout p + p {
498
+ margin-top: 0.5rem;
499
+ }
500
+
501
+ .btn-row {
502
+ display: flex;
503
+ flex-wrap: wrap;
504
+ gap: 10px;
505
+ margin-top: 18px;
506
+ }
507
+
508
+ .btn {
509
+ display: inline-flex;
510
+ align-items: center;
511
+ gap: 7px;
512
+ border-radius: var(--radius-sm);
513
+ padding: 9px 16px;
514
+ font-size: 0.89rem;
515
+ font-weight: 550;
516
+ border: 1px solid var(--accent);
517
+ background: var(--accent);
518
+ color: #fff;
519
+ line-height: 1.4;
520
+ }
521
+
522
+ .btn:hover {
523
+ text-decoration: none;
524
+ filter: brightness(1.08);
525
+ }
526
+
527
+ .btn.secondary {
528
+ background: transparent;
529
+ color: var(--fg);
530
+ border-color: var(--line-strong);
531
+ }
532
+
533
+ .btn.secondary:hover {
534
+ background: var(--bg-soft);
535
+ filter: none;
536
+ }
537
+
538
+ @media (prefers-color-scheme: dark) {
539
+ .btn {
540
+ color: #0e1014;
541
+ }
542
+ }
543
+
544
+ /* --- footer ------------------------------------------------------------- */
545
+
546
+ .site-footer {
547
+ margin-top: 64px;
548
+ border-top: 1px solid var(--line);
549
+ padding: 26px 0 44px;
550
+ font-size: 0.83rem;
551
+ color: var(--fg-faint);
552
  }
553
 
554
+ .site-footer .wrap {
555
+ display: flex;
556
+ flex-wrap: wrap;
557
+ gap: 10px 24px;
558
+ align-items: baseline;
559
+ justify-content: space-between;
560
  }
561
 
562
+ .site-footer a {
563
+ color: var(--fg-muted);
 
 
564
  }
565
 
566
+ .site-footer p {
567
+ margin: 0;
568
+ max-width: 60ch;
569
  }
570
 
571
+ .footer-links {
572
+ display: flex;
573
+ gap: 16px;
574
+ flex-wrap: wrap;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  }
tabs.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Code-sample tab switcher. Progressive enhancement: with JS disabled every
2
+ // panel except the first stays hidden via the `hidden` attribute in the markup,
3
+ // so the page still shows one working example.
4
+ document.querySelectorAll(".code-tabs").forEach((tabs) => {
5
+ const buttons = [...tabs.querySelectorAll("button")];
6
+ const panels = buttons.map((b) => document.getElementById(b.dataset.panel));
7
+
8
+ const select = (i) => {
9
+ buttons.forEach((b, j) => b.setAttribute("aria-selected", String(i === j)));
10
+ panels.forEach((p, j) => p && (p.hidden = i !== j));
11
+ };
12
+
13
+ buttons.forEach((b, i) => b.addEventListener("click", () => select(i)));
14
+ select(0);
15
+ });