@repobit/dex-system-design 0.24.18 → 0.24.20

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 (29) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +4 -4
  3. package/src/components/ai-guardian-demo.stories.js +454 -33
  4. package/src/components/bento-grid/bento-grid.css.js +12 -1
  5. package/src/components/bento-grid/bento-grid.js +59 -1
  6. package/src/components/bento-grid/bento-grid.stories.js +45 -0
  7. package/src/components/cards/card.css.js +56 -0
  8. package/src/components/cards/card.js +64 -11
  9. package/src/components/cards/card.stories.js +65 -2
  10. package/src/components/comparison-panel/comparison-panel.css.js +52 -1
  11. package/src/components/comparison-panel/comparison-panel.js +6 -1
  12. package/src/components/compatibility-table/compatibility-table.css.js +29 -13
  13. package/src/components/compatibility-table/compatibility-table.js +13 -7
  14. package/src/components/cta-banner/cta-banner.css.js +11 -0
  15. package/src/components/cta-banner/cta-banner.js +7 -0
  16. package/src/components/footer/footer-lp.css.js +10 -21
  17. package/src/components/footer/footer-lp.js +3 -18
  18. package/src/components/footer/footer-nav-menu.css.js +0 -2
  19. package/src/components/footer/footer.css.js +18 -35
  20. package/src/components/footer/footer.js +5 -1
  21. package/src/components/header/header-code.css.js +208 -10
  22. package/src/components/header/header-code.js +148 -5
  23. package/src/components/header/header-code.stories.js +83 -27
  24. package/src/components/page-switcher/page-switcher.js +1 -1
  25. package/src/components/paragraph/paragraph.js +6 -0
  26. package/src/components/quick-answer/quick-answer.css.js +2 -0
  27. package/src/components/quick-answer/quick-answer.js +1 -1
  28. package/src/components/terminal/terminal-message.css.js +153 -13
  29. package/src/components/terminal/terminal-message.js +37 -11
@@ -4,6 +4,32 @@ export default css`
4
4
  :host {
5
5
  display: block;
6
6
  font-family: var(--typography-fontFamily-sans);
7
+ position: relative;
8
+ background-color: var(--color-neutral-0, #fff);
9
+ overflow: hidden; /* clips the ::after floor grid, which extends past the edges */
10
+ }
11
+
12
+ :host([variant="tint"]) {
13
+ background-image: radial-gradient(ellipse 900px 560px at 84% 8%, rgba(59, 130, 246, 0.16), transparent 65%);
14
+ background-repeat: no-repeat;
15
+ }
16
+
17
+ :host([variant="tint"])::after {
18
+ content: "";
19
+ position: absolute;
20
+ left: -20%;
21
+ right: -20%;
22
+ bottom: -38%;
23
+ height: 72%;
24
+ transform-origin: 50% 0;
25
+ transform: perspective(620px) rotateX(64deg);
26
+ background:
27
+ linear-gradient(#006dff1a 1px, #0000 1px) 0 0 / 100% 44px,
28
+ linear-gradient(90deg, #006dff1a 1px, #0000 1px) 0 0 / 44px 100%;
29
+ -webkit-mask-image: linear-gradient(#0000 0%, #000 45%, #0000 100%);
30
+ mask-image: linear-gradient(#0000 0%, #000 45%, #0000 100%);
31
+ pointer-events: none;
32
+ z-index: 0;
7
33
  }
8
34
 
9
35
  .bd-headercode-container {
@@ -15,6 +41,8 @@ export default css`
15
41
  margin: 0 auto;
16
42
  padding: var(--spacing-48) var(--layout-ensemble-inline-padding, var(--spacing-24));
17
43
  box-sizing: border-box;
44
+ position: relative;
45
+ z-index: 1;
18
46
  }
19
47
 
20
48
  /* === ENTRANCE ANIMATION === */
@@ -53,9 +81,6 @@ export default css`
53
81
  gap: var(--spacing-16);
54
82
  }
55
83
 
56
- /* Slot for a small breadcrumb-style line above the eyebrow
57
- (e.g. "Bitdefender AI skill-scanner"). Empty by default —
58
- collapses to nothing when no content is slotted in. */
59
84
  .bd-headercode-breadcrumb {
60
85
  display: flex;
61
86
  flex-wrap: wrap;
@@ -89,45 +114,200 @@ export default css`
89
114
  }
90
115
 
91
116
  .bd-headercode-cta {
92
- display: flex;
117
+ display: inline-flex;
93
118
  flex-wrap: wrap;
94
119
  align-items: center;
95
120
  gap: var(--spacing-16);
96
121
  margin-top: var(--spacing-8);
97
122
  }
98
123
 
99
- /* Slot for a small status badge next to the CTA buttons (e.g. "AVAILABLE").
100
- Empty by default — collapses to nothing when no content is slotted in. */
101
124
  .bd-headercode-badge {
102
125
  display: inline-flex;
103
126
  align-items: center;
104
- color: var(--color-neutral-500);
127
+ color: var(--color-neutral-600);
105
128
  font-family: var(--typography-fontFamily-mono, monospace);
106
129
  font-size: var(--typography-fontSize-xs, 12px);
107
130
  letter-spacing: 0.05em;
131
+ padding: var(--spacing-4, 4px) var(--spacing-12, 12px);
132
+ border: 1px solid var(--color-neutral-200, #e5e7eb);
133
+ border-radius: var(--radius-full, 999px);
134
+ background: var(--color-neutral-0, #fff);
108
135
  }
109
136
 
110
137
  .bd-headercode-badge:not(:has(*)) {
111
138
  display: none;
112
139
  }
113
140
 
141
+ .bd-headercode-meta {
142
+ display: flex;
143
+ flex-direction: column;
144
+ width: 100%;
145
+ }
146
+
147
+ .bd-headercode-meta-spec {
148
+ color: var(--color-neutral-500);
149
+ margin: 0;
150
+ padding-top: var(--spacing-16);
151
+ }
152
+
153
+ .bd-headercode-meta-facts {
154
+ display: grid;
155
+ grid-template-columns: repeat(3, auto);
156
+ gap: var(--spacing-8) var(--spacing-32);
157
+ margin-top: var(--spacing-40);
158
+ padding-top: var(--spacing-24);
159
+ border-top: 1px solid var(--color-neutral-100);
160
+ }
161
+
162
+ .bd-headercode-meta-fact {
163
+ display: flex;
164
+ align-items: baseline;
165
+ gap: var(--spacing-8, 8px);
166
+ white-space: nowrap;
167
+ }
168
+
169
+ .bd-headercode-meta-fact-label {
170
+ margin: 0;
171
+ color: var(--color-neutral-400);
172
+ text-transform: uppercase;
173
+ }
174
+
175
+ .bd-headercode-meta-fact-value {
176
+ margin: 0;
177
+ color: var(--color-neutral-900);
178
+ }
179
+
180
+ @media (max-width: 480px) {
181
+ .bd-headercode-meta-facts {
182
+ grid-template-columns: repeat(2, auto);
183
+ }
184
+ }
185
+
114
186
  /* === RIGHT COLUMN === */
115
187
  .bd-headercode-right {
116
188
  display: flex;
117
189
  flex-direction: column;
118
190
  align-items: flex-end;
119
191
  gap: var(--spacing-8);
192
+ position: relative;
120
193
  }
121
194
 
122
- .bd-headercode-right ::slotted(*) {
195
+ /* === 3D SCENE === */
196
+ .bd-headercode-scene {
197
+ width: 100%;
198
+ display: flex;
199
+ justify-content: flex-end;
200
+ align-items: center;
201
+ perspective: 1300px;
202
+ min-height: 400px;
203
+ padding: var(--spacing-16, 16px) 0;
204
+ margin: 0;
205
+ position: relative;
206
+ }
207
+
208
+ .bd-headercode-stack {
209
+ position: relative;
123
210
  width: 100%;
124
211
  max-width: 520px;
125
- box-shadow: 0 1px 2px #0003,0 18px 48px #000a1e73
212
+ transform-style: preserve-3d;
213
+ }
214
+
215
+ :host([variant="tint"]) .bd-headercode-stack {
216
+ transform: perspective(1300px)
217
+ rotateY(var(--ry, -10deg))
218
+ rotateX(var(--rx, 6deg))
219
+ rotateZ(1deg);
220
+ transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
221
+ }
222
+
223
+ @media (prefers-reduced-motion: reduce) {
224
+ :host([variant="tint"]) .bd-headercode-stack {
225
+ transition: none;
226
+ }
227
+ }
228
+
229
+ .bd-headercode-right ::slotted(*) {
230
+ display: block;
231
+ width: 100%;
232
+ box-shadow: 0 1px 2px #0003, 0 18px 48px #000a1e73;
233
+ position: relative;
234
+ z-index: 1; /* sits above the rings */
235
+ }
236
+
237
+ @media (prefers-reduced-motion: reduce) {
238
+ :host([variant="tint"]) .bd-headercode-stack {
239
+ transform: none;
240
+ }
126
241
  }
127
242
 
128
243
  .bd-headercode-terminal-caption {
129
244
  color: var(--color-neutral-500);
130
245
  margin: 0;
246
+ position: relative;
247
+ z-index: 1;
248
+ }
249
+
250
+ .bd-headercode-rings {
251
+ position: absolute;
252
+ inset: 0;
253
+ transform-style: preserve-3d;
254
+ pointer-events: none;
255
+ z-index: 0;
256
+ }
257
+
258
+ .bd-headercode-ring {
259
+ position: absolute;
260
+ inset: 0;
261
+ border-radius: 28px;
262
+ border: 1.5px solid rgba(0, 109, 255, calc(0.5 - var(--d) * 0.08));
263
+ background: rgba(0, 109, 255, calc(0.05 - var(--d) * 0.008));
264
+ box-shadow: 0 0 26px rgba(0, 109, 255, calc(0.16 - var(--d) * 0.025));
265
+ animation: bd-headercode-ring-breathe 6s ease-in-out calc(var(--d) * -1.2s) infinite;
266
+ transform: translate(calc(var(--d) * 10px), calc(var(--d) * 8px));
267
+ }
268
+
269
+ @keyframes bd-headercode-ring-breathe {
270
+ 0%, 100% {
271
+ opacity: 0.85;
272
+ }
273
+ 50% {
274
+ opacity: 0.45;
275
+ }
276
+ }
277
+
278
+ @media (prefers-reduced-motion: reduce) {
279
+ .bd-headercode-ring {
280
+ animation: none;
281
+ }
282
+ }
283
+
284
+ /* === FLOATING ANNOTATION PILLS === */
285
+ .bd-headercode-annotation {
286
+ position: absolute;
287
+ z-index: 2;
288
+ pointer-events: none;
289
+ }
290
+
291
+ .bd-headercode-annotation--top {
292
+ top: -14px;
293
+ right: 32px;
294
+ }
295
+
296
+ .bd-headercode-annotation--bottom {
297
+ bottom: -14px;
298
+ left: 16px;
299
+ right: auto;
300
+ }
301
+
302
+ .bd-headercode-annotation:not(:has(*)) {
303
+ display: none;
304
+ }
305
+
306
+ .bd-headercode-annotation ::slotted(*) {
307
+ width: auto;
308
+ max-width: none;
309
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
310
+ pointer-events: auto;
131
311
  }
132
312
 
133
313
  /* === RESPONSIVE === */
@@ -142,12 +322,30 @@ export default css`
142
322
  align-items: stretch;
143
323
  }
144
324
 
145
- .bd-headercode-right ::slotted(*) {
325
+ .bd-headercode-scene {
326
+ min-height: 0;
327
+ perspective: none;
328
+ justify-content: stretch;
329
+ }
330
+
331
+ .bd-headercode-stack {
146
332
  max-width: none;
147
333
  }
148
334
 
335
+ :host([variant="tint"]) .bd-headercode-stack {
336
+ transform: none;
337
+ }
338
+
339
+ .bd-headercode-rings {
340
+ display: none;
341
+ }
342
+
149
343
  .bd-headercode-description {
150
344
  max-width: none;
151
345
  }
346
+
347
+ .bd-headercode-annotation {
348
+ display: none;
349
+ }
152
350
  }
153
351
  `;
@@ -11,7 +11,11 @@ class HeaderCode extends LitElement {
11
11
  eyebrow : { type: String },
12
12
  title : { type: String },
13
13
  description : { type: String },
14
- terminalCaption: { type: String, attribute: "terminal-caption" }
14
+ terminalCaption: { type: String, attribute: "terminal-caption" },
15
+ variant : { type: String, reflect: true },
16
+ metaSpec : { type: String, attribute: "meta-spec" },
17
+ metaFacts : { type: Array },
18
+ _hasBadge : { state: true }
15
19
  };
16
20
 
17
21
  static styles = [tokens, headerCodeCSS];
@@ -22,6 +26,131 @@ class HeaderCode extends LitElement {
22
26
  this.title = "";
23
27
  this.description = "";
24
28
  this.terminalCaption = "";
29
+ this.variant = "default";
30
+ this.metaSpec = "";
31
+ this.metaFacts = [];
32
+ this._hasBadge = false;
33
+
34
+ this._scene = null;
35
+ this._onPointerMove = this._onPointerMove.bind(this);
36
+ this._onPointerLeave = this._onPointerLeave.bind(this);
37
+ }
38
+
39
+ firstUpdated() {
40
+ this._bindScene();
41
+ }
42
+
43
+ updated(changed) {
44
+ if (changed.has("variant")) this._bindScene();
45
+ }
46
+
47
+ disconnectedCallback() {
48
+ super.disconnectedCallback();
49
+ this._unbindScene();
50
+ }
51
+
52
+ _bindScene() {
53
+ this._unbindScene();
54
+ if (this.variant !== "tint") return;
55
+
56
+ this._scene = this.shadowRoot?.querySelector(".bd-headercode-scene") || null;
57
+ if (!this._scene) return;
58
+
59
+ this._scene.addEventListener("pointermove", this._onPointerMove);
60
+ this._scene.addEventListener("pointerleave", this._onPointerLeave);
61
+ }
62
+
63
+ _unbindScene() {
64
+ if (!this._scene) return;
65
+ this._scene.removeEventListener("pointermove", this._onPointerMove);
66
+ this._scene.removeEventListener("pointerleave", this._onPointerLeave);
67
+ this._scene = null;
68
+ }
69
+
70
+ _onPointerMove(event) {
71
+ const rect = this._scene.getBoundingClientRect();
72
+ const px = (event.clientX - rect.left) / rect.width - 0.5;
73
+ const py = (event.clientY - rect.top) / rect.height - 0.5;
74
+
75
+ const ry = px * 18; // deg
76
+ const rx = -py * 12; // deg
77
+
78
+ this.style.setProperty("--rx", `${rx.toFixed(2)}deg`);
79
+ this.style.setProperty("--ry", `${ry.toFixed(2)}deg`);
80
+ }
81
+
82
+ _onPointerLeave() {
83
+ this.style.removeProperty("--rx");
84
+ this.style.removeProperty("--ry");
85
+ }
86
+
87
+ _onBadgeSlotChange(event) {
88
+ this._hasBadge = event.target.assignedNodes({ flatten: true }).length > 0;
89
+ }
90
+
91
+
92
+ _renderRings() {
93
+ if (this.variant !== "tint") return nothing;
94
+
95
+ return html`
96
+ <div class="bd-headercode-rings" aria-hidden="true">
97
+ <span class="bd-headercode-ring" style="--d:1"></span>
98
+ <span class="bd-headercode-ring" style="--d:2"></span>
99
+ <span class="bd-headercode-ring" style="--d:3"></span>
100
+ <span class="bd-headercode-ring" style="--d:4"></span>
101
+ <span class="bd-headercode-ring" style="--d:5"></span>
102
+ </div>
103
+ `;
104
+ }
105
+
106
+ _renderBadge() {
107
+ const badgeSlot = html`<slot name="badge" @slotchange=${this._onBadgeSlotChange}></slot>`;
108
+
109
+ return this._hasBadge
110
+ ? html`<span class="bd-headercode-badge">${badgeSlot}</span>`
111
+ : html`<span class="bd-headercode-badge-slot-holder" hidden>${badgeSlot}</span>`;
112
+ }
113
+
114
+ _renderMeta() {
115
+ const hasSpec = !!this.metaSpec;
116
+ const hasFacts = this.metaFacts && this.metaFacts.length > 0;
117
+
118
+ if (!hasSpec && !hasFacts) return nothing;
119
+
120
+ const monoFontFamily = "'IBM Plex Mono', ui-monospace, SFMono-Regular, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
121
+
122
+ return html`
123
+ <div class="bd-headercode-meta">
124
+ ${hasSpec
125
+ ? html`<bd-p
126
+ kind="xsmall"
127
+ class="bd-headercode-meta-spec"
128
+ font-family=${monoFontFamily}
129
+ letter-spacing=".1em"
130
+ >${this.metaSpec}</bd-p>`
131
+ : nothing}
132
+
133
+ ${hasFacts
134
+ ? html`
135
+ <div class="bd-headercode-meta-facts">
136
+ ${this.metaFacts.map(
137
+ (fact) => html`
138
+ <div class="bd-headercode-meta-fact">
139
+ <bd-p
140
+ kind="xsmall"
141
+ class="bd-headercode-meta-fact-label"
142
+ font-family=${monoFontFamily}
143
+ letter-spacing=".1em"
144
+ >${fact.label}</bd-p>
145
+ <bd-p kind="regular" font-weight="700" class="bd-headercode-meta-fact-value">${fact.value}</bd-p>
146
+ </div>
147
+ `
148
+ )}
149
+ </div>
150
+ `
151
+ : nothing}
152
+ </div>
153
+ `;
25
154
  }
26
155
 
27
156
  render() {
@@ -47,14 +176,28 @@ class HeaderCode extends LitElement {
47
176
  <div class="bd-headercode-cta">
48
177
  <slot name="primary-button"></slot>
49
178
  <slot name="secondary-button"></slot>
50
- <span class="bd-headercode-badge">
51
- <slot name="badge"></slot>
52
- </span>
179
+ ${this._renderBadge()}
53
180
  </div>
181
+
182
+ ${this._renderMeta()}
54
183
  </div>
55
184
 
56
185
  <div class="bd-headercode-right">
57
- <slot name="terminal-content"></slot>
186
+ <div class="bd-headercode-annotation bd-headercode-annotation--top">
187
+ <slot name="annotation-top"></slot>
188
+ </div>
189
+
190
+
191
+ <div class="bd-headercode-scene">
192
+ <div class="bd-headercode-stack">
193
+ ${this._renderRings()}
194
+ <slot name="terminal-content"></slot>
195
+ </div>
196
+ </div>
197
+
198
+ <div class="bd-headercode-annotation bd-headercode-annotation--bottom">
199
+ <slot name="annotation-bottom"></slot>
200
+ </div>
58
201
 
59
202
  ${this.terminalCaption
60
203
  ? html`<bd-p kind="small" class="bd-headercode-terminal-caption" font-family="'IBM Plex Mono', ui-monospace, SFMono-Regular, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace"
@@ -14,53 +14,51 @@ export default {
14
14
  component: `
15
15
  **HeaderCode** is a hero/header variant pairing marketing copy on the left with a
16
16
  terminal-style code/demo window on the right. Unlike \`bd-header\`, it has no pricing —
17
- just eyebrow, title, description, two CTA buttons, and a slotted terminal.
17
+ just eyebrow, title, description, two CTA buttons, an optional meta row, and a slotted terminal.
18
18
 
19
19
  The terminal window — including the traffic-light titlebar dots — is owned by
20
20
  \`<bd-terminal-message>\`. \`bd-header-code\` additionally renders an optional small
21
21
  caption **below** the terminal box via \`terminal-caption\`.
22
22
 
23
+ \`variant="tint"\` swaps the plain white background for a light-blue gradient with a
24
+ dot-grid pattern, and gives the \`badge\` slot a bordered pill treatment.
25
+
23
26
  ### Usage
24
27
  \`\`\`html
25
28
  <bd-header-code
26
- eyebrow="Security for autonomous AI agents"
27
- title="Bitdefender AI Guardian"
28
- description="A security layer between your AI agents and the actions they take."
29
- terminal-caption="AI Guardian CLI · first-run setup"
29
+ variant="tint"
30
+ eyebrow="Privacy for autonomous AI agents"
31
+ title="Bitdefender VPN for AI Agents"
32
+ description="Each prompt gets a fresh, disposable container scoped to a specific location."
33
+ terminal-caption="// illustrative — every connection closes when the prompt ends"
30
34
  >
31
- <bd-button-link slot="primary-button" label="Download BETA Today" kind="primary">Download BETA Today</bd-button-link>
35
+ <span slot="badge">EXTERNAL BETA</span>
36
+ <bd-button-link slot="primary-button" label="Download for macOS" kind="primary">Download for macOS</bd-button-link>
32
37
  <bd-button-link slot="secondary-button" label="See how it works" kind="outline">See how it works</bd-button-link>
33
38
 
34
- <bd-terminal-message
35
- slot="terminal-content"
36
- show-titlebar
37
- panel-label="Welcome"
38
- icon="👋"
39
- greeting="Hi, I'm AI Guardian."
40
- intro="I keep an eye on what your AI assistant can access and do:"
41
- .items=\${["which tools it can use", "which credentials to watch", "which MCP servers to trust"]}
42
- footer-bold="Let's get started."
43
- footer="It only takes a minute, and nothing changes until you confirm it."
44
- show-cursor
45
- hint="[Enter] to continue · [esc] to cancel"
46
- ></bd-terminal-message>
39
+ <div slot="meta">…facts row…</div>
40
+
41
+ <bd-terminal-message slot="terminal-content" show-titlebar></bd-terminal-message>
47
42
  </bd-header-code>
48
43
  \`\`\`
49
44
 
50
45
  ### Attributes
51
- | Attribute | Type | Default | Description |
52
- |----------------------|--------|---------|-----------------------------------------------------|
53
- | \`eyebrow\` | String | \`""\` | Small label above the title |
54
- | \`title\` | String | \`""\` | Main heading (can be overridden via \`title\` slot) |
55
- | \`description\` | String | \`""\` | Supporting paragraph under the title |
56
- | \`terminal-caption\` | String | \`""\` | Small caption rendered below the terminal box |
46
+ | Attribute | Type | Default | Description |
47
+ |----------------------|--------|--------------|---------------------------------------------------------|
48
+ | \`eyebrow\` | String | \`""\` | Small label above the title |
49
+ | \`title\` | String | \`""\` | Main heading (can be overridden via \`title\` slot) |
50
+ | \`description\` | String | \`""\` | Supporting paragraph under the title |
51
+ | \`terminal-caption\` | String | \`""\` | Small caption rendered below the terminal box |
52
+ | \`variant\` | String | \`"default"\` | \`"default"\` or \`"tint"\` — background treatment |
57
53
 
58
54
  ### Slots
59
55
  | Slot | Description |
60
56
  |---------------------|-----------------------------------------------|
61
57
  | \`title\` | Overrides the \`title\` attribute if content is provided |
58
+ | \`badge\` | Small pill badge next to the CTAs (e.g. "AVAILABLE", "EXTERNAL BETA") |
62
59
  | \`primary-button\` | The main CTA (typically \`bd-button-link\`) |
63
60
  | \`secondary-button\` | The secondary CTA |
61
+ | \`meta\` | Freeform row under the CTAs — spec line, facts list, etc. |
64
62
  | \`terminal-content\` | Freeform content rendered on the right (typically a \`bd-terminal-message\`) |
65
63
  `
66
64
  }
@@ -70,7 +68,8 @@ caption **below** the terminal box via \`terminal-caption\`.
70
68
  eyebrow : { control: "text", table: { category: "Content" } },
71
69
  title : { control: "text", table: { category: "Content" } },
72
70
  description : { control: "text", table: { category: "Content" } },
73
- terminalCaption: { control: "text", table: { category: "Terminal" } }
71
+ terminalCaption: { control: "text", table: { category: "Terminal" } },
72
+ variant : { control: { type: "select" }, options: ["default", "tint"], table: { category: "Appearance" } }
74
73
  }
75
74
  };
76
75
 
@@ -92,6 +91,7 @@ const terminalContent = html`
92
91
 
93
92
  const render = (args) => html`
94
93
  <bd-header-code
94
+ variant=${args.variant}
95
95
  eyebrow=${args.eyebrow}
96
96
  title=${args.title}
97
97
  description=${args.description}
@@ -122,6 +122,7 @@ const render = (args) => html`
122
122
  export const Default = {
123
123
  name: "Default — AI Guardian",
124
124
  args: {
125
+ variant : "default",
125
126
  eyebrow : "Security for autonomous AI agents",
126
127
  title : "Bitdefender AI Guardian",
127
128
  description : "A security layer between your AI agents and the actions they take — policies, guardrails, and monitoring that let agents run safely.",
@@ -137,9 +138,60 @@ export const Default = {
137
138
  }
138
139
  };
139
140
 
141
+ export const TintVariant = {
142
+ name: "Tint Variant — VPN",
143
+ args: {
144
+ variant : "tint",
145
+ eyebrow : "Privacy for autonomous AI agents",
146
+ title : "Bitdefender VPN for AI Agents",
147
+ description : "Each prompt gets a fresh, disposable container scoped to a specific location, so your agent's traffic isn't tied to your home connection or your personal IP.",
148
+ terminalCaption: "// illustrative — every connection closes when the prompt ends"
149
+ },
150
+ render: (args) => html`
151
+ <bd-header-code
152
+ variant=${args.variant}
153
+ eyebrow=${args.eyebrow}
154
+ title=${args.title}
155
+ description=${args.description}
156
+ terminal-caption=${args.terminalCaption}
157
+ >
158
+ <span slot="breadcrumb">Bitdefender AI</span>
159
+ <span slot="breadcrumb">vpn-for-agents</span>
160
+
161
+ <bd-button-link slot="primary-button" label="Download for macOS" kind="primary" size="md" font-size="18px" font-weight="600">Download for macOS</bd-button-link>
162
+ <bd-button-link slot="secondary-button" label="See how it works" kind="primary-outline" size="md" font-size="18px" font-weight="600">See how it works</bd-button-link>
163
+ <span slot="badge">EXTERNAL BETA</span>
164
+
165
+ <bd-terminal-message
166
+ slot="terminal-content"
167
+ show-titlebar
168
+ .lines=${[
169
+ { type: "strong", text: "VPN for AI Agents · connection log" },
170
+ { type: "sep" },
171
+ { type: "dim", text: "MCP vpn_connect(location: frankfurt) [Open]" },
172
+ { type: "dim", text: "REQUEST agent reaches insurer-compare.com [Server IP]" },
173
+ { type: "dim", text: "NETWORK hotel Wi-Fi, unencrypted [Protected]" },
174
+ { type: "dim", text: "MCP vpn_safefetch(location: frankfurt, url:" },
175
+ { type: "dim", text: " insurer-compare.com) [Scanned]" },
176
+ { type: "dim", text: "MCP vpn_disconnect(location: frankfurt)" },
177
+ { type: "dim", text: " — prompt ends [Closed, session cleared]" }
178
+ ]}
179
+ ></bd-terminal-message>
180
+ </bd-header-code>
181
+ `,
182
+ parameters: {
183
+ docs: {
184
+ description: {
185
+ story: "`variant=\"tint\"` swaps the plain background for the light-blue gradient + dot-grid treatment, and the badge slot renders as a bordered pill. The floating annotation callouts seen in the reference design (\"exit: frankfurt · server IP\", \"tunnel closed — session cleared\") aren't reproduced here — they'd need to be a feature of `bd-terminal-message` itself, which isn't in scope for `bd-header-code`."
186
+ }
187
+ }
188
+ }
189
+ };
190
+
140
191
  export const NoEyebrow = {
141
192
  name: "No Eyebrow",
142
193
  args: {
194
+ variant : "default",
143
195
  eyebrow : "",
144
196
  title : "Bitdefender AI Guardian",
145
197
  description : "A security layer between your AI agents and the actions they take.",
@@ -158,6 +210,7 @@ export const NoEyebrow = {
158
210
  export const SingleButton = {
159
211
  name: "Single CTA",
160
212
  args: {
213
+ variant : "default",
161
214
  eyebrow : "Security for autonomous AI agents",
162
215
  title : "Bitdefender AI Guardian",
163
216
  description : "A security layer between your AI agents and the actions they take.",
@@ -165,6 +218,7 @@ export const SingleButton = {
165
218
  },
166
219
  render: (args) => html`
167
220
  <bd-header-code
221
+ variant=${args.variant}
168
222
  eyebrow=${args.eyebrow}
169
223
  title=${args.title}
170
224
  description=${args.description}
@@ -192,6 +246,7 @@ export const SingleButton = {
192
246
  export const NoCaption = {
193
247
  name: "No Caption",
194
248
  args: {
249
+ variant : "default",
195
250
  eyebrow : "Security for autonomous AI agents",
196
251
  title : "Bitdefender AI Guardian",
197
252
  description : "A security layer between your AI agents and the actions they take.",
@@ -210,6 +265,7 @@ export const NoCaption = {
210
265
  export const Playground = {
211
266
  name: "Playground",
212
267
  args: {
268
+ variant : "default",
213
269
  eyebrow : "Security for autonomous AI agents",
214
270
  title : "Bitdefender AI Guardian",
215
271
  description : "A security layer between your AI agents and the actions they take — policies, guardrails, and monitoring that let agents run safely.",
@@ -219,7 +275,7 @@ export const Playground = {
219
275
  parameters: {
220
276
  docs: {
221
277
  description: {
222
- story: "Fully interactive story — use the Controls panel to edit eyebrow, title, description, and terminal caption live."
278
+ story: "Fully interactive story — use the Controls panel to edit eyebrow, title, description, variant, and terminal caption live."
223
279
  }
224
280
  }
225
281
  }
@@ -19,7 +19,7 @@ class BdPageSwitcher extends LitElement {
19
19
  render() {
20
20
  return html`
21
21
  <div class="inner">
22
- <bd-ai-icon class="logo" width="126" height="24"></bd-ai-icon>
22
+ <bd-ai-icon class="logo" width="200" height="24"></bd-ai-icon>
23
23
 
24
24
  <nav aria-label="Product navigation">
25
25
  ${this.items.map(({ title, href, active }) => html`