feat(landing): update cyber hero visuals and theme sync
This commit is contained in:
parent
50d4b6b0ca
commit
9eff462422
33 changed files with 1151 additions and 320 deletions
BIN
landing/assets/images/footer/robot-lead-lounge-v1.webp
Normal file
BIN
landing/assets/images/footer/robot-lead-lounge-v1.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
BIN
landing/assets/images/hero/robots/robot-avatar-cyan-cat-v1.webp
Normal file
BIN
landing/assets/images/hero/robots/robot-avatar-cyan-cat-v1.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
BIN
landing/assets/images/hero/robots/robot-avatar-red-flame-v1.webp
Normal file
BIN
landing/assets/images/hero/robots/robot-avatar-red-flame-v1.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 194 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 185 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 182 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
|
|
@ -14,17 +14,218 @@
|
|||
--cyber-muted: #9ba8c7;
|
||||
--cyber-border-cyan: rgba(0, 234, 255, 0.42);
|
||||
--cyber-border-magenta: rgba(255, 43, 255, 0.42);
|
||||
--cyber-panel-bg:
|
||||
linear-gradient(135deg, rgba(5, 14, 31, 0.9), rgba(3, 10, 22, 0.64));
|
||||
--cyber-panel-shadow:
|
||||
0 0 0 1px rgba(47, 125, 255, 0.12) inset,
|
||||
0 0 24px rgba(0, 234, 255, 0.12);
|
||||
--cyber-hero-bg:
|
||||
radial-gradient(circle at 76% 30%, rgba(0, 234, 255, 0.14), transparent 30%),
|
||||
radial-gradient(circle at 86% 70%, rgba(255, 43, 255, 0.14), transparent 34%),
|
||||
linear-gradient(180deg, var(--cyber-bg-0), var(--cyber-bg-1) 58%, var(--cyber-bg-0));
|
||||
--cyber-monterey-bg:
|
||||
radial-gradient(circle at 76% 22%, rgba(138, 47, 255, 0.76), transparent 30%),
|
||||
radial-gradient(circle at 18% 76%, rgba(37, 8, 128, 0.72), transparent 36%),
|
||||
linear-gradient(180deg, #180061 0%, #3200a2 46%, #130042 100%);
|
||||
--cyber-monterey-before-bg:
|
||||
radial-gradient(circle at 18% 34%, rgba(2, 5, 13, 0.62), rgba(2, 5, 13, 0.14) 34%, transparent 62%),
|
||||
linear-gradient(90deg, rgba(2, 5, 13, 0.48) 0%, rgba(2, 5, 13, 0.17) 42%, rgba(2, 5, 13, 0.04) 64%, rgba(2, 5, 13, 0.3) 100%);
|
||||
--cyber-monterey-after-bg:
|
||||
linear-gradient(180deg, rgba(2, 5, 13, 0.92) 0%, rgba(2, 5, 13, 0.62) 15%, rgba(2, 5, 13, 0.08) 44%, rgba(2, 5, 13, 0.68) 100%),
|
||||
radial-gradient(circle at 64% 42%, transparent 0 26%, rgba(2, 5, 13, 0.24) 70%, rgba(2, 5, 13, 0.54) 100%);
|
||||
--cyber-monterey-canvas-opacity: 1;
|
||||
--cyber-monterey-canvas-filter: blur(4px) saturate(1.22) brightness(1.08) contrast(1.08);
|
||||
--cyber-monterey-canvas-blend: normal;
|
||||
--cyber-background-bg:
|
||||
radial-gradient(circle at 72% 28%, rgba(0, 234, 255, 0.1), transparent 30%),
|
||||
radial-gradient(circle at 88% 62%, rgba(255, 43, 255, 0.1), transparent 32%),
|
||||
linear-gradient(90deg, transparent 0 64px, rgba(0, 234, 255, 0.045) 65px 66px, transparent 67px 160px),
|
||||
linear-gradient(180deg, rgba(2, 5, 13, 0.66) 0%, rgba(2, 5, 13, 0.2) 38%, rgba(2, 5, 13, 0.78) 100%);
|
||||
--cyber-background-opacity: 0.58;
|
||||
--cyber-background-before-bg:
|
||||
linear-gradient(90deg, transparent 0 8%, rgba(0, 234, 255, 0.14) 8.1% 8.22%, transparent 8.34% 18%, rgba(255, 43, 255, 0.12) 18.1% 18.22%, transparent 18.34% 31%, rgba(0, 234, 255, 0.11) 31.1% 31.24%, transparent 31.36% 44%, rgba(47, 125, 255, 0.12) 44.1% 44.2%, transparent 44.34% 62%, rgba(255, 43, 255, 0.1) 62.1% 62.22%, transparent 62.34% 78%, rgba(0, 234, 255, 0.12) 78.1% 78.22%, transparent 78.34%),
|
||||
repeating-linear-gradient(90deg, transparent 0 78px, rgba(0, 234, 255, 0.05) 80px 82px, transparent 84px 116px),
|
||||
linear-gradient(to top, rgba(3, 12, 27, 0.66) 0%, rgba(3, 12, 27, 0.42) 8%, rgba(3, 12, 27, 0.16) 17%, transparent 28%),
|
||||
linear-gradient(to top, rgba(5, 20, 44, 0.5) 0%, rgba(5, 20, 44, 0.28) 12%, rgba(5, 20, 44, 0.12) 24%, transparent 36%),
|
||||
linear-gradient(to top, rgba(4, 17, 38, 0.38) 0%, rgba(4, 17, 38, 0.22) 18%, rgba(4, 17, 38, 0.08) 34%, transparent 48%);
|
||||
--cyber-background-before-opacity: 0.62;
|
||||
--cyber-background-before-blend: screen;
|
||||
--cyber-background-after-bg:
|
||||
repeating-linear-gradient(90deg, transparent 0 34px, rgba(0, 234, 255, 0.12) 35px 36px, transparent 37px 110px),
|
||||
repeating-linear-gradient(180deg, transparent 0 28px, rgba(255, 43, 255, 0.1) 29px 30px, transparent 31px 78px),
|
||||
linear-gradient(90deg, transparent, rgba(0, 234, 255, 0.08), transparent);
|
||||
--cyber-background-after-opacity: 0.26;
|
||||
--cyber-background-after-blend: screen;
|
||||
--cyber-wash-bg:
|
||||
radial-gradient(circle at 18% 44%, rgba(2, 5, 13, 0.48), rgba(2, 5, 13, 0.22) 34%, transparent 58%),
|
||||
linear-gradient(90deg, rgba(2, 5, 13, 0.42) 0%, rgba(2, 5, 13, 0.28) 34%, rgba(2, 5, 13, 0.08) 66%, rgba(2, 5, 13, 0.3) 100%),
|
||||
linear-gradient(180deg, rgba(2, 5, 13, 0.18), rgba(2, 5, 13, 0.08) 58%, rgba(2, 5, 13, 0.92));
|
||||
--cyber-gridlines-bg:
|
||||
linear-gradient(rgba(0, 234, 255, 0.055) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 234, 255, 0.045) 1px, transparent 1px);
|
||||
--cyber-gridlines-opacity: 0.16;
|
||||
--cyber-scanlines-bg: repeating-linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.08) 0,
|
||||
rgba(255, 255, 255, 0.08) 1px,
|
||||
transparent 1px,
|
||||
transparent 4px
|
||||
);
|
||||
--cyber-scanlines-opacity: 0.11;
|
||||
--cyber-copy-aura: radial-gradient(circle at 28% 38%, rgba(2, 5, 13, 0.82), rgba(2, 5, 13, 0.36) 62%, transparent 78%);
|
||||
--cyber-title-color: rgba(244, 247, 255, 0.96);
|
||||
--cyber-description-color: rgba(222, 229, 255, 0.84);
|
||||
--cyber-action-primary-color: var(--cyber-bg-0);
|
||||
--cyber-action-secondary-bg: rgba(3, 10, 22, 0.56);
|
||||
--cyber-action-secondary-hover-bg: rgba(0, 234, 255, 0.08);
|
||||
--cyber-release-color: rgba(244, 247, 255, 0.62);
|
||||
--cyber-scene-floor-bg:
|
||||
radial-gradient(ellipse at 58% 84%, rgba(255, 43, 255, 0.24), transparent 18%),
|
||||
radial-gradient(ellipse at 56% 84%, rgba(0, 234, 255, 0.18), transparent 32%),
|
||||
repeating-radial-gradient(ellipse at 58% 84%, rgba(0, 234, 255, 0.1) 0 1px, transparent 1px 20px);
|
||||
--cyber-scene-floor-opacity: 0.48;
|
||||
--cyber-scene-foreground-bg:
|
||||
linear-gradient(90deg, transparent 0 4%, rgba(0, 234, 255, 0.08) 4.1%, transparent 4.4%),
|
||||
linear-gradient(180deg, transparent 0 88%, rgba(255, 43, 255, 0.08));
|
||||
--cyber-scene-foreground-opacity: 0.72;
|
||||
--cyber-video-frame-bg: rgba(2, 6, 16, 0.82);
|
||||
--cyber-video-content-bg: rgba(2, 6, 16, 0.94);
|
||||
--cyber-card-text: rgba(244, 247, 255, 0.84);
|
||||
--cyber-card-muted: rgba(222, 229, 255, 0.72);
|
||||
--cyber-card-subtle: rgba(222, 229, 255, 0.62);
|
||||
--cyber-card-inset: rgba(255, 255, 255, 0.06);
|
||||
--cyber-feature-shell-bg:
|
||||
radial-gradient(ellipse at 50% 62%, rgba(0, 234, 255, 0.08), transparent 56%),
|
||||
radial-gradient(ellipse at 78% 52%, rgba(255, 43, 255, 0.08), transparent 48%),
|
||||
linear-gradient(180deg, transparent 0%, rgba(5, 17, 40, 0.08) 30%, rgba(5, 14, 31, 0.18) 52%, rgba(5, 17, 40, 0.08) 72%, transparent 100%);
|
||||
--cyber-feature-rail-bg:
|
||||
linear-gradient(180deg, rgba(3, 10, 22, 0.34) 0%, rgba(5, 14, 31, 0.74) 30%, rgba(5, 14, 31, 0.72) 70%, rgba(3, 10, 22, 0.34) 100%),
|
||||
linear-gradient(135deg, rgba(5, 14, 31, 0.58), rgba(3, 10, 22, 0.42));
|
||||
--cyber-feature-rail-shadow:
|
||||
0 0 0 1px rgba(47, 125, 255, 0.1) inset,
|
||||
0 -28px 46px rgba(2, 5, 13, 0.14),
|
||||
0 30px 58px rgba(2, 5, 13, 0.18),
|
||||
0 0 24px rgba(0, 234, 255, 0.1);
|
||||
--cyber-feature-divider: rgba(0, 234, 255, 0.16);
|
||||
--cyber-feature-title: rgba(244, 247, 255, 0.94);
|
||||
--cyber-feature-text: rgba(222, 229, 255, 0.62);
|
||||
--cyber-radius-xs: 4px;
|
||||
--cyber-radius-sm: 6px;
|
||||
--cyber-radius-md: 8px;
|
||||
--cyber-frame-cut: 18px;
|
||||
}
|
||||
|
||||
.v-theme--light .cyber-hero {
|
||||
--cyber-bg-0: #f8fcff;
|
||||
--cyber-bg-1: #eaf7fb;
|
||||
--cyber-panel-weak: rgba(255, 255, 255, 0.68);
|
||||
--cyber-panel: rgba(255, 255, 255, 0.78);
|
||||
--cyber-panel-strong: rgba(255, 255, 255, 0.92);
|
||||
--cyber-cyan: #008fb3;
|
||||
--cyber-blue: #2563eb;
|
||||
--cyber-magenta: #b832d8;
|
||||
--cyber-violet: #7c3aed;
|
||||
--cyber-text: #132238;
|
||||
--cyber-muted: #596b83;
|
||||
--cyber-border-cyan: rgba(8, 145, 178, 0.34);
|
||||
--cyber-border-magenta: rgba(184, 50, 216, 0.28);
|
||||
--cyber-panel-bg:
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(237, 249, 252, 0.68));
|
||||
--cyber-panel-shadow:
|
||||
0 0 0 1px rgba(8, 145, 178, 0.1) inset,
|
||||
0 18px 48px rgba(8, 145, 178, 0.12),
|
||||
0 0 22px rgba(184, 50, 216, 0.07);
|
||||
--cyber-hero-bg:
|
||||
radial-gradient(circle at 76% 30%, rgba(0, 178, 214, 0.14), transparent 32%),
|
||||
radial-gradient(circle at 86% 70%, rgba(184, 50, 216, 0.13), transparent 36%),
|
||||
linear-gradient(180deg, #fbfdff 0%, #edf8fb 56%, #f8fcff 100%);
|
||||
--cyber-monterey-bg:
|
||||
radial-gradient(circle at 78% 24%, rgba(113, 185, 255, 0.28), transparent 31%),
|
||||
radial-gradient(circle at 22% 72%, rgba(221, 170, 255, 0.24), transparent 38%),
|
||||
radial-gradient(circle at 8% 32%, rgba(101, 218, 255, 0.18), transparent 34%),
|
||||
linear-gradient(180deg, #f8fcff 0%, #eaf7fb 48%, #fbf7ff 100%);
|
||||
--cyber-monterey-before-bg:
|
||||
radial-gradient(circle at 18% 34%, rgba(255, 255, 255, 0.46), rgba(255, 255, 255, 0.14) 34%, transparent 62%),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0.08) 42%, rgba(255, 255, 255, 0.03) 64%, rgba(237, 247, 252, 0.2) 100%);
|
||||
--cyber-monterey-after-bg:
|
||||
linear-gradient(180deg, rgba(248, 252, 255, 0.52) 0%, rgba(248, 252, 255, 0.22) 18%, rgba(248, 252, 255, 0.04) 48%, rgba(248, 252, 255, 0.58) 100%),
|
||||
radial-gradient(circle at 64% 42%, transparent 0 26%, rgba(255, 255, 255, 0.16) 70%, rgba(235, 247, 252, 0.42) 100%);
|
||||
--cyber-monterey-canvas-opacity: 1;
|
||||
--cyber-monterey-canvas-filter: blur(1px) saturate(1.34) brightness(1.12) contrast(1.16);
|
||||
--cyber-monterey-canvas-blend: multiply;
|
||||
--cyber-background-bg:
|
||||
radial-gradient(circle at 72% 28%, rgba(0, 178, 214, 0.12), transparent 31%),
|
||||
radial-gradient(circle at 88% 62%, rgba(184, 50, 216, 0.1), transparent 33%),
|
||||
linear-gradient(90deg, transparent 0 64px, rgba(8, 145, 178, 0.06) 65px 66px, transparent 67px 160px),
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0.08) 38%, rgba(237, 247, 252, 0.64) 100%);
|
||||
--cyber-background-opacity: 0.5;
|
||||
--cyber-background-before-bg:
|
||||
linear-gradient(90deg, transparent 0 8%, rgba(8, 145, 178, 0.16) 8.1% 8.22%, transparent 8.34% 18%, rgba(184, 50, 216, 0.12) 18.1% 18.22%, transparent 18.34% 31%, rgba(8, 145, 178, 0.13) 31.1% 31.24%, transparent 31.36% 44%, rgba(37, 99, 235, 0.12) 44.1% 44.2%, transparent 44.34% 62%, rgba(184, 50, 216, 0.1) 62.1% 62.22%, transparent 62.34% 78%, rgba(8, 145, 178, 0.12) 78.1% 78.22%, transparent 78.34%),
|
||||
repeating-linear-gradient(90deg, transparent 0 78px, rgba(8, 145, 178, 0.06) 80px 82px, transparent 84px 116px),
|
||||
linear-gradient(to top, rgba(8, 145, 178, 0.12) 0%, rgba(8, 145, 178, 0.07) 13%, transparent 31%),
|
||||
linear-gradient(to top, rgba(184, 50, 216, 0.1) 0%, rgba(184, 50, 216, 0.05) 16%, transparent 38%),
|
||||
linear-gradient(to top, rgba(37, 99, 235, 0.09) 0%, rgba(37, 99, 235, 0.04) 20%, transparent 48%);
|
||||
--cyber-background-before-opacity: 0.58;
|
||||
--cyber-background-before-blend: multiply;
|
||||
--cyber-background-after-bg:
|
||||
repeating-linear-gradient(90deg, transparent 0 34px, rgba(8, 145, 178, 0.1) 35px 36px, transparent 37px 110px),
|
||||
repeating-linear-gradient(180deg, transparent 0 28px, rgba(184, 50, 216, 0.08) 29px 30px, transparent 31px 78px),
|
||||
linear-gradient(90deg, transparent, rgba(8, 145, 178, 0.07), transparent);
|
||||
--cyber-background-after-opacity: 0.18;
|
||||
--cyber-background-after-blend: multiply;
|
||||
--cyber-wash-bg:
|
||||
radial-gradient(circle at 18% 44%, rgba(255, 255, 255, 0.28), rgba(255, 255, 255, 0.12) 36%, transparent 60%),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.18) 0%, rgba(237, 248, 252, 0.1) 36%, rgba(255, 255, 255, 0.03) 68%, rgba(251, 247, 255, 0.16) 100%),
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(248, 252, 255, 0.04) 58%, rgba(248, 252, 255, 0.72));
|
||||
--cyber-gridlines-bg:
|
||||
linear-gradient(rgba(8, 145, 178, 0.055) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(8, 145, 178, 0.045) 1px, transparent 1px);
|
||||
--cyber-gridlines-opacity: 0.2;
|
||||
--cyber-scanlines-bg: repeating-linear-gradient(
|
||||
to bottom,
|
||||
rgba(8, 35, 50, 0.035) 0,
|
||||
rgba(8, 35, 50, 0.035) 1px,
|
||||
transparent 1px,
|
||||
transparent 4px
|
||||
);
|
||||
--cyber-scanlines-opacity: 0.08;
|
||||
--cyber-copy-aura: radial-gradient(circle at 28% 38%, rgba(255, 255, 255, 0.92), rgba(255, 255, 255, 0.38) 58%, transparent 78%);
|
||||
--cyber-title-color: rgba(19, 34, 56, 0.98);
|
||||
--cyber-description-color: rgba(50, 65, 88, 0.82);
|
||||
--cyber-action-primary-color: #061722;
|
||||
--cyber-action-secondary-bg: rgba(255, 255, 255, 0.62);
|
||||
--cyber-action-secondary-hover-bg: rgba(8, 145, 178, 0.08);
|
||||
--cyber-release-color: rgba(50, 65, 88, 0.68);
|
||||
--cyber-scene-floor-bg:
|
||||
radial-gradient(ellipse at 58% 84%, rgba(184, 50, 216, 0.16), transparent 18%),
|
||||
radial-gradient(ellipse at 56% 84%, rgba(8, 145, 178, 0.14), transparent 32%),
|
||||
repeating-radial-gradient(ellipse at 58% 84%, rgba(8, 145, 178, 0.08) 0 1px, transparent 1px 20px);
|
||||
--cyber-scene-floor-opacity: 0.38;
|
||||
--cyber-scene-foreground-bg:
|
||||
linear-gradient(90deg, transparent 0 4%, rgba(8, 145, 178, 0.07) 4.1%, transparent 4.4%),
|
||||
linear-gradient(180deg, transparent 0 88%, rgba(184, 50, 216, 0.06));
|
||||
--cyber-scene-foreground-opacity: 0.48;
|
||||
--cyber-video-frame-bg: rgba(255, 255, 255, 0.7);
|
||||
--cyber-video-content-bg: rgba(8, 20, 34, 0.9);
|
||||
--cyber-card-text: rgba(19, 34, 56, 0.88);
|
||||
--cyber-card-muted: rgba(67, 82, 105, 0.76);
|
||||
--cyber-card-subtle: rgba(67, 82, 105, 0.64);
|
||||
--cyber-card-inset: rgba(255, 255, 255, 0.62);
|
||||
--cyber-feature-shell-bg: transparent;
|
||||
--cyber-feature-rail-bg: transparent;
|
||||
--cyber-feature-rail-shadow:
|
||||
0 0 0 1px rgba(8, 145, 178, 0.12) inset,
|
||||
0 -1px 0 rgba(8, 145, 178, 0.18),
|
||||
0 1px 0 rgba(8, 145, 178, 0.16);
|
||||
--cyber-feature-divider: rgba(8, 145, 178, 0.18);
|
||||
--cyber-feature-title: rgba(19, 34, 56, 0.94);
|
||||
--cyber-feature-text: rgba(67, 82, 105, 0.72);
|
||||
}
|
||||
|
||||
.cyber-panel {
|
||||
position: relative;
|
||||
border: 1px solid var(--cyber-border-cyan);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(5, 14, 31, 0.9), rgba(3, 10, 22, 0.64));
|
||||
background: var(--cyber-panel-bg);
|
||||
clip-path: polygon(
|
||||
var(--cyber-frame-cut) 0,
|
||||
100% 0,
|
||||
|
|
@ -33,9 +234,7 @@
|
|||
0 100%,
|
||||
0 var(--cyber-frame-cut)
|
||||
);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(47, 125, 255, 0.12) inset,
|
||||
0 0 24px rgba(0, 234, 255, 0.12);
|
||||
box-shadow: var(--cyber-panel-shadow);
|
||||
}
|
||||
|
||||
.cyber-hero {
|
||||
|
|
@ -53,13 +252,11 @@
|
|||
isolation: isolate;
|
||||
overflow: clip;
|
||||
color: var(--cyber-text);
|
||||
background:
|
||||
radial-gradient(circle at 76% 30%, rgba(0, 234, 255, 0.14), transparent 30%),
|
||||
radial-gradient(circle at 86% 70%, rgba(255, 43, 255, 0.14), transparent 34%),
|
||||
linear-gradient(180deg, var(--cyber-bg-0), var(--cyber-bg-1) 58%, var(--cyber-bg-0));
|
||||
background: var(--cyber-hero-bg);
|
||||
}
|
||||
|
||||
.cyber-hero__background,
|
||||
.cyber-hero__monterey,
|
||||
.cyber-hero__wash,
|
||||
.cyber-hero__gridlines,
|
||||
.cyber-hero__scanlines {
|
||||
|
|
@ -68,32 +265,75 @@
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cyber-hero__monterey {
|
||||
z-index: -5;
|
||||
overflow: hidden;
|
||||
background: var(--cyber-monterey-bg);
|
||||
}
|
||||
|
||||
.cyber-hero__monterey::before,
|
||||
.cyber-hero__monterey::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cyber-hero__monterey::before {
|
||||
z-index: 2;
|
||||
background: var(--cyber-monterey-before-bg);
|
||||
}
|
||||
|
||||
.cyber-hero__monterey::after {
|
||||
z-index: 3;
|
||||
background: var(--cyber-monterey-after-bg);
|
||||
}
|
||||
|
||||
.cyber-hero__monterey-canvas {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
filter: var(--cyber-monterey-canvas-filter);
|
||||
mix-blend-mode: var(--cyber-monterey-canvas-blend);
|
||||
transform: scale(1.035);
|
||||
transition: opacity 1.65s cubic-bezier(0.22, 0.72, 0.2, 1);
|
||||
}
|
||||
|
||||
.cyber-hero__monterey--live .cyber-hero__monterey-canvas {
|
||||
opacity: var(--cyber-monterey-canvas-opacity);
|
||||
}
|
||||
|
||||
.cyber-hero__monterey a[data-n] {
|
||||
left: 14px !important;
|
||||
right: auto !important;
|
||||
bottom: 12px !important;
|
||||
z-index: 5 !important;
|
||||
padding: 0 !important;
|
||||
color: rgba(244, 247, 255, 0.46) !important;
|
||||
font: 700 10px/1 var(--font-family-mono, monospace) !important;
|
||||
letter-spacing: 0.14em !important;
|
||||
opacity: 0.32 !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
.cyber-hero__background {
|
||||
z-index: -4;
|
||||
inset: -40px -40px -80px;
|
||||
background:
|
||||
radial-gradient(circle at 70% 26%, rgba(0, 234, 255, 0.16), transparent 30%),
|
||||
radial-gradient(circle at 86% 62%, rgba(255, 43, 255, 0.16), transparent 32%),
|
||||
linear-gradient(90deg, transparent 0 64px, rgba(0, 234, 255, 0.055) 65px 66px, transparent 67px 160px),
|
||||
linear-gradient(180deg, transparent 0 55%, rgba(255, 43, 255, 0.05) 55.2% 55.4%, transparent 55.6%),
|
||||
linear-gradient(to top, rgba(3, 10, 24, 0.88) 0%, rgba(3, 10, 24, 0.7) 12%, rgba(3, 10, 24, 0.34) 25%, transparent 38%),
|
||||
linear-gradient(to top, rgba(5, 17, 40, 0.42) 0%, rgba(5, 17, 40, 0.28) 24%, rgba(5, 17, 40, 0.12) 43%, transparent 62%),
|
||||
linear-gradient(180deg, #02050d 0%, #061124 54%, #02050d 100%);
|
||||
background: var(--cyber-background-bg);
|
||||
background-size:
|
||||
auto,
|
||||
auto,
|
||||
220px 100%,
|
||||
auto,
|
||||
260px 100%,
|
||||
180px 100%,
|
||||
auto;
|
||||
background-position:
|
||||
0 0,
|
||||
center,
|
||||
24px 0,
|
||||
0 0,
|
||||
center;
|
||||
opacity: 0.92;
|
||||
opacity: var(--cyber-background-opacity);
|
||||
transform: translate3d(
|
||||
0,
|
||||
calc(var(--hero-scroll) * 0.035px),
|
||||
|
|
@ -106,12 +346,7 @@
|
|||
content: "";
|
||||
position: absolute;
|
||||
inset: 12% 0 0;
|
||||
background:
|
||||
linear-gradient(90deg, transparent 0 8%, rgba(0, 234, 255, 0.14) 8.1% 8.22%, transparent 8.34% 18%, rgba(255, 43, 255, 0.12) 18.1% 18.22%, transparent 18.34% 31%, rgba(0, 234, 255, 0.11) 31.1% 31.24%, transparent 31.36% 44%, rgba(47, 125, 255, 0.12) 44.1% 44.2%, transparent 44.34% 62%, rgba(255, 43, 255, 0.1) 62.1% 62.22%, transparent 62.34% 78%, rgba(0, 234, 255, 0.12) 78.1% 78.22%, transparent 78.34%),
|
||||
repeating-linear-gradient(90deg, transparent 0 78px, rgba(0, 234, 255, 0.05) 80px 82px, transparent 84px 116px),
|
||||
linear-gradient(to top, rgba(3, 12, 27, 0.66) 0%, rgba(3, 12, 27, 0.42) 8%, rgba(3, 12, 27, 0.16) 17%, transparent 28%),
|
||||
linear-gradient(to top, rgba(5, 20, 44, 0.5) 0%, rgba(5, 20, 44, 0.28) 12%, rgba(5, 20, 44, 0.12) 24%, transparent 36%),
|
||||
linear-gradient(to top, rgba(4, 17, 38, 0.38) 0%, rgba(4, 17, 38, 0.22) 18%, rgba(4, 17, 38, 0.08) 34%, transparent 48%);
|
||||
background: var(--cyber-background-before-bg);
|
||||
background-size:
|
||||
auto,
|
||||
auto,
|
||||
|
|
@ -124,8 +359,8 @@
|
|||
16px bottom,
|
||||
72px bottom,
|
||||
120px bottom;
|
||||
opacity: 0.62;
|
||||
mix-blend-mode: screen;
|
||||
opacity: var(--cyber-background-before-opacity);
|
||||
mix-blend-mode: var(--cyber-background-before-blend);
|
||||
mask-image: linear-gradient(180deg, transparent 0, black 22%, black 78%, transparent 100%);
|
||||
}
|
||||
|
||||
|
|
@ -133,12 +368,9 @@
|
|||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
repeating-linear-gradient(90deg, transparent 0 34px, rgba(0, 234, 255, 0.12) 35px 36px, transparent 37px 110px),
|
||||
repeating-linear-gradient(180deg, transparent 0 28px, rgba(255, 43, 255, 0.1) 29px 30px, transparent 31px 78px),
|
||||
linear-gradient(90deg, transparent, rgba(0, 234, 255, 0.08), transparent);
|
||||
opacity: 0.26;
|
||||
mix-blend-mode: screen;
|
||||
background: var(--cyber-background-after-bg);
|
||||
opacity: var(--cyber-background-after-opacity);
|
||||
mix-blend-mode: var(--cyber-background-after-blend);
|
||||
mask-image:
|
||||
linear-gradient(180deg, transparent 0 10%, black 22%, black 82%, transparent 100%),
|
||||
linear-gradient(90deg, transparent 0 8%, black 24%, black 94%, transparent 100%);
|
||||
|
|
@ -146,32 +378,21 @@
|
|||
|
||||
.cyber-hero__wash {
|
||||
z-index: -3;
|
||||
background:
|
||||
radial-gradient(circle at 18% 44%, rgba(2, 5, 13, 0.48), rgba(2, 5, 13, 0.22) 34%, transparent 58%),
|
||||
linear-gradient(90deg, rgba(2, 5, 13, 0.42) 0%, rgba(2, 5, 13, 0.28) 34%, rgba(2, 5, 13, 0.08) 66%, rgba(2, 5, 13, 0.3) 100%),
|
||||
linear-gradient(180deg, rgba(2, 5, 13, 0.18), rgba(2, 5, 13, 0.08) 58%, rgba(2, 5, 13, 0.92));
|
||||
background: var(--cyber-wash-bg);
|
||||
}
|
||||
|
||||
.cyber-hero__gridlines {
|
||||
z-index: -2;
|
||||
opacity: 0.16;
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 234, 255, 0.055) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(0, 234, 255, 0.045) 1px, transparent 1px);
|
||||
opacity: var(--cyber-gridlines-opacity);
|
||||
background-image: var(--cyber-gridlines-bg);
|
||||
background-size: 72px 72px;
|
||||
mask-image: linear-gradient(180deg, transparent, black 12%, black 72%, transparent);
|
||||
}
|
||||
|
||||
.cyber-hero__scanlines {
|
||||
z-index: 8;
|
||||
opacity: 0.11;
|
||||
background-image: repeating-linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.08) 0,
|
||||
rgba(255, 255, 255, 0.08) 1px,
|
||||
transparent 1px,
|
||||
transparent 4px
|
||||
);
|
||||
opacity: var(--cyber-scanlines-opacity);
|
||||
background-image: var(--cyber-scanlines-bg);
|
||||
mix-blend-mode: overlay;
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +400,8 @@
|
|||
position: relative;
|
||||
z-index: 2;
|
||||
width: min(1580px, calc(100vw - 56px));
|
||||
max-width: none !important;
|
||||
max-width: min(1580px, calc(100vw - 56px)) !important;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.cyber-hero__layout {
|
||||
|
|
@ -198,12 +420,7 @@
|
|||
}
|
||||
|
||||
.cyber-hero__copy::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -64px -64px -42px -36px;
|
||||
z-index: -1;
|
||||
background: radial-gradient(circle at 28% 38%, rgba(2, 5, 13, 0.82), rgba(2, 5, 13, 0.36) 62%, transparent 78%);
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cyber-hero__brand-lockup {
|
||||
|
|
@ -241,7 +458,7 @@
|
|||
line-height: 1;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0;
|
||||
color: rgba(244, 247, 255, 0.96);
|
||||
color: var(--cyber-title-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
@ -269,7 +486,7 @@
|
|||
.cyber-hero__description {
|
||||
max-width: 560px;
|
||||
margin: 0 0 30px;
|
||||
color: rgba(222, 229, 255, 0.84);
|
||||
color: var(--cyber-description-color);
|
||||
font-size: clamp(1rem, 1.08vw, 1.22rem);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
|
@ -302,7 +519,7 @@
|
|||
}
|
||||
|
||||
.cyber-hero__action--primary.v-btn {
|
||||
color: var(--cyber-bg-0) !important;
|
||||
color: var(--cyber-action-primary-color) !important;
|
||||
background: linear-gradient(135deg, var(--cyber-cyan), var(--cyber-magenta)) !important;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.16) inset,
|
||||
|
|
@ -314,14 +531,14 @@
|
|||
.cyber-hero__action--docs.v-btn {
|
||||
color: var(--cyber-text) !important;
|
||||
border-color: rgba(0, 234, 255, 0.46) !important;
|
||||
background: rgba(3, 10, 22, 0.56) !important;
|
||||
background: var(--cyber-action-secondary-bg) !important;
|
||||
}
|
||||
|
||||
.cyber-hero__action--watch.v-btn:hover,
|
||||
.cyber-hero__action--docs.v-btn:hover {
|
||||
color: var(--cyber-cyan) !important;
|
||||
border-color: rgba(0, 234, 255, 0.74) !important;
|
||||
background: rgba(0, 234, 255, 0.08) !important;
|
||||
background: var(--cyber-action-secondary-hover-bg) !important;
|
||||
}
|
||||
|
||||
.cyber-hero__terminal-note {
|
||||
|
|
@ -345,10 +562,14 @@
|
|||
|
||||
.cyber-hero__release {
|
||||
flex: 0 0 auto;
|
||||
color: rgba(244, 247, 255, 0.62);
|
||||
color: var(--cyber-release-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cyber-hero__release-date {
|
||||
color: var(--cyber-card-subtle);
|
||||
}
|
||||
|
||||
.cyber-hero__scene {
|
||||
min-width: 0;
|
||||
width: clamp(940px, 60vw, 1220px);
|
||||
|
|
@ -384,12 +605,9 @@
|
|||
|
||||
.cyber-scene__floor {
|
||||
z-index: 0;
|
||||
background:
|
||||
radial-gradient(ellipse at 58% 84%, rgba(255, 43, 255, 0.24), transparent 18%),
|
||||
radial-gradient(ellipse at 56% 84%, rgba(0, 234, 255, 0.18), transparent 32%),
|
||||
repeating-radial-gradient(ellipse at 58% 84%, rgba(0, 234, 255, 0.1) 0 1px, transparent 1px 20px);
|
||||
background: var(--cyber-scene-floor-bg);
|
||||
filter: blur(8px);
|
||||
opacity: 0.48;
|
||||
opacity: var(--cyber-scene-floor-opacity);
|
||||
mask-image: radial-gradient(ellipse at 58% 84%, black 0 26%, rgba(0, 0, 0, 0.52) 40%, transparent 62%);
|
||||
}
|
||||
|
||||
|
|
@ -427,11 +645,9 @@
|
|||
|
||||
.cyber-scene__foreground {
|
||||
z-index: 6;
|
||||
background:
|
||||
linear-gradient(90deg, transparent 0 4%, rgba(0, 234, 255, 0.08) 4.1%, transparent 4.4%),
|
||||
linear-gradient(180deg, transparent 0 88%, rgba(255, 43, 255, 0.08));
|
||||
background: var(--cyber-scene-foreground-bg);
|
||||
mix-blend-mode: screen;
|
||||
opacity: 0.72;
|
||||
opacity: var(--cyber-scene-foreground-opacity);
|
||||
mask-image: linear-gradient(180deg, transparent 0 18%, black 28%, black 78%, transparent 100%);
|
||||
}
|
||||
|
||||
|
|
@ -441,7 +657,7 @@
|
|||
position: relative;
|
||||
aspect-ratio: 16 / 9;
|
||||
border: 1px solid rgba(0, 234, 255, 0.66);
|
||||
background: rgba(2, 6, 16, 0.82);
|
||||
background: var(--cyber-video-frame-bg);
|
||||
clip-path: polygon(20px 0, 100% 0, 100% calc(100% - 20px), calc(100% - 20px) 100%, 0 100%, 0 20px);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(47, 125, 255, 0.2) inset,
|
||||
|
|
@ -484,7 +700,7 @@
|
|||
z-index: 2;
|
||||
overflow: hidden;
|
||||
border-radius: var(--cyber-radius-sm);
|
||||
background: rgba(2, 6, 16, 0.94);
|
||||
background: var(--cyber-video-content-bg);
|
||||
clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
|
||||
}
|
||||
|
||||
|
|
@ -494,7 +710,7 @@
|
|||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: rgba(2, 6, 16, 0.95);
|
||||
background: var(--cyber-video-content-bg);
|
||||
}
|
||||
|
||||
.cyber-video-frame__content .hero-video__player {
|
||||
|
|
@ -635,13 +851,13 @@
|
|||
left: 66%;
|
||||
width: 274px;
|
||||
padding: 12px 13px;
|
||||
color: rgba(244, 247, 255, 0.84);
|
||||
color: var(--cyber-card-text);
|
||||
font-family: var(--at-font-mono);
|
||||
font-size: 1.08rem;
|
||||
line-height: 1.28;
|
||||
border-color: color-mix(in srgb, var(--agent-accent), transparent 44%);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.06) inset,
|
||||
0 0 0 1px var(--cyber-card-inset) inset,
|
||||
0 0 24px var(--agent-accent-soft);
|
||||
}
|
||||
|
||||
|
|
@ -691,7 +907,7 @@
|
|||
.cyber-agent__tasks li {
|
||||
position: relative;
|
||||
padding-left: 15px;
|
||||
color: rgba(222, 229, 255, 0.72);
|
||||
color: var(--cyber-card-muted);
|
||||
}
|
||||
|
||||
.cyber-agent__tasks li::before {
|
||||
|
|
@ -709,7 +925,7 @@
|
|||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 7px;
|
||||
color: rgba(222, 229, 255, 0.62);
|
||||
color: var(--cyber-card-subtle);
|
||||
}
|
||||
|
||||
.cyber-agent__status strong {
|
||||
|
|
@ -744,13 +960,13 @@
|
|||
|
||||
.cyber-agent[data-agent="planner"] .cyber-agent__card {
|
||||
display: block;
|
||||
top: 12%;
|
||||
right: 108%;
|
||||
top: 10%;
|
||||
right: 106%;
|
||||
left: auto;
|
||||
width: 280px;
|
||||
padding: 12px 13px;
|
||||
font-size: 1.14rem;
|
||||
transform: translate(110px, -10%);
|
||||
width: 258px;
|
||||
padding: 11px 12px;
|
||||
font-size: 1.04rem;
|
||||
transform: translate(-28px, -8%);
|
||||
}
|
||||
|
||||
.cyber-agent[data-agent="lead"] .cyber-agent__card {
|
||||
|
|
@ -763,15 +979,29 @@
|
|||
|
||||
.cyber-agent[data-agent="developer"] .cyber-agent__card {
|
||||
top: -14%;
|
||||
left: 102%;
|
||||
left: 98%;
|
||||
width: 274px;
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
|
||||
@media (min-width: 1101px) {
|
||||
.cyber-agent[data-agent="planner"] {
|
||||
left: calc(var(--agent-x) * 1% + 20px);
|
||||
top: calc(var(--agent-y) * 1% - 26px);
|
||||
}
|
||||
|
||||
.cyber-agent[data-agent="lead"],
|
||||
.cyber-agent[data-agent="developer"] {
|
||||
top: calc(var(--agent-y) * 1% + 12px);
|
||||
top: calc(var(--agent-y) * 1% - 4px);
|
||||
}
|
||||
|
||||
.cyber-agent[data-agent="lead"] .cyber-agent__float,
|
||||
.cyber-agent[data-agent="developer"] .cyber-agent__float {
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.cyber-agent[data-agent="planner"] .cyber-agent__float {
|
||||
top: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -797,6 +1027,10 @@
|
|||
position: absolute;
|
||||
left: calc(var(--bubble-x) * 1%);
|
||||
top: calc(var(--bubble-y) * 1%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
max-width: 168px;
|
||||
min-height: 30px;
|
||||
padding: 7px 11px;
|
||||
|
|
@ -919,35 +1153,35 @@
|
|||
@keyframes cyberFeatureReviewerSpeechFloat {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0) rotate(-4deg);
|
||||
transform: translateX(calc(50% + var(--reviewer-bubble-center-shift, 0px))) translate3d(0, 0, 0) rotate(-4deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d(0, -3px, 0) rotate(-3deg);
|
||||
transform: translateX(calc(50% + var(--reviewer-bubble-center-shift, 0px))) translate3d(0, -3px, 0) rotate(-3deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cyberFeatureReviewerSpeechPop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(12px, 12px, 0) scale(0.54) rotate(-12deg);
|
||||
transform: translateX(calc(50% + var(--reviewer-bubble-center-shift, 0px))) translate3d(12px, 12px, 0) scale(0.54) rotate(-12deg);
|
||||
}
|
||||
|
||||
62% {
|
||||
opacity: 1;
|
||||
transform: translate3d(-2px, -3px, 0) scale(1.08) rotate(-3deg);
|
||||
transform: translateX(calc(50% + var(--reviewer-bubble-center-shift, 0px))) translate3d(-2px, -3px, 0) scale(1.08) rotate(-3deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale(1) rotate(-4deg);
|
||||
transform: translateX(calc(50% + var(--reviewer-bubble-center-shift, 0px))) translate3d(0, 0, 0) scale(1) rotate(-4deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes cyberFeatureReviewerSpeechExit {
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate3d(8px, 8px, 0) scale(0.86) rotate(-8deg);
|
||||
transform: translateX(calc(50% + var(--reviewer-bubble-center-shift, 0px))) translate3d(8px, 8px, 0) scale(0.86) rotate(-8deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -964,10 +1198,7 @@
|
|||
z-index: -1;
|
||||
inset: -118px -3vw -96px;
|
||||
pointer-events: none;
|
||||
background:
|
||||
radial-gradient(ellipse at 50% 62%, rgba(0, 234, 255, 0.08), transparent 56%),
|
||||
radial-gradient(ellipse at 78% 52%, rgba(255, 43, 255, 0.08), transparent 48%),
|
||||
linear-gradient(180deg, transparent 0%, rgba(5, 17, 40, 0.08) 30%, rgba(5, 14, 31, 0.18) 52%, rgba(5, 17, 40, 0.08) 72%, transparent 100%);
|
||||
background: var(--cyber-feature-shell-bg);
|
||||
opacity: 0.86;
|
||||
mask-image: radial-gradient(ellipse at 50% 54%, black 0 48%, rgba(0, 0, 0, 0.5) 62%, transparent 78%);
|
||||
}
|
||||
|
|
@ -980,97 +1211,70 @@
|
|||
gap: 0;
|
||||
width: 100%;
|
||||
padding: 17px 18px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(3, 10, 22, 0.34) 0%, rgba(5, 14, 31, 0.74) 30%, rgba(5, 14, 31, 0.72) 70%, rgba(3, 10, 22, 0.34) 100%),
|
||||
linear-gradient(135deg, rgba(5, 14, 31, 0.58), rgba(3, 10, 22, 0.42));
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(47, 125, 255, 0.1) inset,
|
||||
0 -28px 46px rgba(2, 5, 13, 0.14),
|
||||
0 30px 58px rgba(2, 5, 13, 0.18),
|
||||
0 0 24px rgba(0, 234, 255, 0.1);
|
||||
background: var(--cyber-feature-rail-bg);
|
||||
box-shadow: var(--cyber-feature-rail-shadow);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__collaboration {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: calc(100% - 16px);
|
||||
z-index: 2;
|
||||
width: clamp(132px, 11vw, 168px);
|
||||
height: auto;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
filter:
|
||||
drop-shadow(0 18px 24px rgba(0, 0, 0, 0.5))
|
||||
drop-shadow(0 0 20px rgba(120, 58, 255, 0.18))
|
||||
drop-shadow(0 0 18px rgba(255, 45, 64, 0.14));
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer {
|
||||
--reviewer-robot-width: clamp(76px, 5.4vw, 96px);
|
||||
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
right: clamp(34px, 5vw, 86px);
|
||||
bottom: calc(100% - 8px);
|
||||
right: clamp(52px, 5vw, 82px);
|
||||
bottom: calc(100% - 6px);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer--active .cyber-feature-rail__reviewer-card {
|
||||
border-color: rgba(255, 43, 255, 0.86);
|
||||
border-color: rgba(0, 234, 255, 0.86);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.08) inset,
|
||||
0 0 28px rgba(255, 43, 255, 0.28);
|
||||
0 0 28px rgba(0, 234, 255, 0.26);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer--active .cyber-feature-rail__robot {
|
||||
filter:
|
||||
drop-shadow(0 16px 24px rgba(0, 0, 0, 0.5))
|
||||
drop-shadow(0 0 22px rgba(255, 43, 255, 0.42));
|
||||
drop-shadow(0 0 22px rgba(0, 234, 255, 0.36));
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-bubble {
|
||||
--speech-fill: rgba(255, 246, 159, 0.9);
|
||||
--reviewer-bubble-center-shift: 3px;
|
||||
|
||||
position: absolute;
|
||||
right: clamp(98px, 6.5vw, 116px);
|
||||
bottom: calc(100% - 44px);
|
||||
left: auto;
|
||||
top: auto;
|
||||
right: calc(var(--reviewer-robot-width) / 2);
|
||||
bottom: calc(100% + 10px);
|
||||
z-index: 6;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
width: max-content;
|
||||
max-width: 158px;
|
||||
padding: 7px 11px;
|
||||
color: #0b1020;
|
||||
font-family: var(--at-font-mono);
|
||||
font-size: 0.64rem;
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
letter-spacing: 0;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
background:
|
||||
radial-gradient(circle at 28% 24%, rgba(255, 255, 255, 0.9), var(--speech-fill) 64%, rgba(255, 224, 88, 0.92) 100%);
|
||||
border: 2px solid #050816;
|
||||
border-radius: 999px;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 215, 0, 0.34),
|
||||
0 5px 0 rgba(0, 0, 0, 0.2),
|
||||
0 0 14px rgba(255, 215, 0, 0.18);
|
||||
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.62);
|
||||
transform: rotate(-4deg);
|
||||
transform-origin: right bottom;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
text-wrap: balance;
|
||||
transform: translateX(calc(50% + var(--reviewer-bubble-center-shift))) translate3d(0, 0, 0) rotate(-4deg);
|
||||
transform-origin: center bottom;
|
||||
animation: cyberFeatureReviewerSpeechFloat 2.8s ease-in-out 0.45s infinite;
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-bubble::before,
|
||||
.cyber-feature-rail__reviewer-bubble::after {
|
||||
position: absolute;
|
||||
top: 52%;
|
||||
content: "";
|
||||
clip-path: polygon(0 0, 100% 50%, 0 100%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-bubble::before {
|
||||
right: -21px;
|
||||
width: 23px;
|
||||
height: 17px;
|
||||
background: #050816;
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-bubble::after {
|
||||
right: -16px;
|
||||
width: 18px;
|
||||
height: 11px;
|
||||
background: rgba(255, 226, 78, 0.96);
|
||||
}
|
||||
|
||||
.cyber-feature-bubble-enter-active,
|
||||
.cyber-feature-bubble-leave-active {
|
||||
transition:
|
||||
|
|
@ -1095,13 +1299,13 @@
|
|||
.cyber-feature-rail__robot {
|
||||
position: relative;
|
||||
top: 9px;
|
||||
width: clamp(74px, 5.6vw, 96px);
|
||||
width: var(--reviewer-robot-width);
|
||||
height: auto;
|
||||
transform: rotate(3deg);
|
||||
transform-origin: center bottom;
|
||||
filter:
|
||||
drop-shadow(0 14px 22px rgba(0, 0, 0, 0.48))
|
||||
drop-shadow(0 0 16px rgba(255, 43, 255, 0.26));
|
||||
drop-shadow(0 0 16px rgba(0, 234, 255, 0.22));
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
animation: cyberRobotBob 6.4s ease-in-out infinite;
|
||||
|
|
@ -1109,16 +1313,17 @@
|
|||
|
||||
.cyber-feature-rail__reviewer-card {
|
||||
position: relative;
|
||||
width: clamp(190px, 15.5vw, 248px);
|
||||
padding: 10px 12px;
|
||||
color: rgba(244, 247, 255, 0.84);
|
||||
width: clamp(146px, 10.2vw, 160px);
|
||||
padding: 7px 8px;
|
||||
color: var(--cyber-card-text);
|
||||
font-family: var(--at-font-mono);
|
||||
font-size: clamp(0.56rem, 0.65vw, 0.72rem);
|
||||
line-height: 1.32;
|
||||
border-color: rgba(255, 43, 255, 0.5);
|
||||
font-size: clamp(0.48rem, 0.46vw, 0.54rem);
|
||||
line-height: 1.28;
|
||||
border-color: rgba(0, 234, 255, 0.52);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.06) inset,
|
||||
0 0 24px rgba(255, 43, 255, 0.2);
|
||||
0 0 0 1px var(--cyber-card-inset) inset,
|
||||
0 0 24px rgba(0, 234, 255, 0.18);
|
||||
transform: translateY(-34px);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-card::after {
|
||||
|
|
@ -1126,15 +1331,15 @@
|
|||
position: absolute;
|
||||
left: calc(100% - 1px);
|
||||
top: 56%;
|
||||
width: 18px;
|
||||
width: 13px;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, rgba(255, 43, 255, 0.72), transparent);
|
||||
box-shadow: 0 0 12px rgba(255, 43, 255, 0.34);
|
||||
background: linear-gradient(90deg, rgba(0, 234, 255, 0.72), transparent);
|
||||
box-shadow: 0 0 12px rgba(0, 234, 255, 0.3);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-label {
|
||||
margin-bottom: 7px;
|
||||
color: var(--cyber-magenta);
|
||||
margin-bottom: 5px;
|
||||
color: var(--cyber-cyan);
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.07em;
|
||||
|
|
@ -1142,7 +1347,7 @@
|
|||
|
||||
.cyber-feature-rail__reviewer-tasks {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
gap: 3px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
|
@ -1150,8 +1355,8 @@
|
|||
|
||||
.cyber-feature-rail__reviewer-tasks li {
|
||||
position: relative;
|
||||
padding-left: 15px;
|
||||
color: rgba(222, 229, 255, 0.72);
|
||||
padding-left: 12px;
|
||||
color: var(--cyber-card-muted);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-tasks li::before {
|
||||
|
|
@ -1159,21 +1364,21 @@
|
|||
position: absolute;
|
||||
left: 0;
|
||||
top: 0.52em;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border: 1px solid var(--cyber-magenta);
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border: 1px solid var(--cyber-cyan);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-status {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 7px;
|
||||
color: rgba(222, 229, 255, 0.62);
|
||||
gap: 5px;
|
||||
margin-top: 5px;
|
||||
color: var(--cyber-card-subtle);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer-status strong {
|
||||
color: var(--cyber-magenta);
|
||||
color: var(--cyber-cyan);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
|
|
@ -1184,7 +1389,7 @@
|
|||
gap: 12px;
|
||||
min-width: 0;
|
||||
padding: 0 18px;
|
||||
border-right: 1px solid rgba(0, 234, 255, 0.16);
|
||||
border-right: 1px solid var(--cyber-feature-divider);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__item:last-child {
|
||||
|
|
@ -1204,13 +1409,13 @@
|
|||
|
||||
.cyber-feature-rail__title {
|
||||
margin-bottom: 3px;
|
||||
color: rgba(244, 247, 255, 0.94);
|
||||
color: var(--cyber-feature-title);
|
||||
font-weight: 800;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.cyber-feature-rail__text {
|
||||
color: rgba(222, 229, 255, 0.62);
|
||||
color: var(--cyber-feature-text);
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
|
@ -1329,7 +1534,15 @@
|
|||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.cyber-feature-rail__collaboration {
|
||||
left: 50%;
|
||||
bottom: calc(100% - 14px);
|
||||
width: 132px;
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer {
|
||||
--reviewer-robot-width: 78px;
|
||||
|
||||
right: 56px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
|
@ -1340,7 +1553,7 @@
|
|||
}
|
||||
|
||||
.cyber-feature-rail__robot {
|
||||
width: 78px;
|
||||
width: var(--reviewer-robot-width);
|
||||
}
|
||||
|
||||
.cyber-feature-rail__item:nth-child(3) {
|
||||
|
|
@ -1363,6 +1576,14 @@
|
|||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.cyber-hero__monterey {
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
.cyber-hero__monterey-canvas {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cyber-hero__container {
|
||||
width: min(100% - 32px, 680px);
|
||||
}
|
||||
|
|
@ -1484,6 +1705,10 @@
|
|||
padding: 16px;
|
||||
}
|
||||
|
||||
.cyber-feature-rail__collaboration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cyber-feature-rail__reviewer {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -1510,6 +1735,7 @@
|
|||
}
|
||||
|
||||
.cyber-hero__background,
|
||||
.cyber-hero__monterey,
|
||||
.cyber-scene,
|
||||
.cyber-scene__video,
|
||||
.cyber-agent {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ const { isDark, toggleTheme } = useBrowserTheme();
|
|||
const { trackThemeToggle } = useAnalytics();
|
||||
|
||||
const tooltip = computed(() => isDark.value ? t('theme.light') : t('theme.dark'));
|
||||
const icon = computed(() => isDark.value ? mdiWeatherNight : mdiWeatherSunny);
|
||||
|
||||
const onToggle = () => {
|
||||
toggleTheme();
|
||||
trackThemeToggle(isDark.value ? 'dark' : 'light');
|
||||
const theme = toggleTheme();
|
||||
trackThemeToggle(theme);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ const onToggle = () => {
|
|||
<template #activator="{ props }">
|
||||
<v-btn
|
||||
v-bind="props"
|
||||
:icon="isDark ? mdiWeatherSunny : mdiWeatherNight"
|
||||
:icon="icon"
|
||||
variant="text"
|
||||
size="small"
|
||||
:aria-label="tooltip"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
mdiMonitorDashboard,
|
||||
} from "@mdi/js";
|
||||
import {
|
||||
heroCollaborationFeature,
|
||||
heroFeatureRail,
|
||||
heroReviewerFeatureCard,
|
||||
type HeroMessage,
|
||||
|
|
@ -48,6 +49,14 @@ const reviewerBubbleText = computed(() => {
|
|||
|
||||
<template>
|
||||
<div class="cyber-feature-rail-shell">
|
||||
<img
|
||||
class="cyber-feature-rail__collaboration"
|
||||
:src="heroCollaborationFeature.asset"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div
|
||||
class="cyber-feature-rail__reviewer"
|
||||
:class="{
|
||||
|
|
@ -58,12 +67,13 @@ const reviewerBubbleText = computed(() => {
|
|||
aria-hidden="true"
|
||||
>
|
||||
<Transition name="cyber-feature-bubble">
|
||||
<div
|
||||
<CyberHeroSpeechBubble
|
||||
v-if="reviewerBubbleText"
|
||||
class="cyber-feature-rail__reviewer-bubble"
|
||||
role="reviewer"
|
||||
>
|
||||
{{ reviewerBubbleText }}
|
||||
</div>
|
||||
</CyberHeroSpeechBubble>
|
||||
</Transition>
|
||||
<div class="cyber-feature-rail__reviewer-card cyber-panel">
|
||||
<div class="cyber-feature-rail__reviewer-label">{{ heroReviewerFeatureCard.label }}</div>
|
||||
|
|
|
|||
|
|
@ -28,29 +28,29 @@ const showReceiver = computed(() =>
|
|||
<template>
|
||||
<div class="cyber-messages" aria-hidden="true">
|
||||
<Transition name="cyber-bubble">
|
||||
<div
|
||||
<CyberHeroSpeechBubble
|
||||
v-if="showSender && message && !reducedMotion"
|
||||
class="cyber-message cyber-message--sender"
|
||||
:class="`cyber-message--role-${message.from}`"
|
||||
:style="senderStyle"
|
||||
variant="sender"
|
||||
:role="message.from"
|
||||
:bubble-style="senderStyle"
|
||||
>
|
||||
{{ message.text }}
|
||||
</div>
|
||||
</CyberHeroSpeechBubble>
|
||||
</Transition>
|
||||
|
||||
<Transition name="cyber-bubble">
|
||||
<div
|
||||
<CyberHeroSpeechBubble
|
||||
v-if="showReceiver && message && !reducedMotion"
|
||||
class="cyber-message cyber-message--receiver"
|
||||
:class="`cyber-message--role-${message.to}`"
|
||||
:style="receiverStyle"
|
||||
variant="receiver"
|
||||
:role="message.to"
|
||||
:bubble-style="receiverStyle"
|
||||
>
|
||||
{{ message.response }}
|
||||
</div>
|
||||
</CyberHeroSpeechBubble>
|
||||
</Transition>
|
||||
|
||||
<div v-if="reducedMotion" class="cyber-message cyber-message--static cyber-panel">
|
||||
<CyberHeroSpeechBubble v-if="reducedMotion" class="cyber-panel" variant="static">
|
||||
Agents coordinate work automatically.
|
||||
</div>
|
||||
</CyberHeroSpeechBubble>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
174
landing/components/hero/CyberHeroMontereyBackground.vue
Normal file
174
landing/components/hero/CyberHeroMontereyBackground.vue
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
<script setup lang="ts">
|
||||
import type { NeatConfig, NeatController } from "@firecms/neat";
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||
const isLive = ref(false);
|
||||
|
||||
let gradient: NeatController | null = null;
|
||||
let heroObserver: IntersectionObserver | null = null;
|
||||
let motionQuery: MediaQueryList | null = null;
|
||||
let mobileQuery: MediaQueryList | null = null;
|
||||
let isVisible = false;
|
||||
let isInitializing = false;
|
||||
let initToken = 0;
|
||||
let revealTimer: number | null = null;
|
||||
|
||||
const montereyConfig: NeatConfig = {
|
||||
colors: [
|
||||
{ color: "#130437", enabled: true },
|
||||
{ color: "#B34BD0", enabled: true },
|
||||
{ color: "#210751", enabled: true },
|
||||
{ color: "#3511A5", enabled: true },
|
||||
{ color: "#8F3E8D", enabled: false },
|
||||
{ color: "#FF9A9E", enabled: false },
|
||||
],
|
||||
speed: 4.8,
|
||||
horizontalPressure: 7,
|
||||
verticalPressure: 3,
|
||||
waveFrequencyX: 0,
|
||||
waveFrequencyY: 0,
|
||||
waveAmplitude: 0,
|
||||
shadows: 4,
|
||||
highlights: 0,
|
||||
colorBrightness: 1.92,
|
||||
colorSaturation: 2.18,
|
||||
wireframe: false,
|
||||
colorBlending: 9,
|
||||
backgroundColor: "#030012",
|
||||
backgroundAlpha: 1,
|
||||
grainScale: 6,
|
||||
grainSparsity: 0,
|
||||
grainIntensity: 0.1,
|
||||
grainSpeed: 0,
|
||||
resolution: 0.32,
|
||||
yOffset: 150,
|
||||
flowDistortionA: 0.4,
|
||||
flowDistortionB: 10,
|
||||
flowScale: 3.3,
|
||||
flowEase: 0.37,
|
||||
enableProceduralTexture: false,
|
||||
textureVoidLikelihood: 0.06,
|
||||
textureVoidWidthMin: 10,
|
||||
textureVoidWidthMax: 500,
|
||||
textureBandDensity: 0.8,
|
||||
textureColorBlending: 0.06,
|
||||
textureSeed: 333,
|
||||
textureEase: 0.38,
|
||||
proceduralBackgroundColor: "#003FFF",
|
||||
textureShapeTriangles: 20,
|
||||
textureShapeCircles: 15,
|
||||
textureShapeBars: 15,
|
||||
textureShapeSquiggles: 10,
|
||||
yOffsetWaveMultiplier: 4.5,
|
||||
yOffsetColorMultiplier: 4.8,
|
||||
yOffsetFlowMultiplier: 5.2,
|
||||
flowEnabled: true,
|
||||
};
|
||||
|
||||
function supportsWebGl() {
|
||||
try {
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("webgl2") || canvas.getContext("webgl");
|
||||
const isSupported = Boolean(context);
|
||||
context?.getExtension("WEBGL_lose_context")?.loseContext();
|
||||
return isSupported;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function shouldUseLiveGradient() {
|
||||
return Boolean(
|
||||
canvasRef.value &&
|
||||
isVisible &&
|
||||
!motionQuery?.matches &&
|
||||
!mobileQuery?.matches &&
|
||||
supportsWebGl(),
|
||||
);
|
||||
}
|
||||
|
||||
function destroyGradient() {
|
||||
initToken += 1;
|
||||
if (revealTimer !== null) {
|
||||
window.clearTimeout(revealTimer);
|
||||
revealTimer = null;
|
||||
}
|
||||
gradient?.destroy();
|
||||
gradient = null;
|
||||
isLive.value = false;
|
||||
}
|
||||
|
||||
async function initGradient() {
|
||||
if (gradient || isInitializing || !shouldUseLiveGradient()) return;
|
||||
|
||||
const token = initToken;
|
||||
isInitializing = true;
|
||||
|
||||
try {
|
||||
const { NeatGradient } = await import("@firecms/neat");
|
||||
|
||||
if (token !== initToken || !canvasRef.value || !shouldUseLiveGradient()) return;
|
||||
|
||||
gradient = new NeatGradient({
|
||||
ref: canvasRef.value,
|
||||
...montereyConfig,
|
||||
resolution: window.devicePixelRatio > 1 ? 0.24 : 0.34,
|
||||
});
|
||||
revealTimer = window.setTimeout(() => {
|
||||
revealTimer = null;
|
||||
if (token === initToken && gradient && shouldUseLiveGradient()) {
|
||||
isLive.value = true;
|
||||
}
|
||||
}, 180);
|
||||
} catch (error) {
|
||||
console.warn("Monterey hero background is unavailable", error);
|
||||
destroyGradient();
|
||||
} finally {
|
||||
isInitializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
function syncGradient() {
|
||||
if (shouldUseLiveGradient()) {
|
||||
void initGradient();
|
||||
return;
|
||||
}
|
||||
|
||||
destroyGradient();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||
mobileQuery = window.matchMedia("(max-width: 700px)");
|
||||
motionQuery.addEventListener("change", syncGradient);
|
||||
mobileQuery.addEventListener("change", syncGradient);
|
||||
|
||||
heroObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
isVisible = Boolean(entry?.isIntersecting);
|
||||
syncGradient();
|
||||
},
|
||||
{ rootMargin: "160px 0px", threshold: 0.01 },
|
||||
);
|
||||
|
||||
const target = canvasRef.value?.closest(".cyber-hero");
|
||||
if (target) heroObserver.observe(target);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
heroObserver?.disconnect();
|
||||
motionQuery?.removeEventListener("change", syncGradient);
|
||||
mobileQuery?.removeEventListener("change", syncGradient);
|
||||
destroyGradient();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="cyber-hero__monterey"
|
||||
:class="{ 'cyber-hero__monterey--live': isLive }"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<canvas ref="canvasRef" class="cyber-hero__monterey-canvas" />
|
||||
</div>
|
||||
</template>
|
||||
25
landing/components/hero/CyberHeroSpeechBubble.vue
Normal file
25
landing/components/hero/CyberHeroSpeechBubble.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import type { CSSProperties } from "vue";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
variant?: "sender" | "receiver" | "static";
|
||||
role?: string | null;
|
||||
bubbleStyle?: CSSProperties;
|
||||
}>(), {
|
||||
variant: "sender",
|
||||
role: null,
|
||||
bubbleStyle: undefined,
|
||||
});
|
||||
|
||||
const bubbleClasses = computed(() => [
|
||||
"cyber-message",
|
||||
`cyber-message--${props.variant}`,
|
||||
props.role ? `cyber-message--role-${props.role}` : null,
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="bubbleClasses" :style="bubbleStyle">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import robotAvatarCyan from "~/assets/images/hero/robots/robot-avatar-cyan-v1.webp";
|
||||
import robotLeadLounge from "~/assets/images/footer/robot-lead-lounge-v1.webp";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const { repoUrl } = useGithubRepo();
|
||||
|
|
@ -13,14 +13,30 @@ const docsHref = computed(() => {
|
|||
|
||||
<template>
|
||||
<footer class="app-footer">
|
||||
<img
|
||||
class="app-footer__robot"
|
||||
:src="robotAvatarCyan"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="app-footer__robot-stage">
|
||||
<span class="app-footer__robot-bubble">
|
||||
<svg
|
||||
class="app-footer__robot-bubble-shape"
|
||||
viewBox="0 0 92 62"
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
>
|
||||
<path
|
||||
class="app-footer__robot-bubble-fill"
|
||||
d="M18 5H58C73 5 84 14 84 27C84 40 73 47 59 47H52L61 58L39 47H18C9 47 4 38 4 26C4 14 9 5 18 5Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="app-footer__robot-bubble-text">{{ t('footer.robotBubble') }}</span>
|
||||
</span>
|
||||
<img
|
||||
class="app-footer__robot"
|
||||
:src="robotLeadLounge"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
draggable="false"
|
||||
>
|
||||
</div>
|
||||
<v-container class="app-footer__inner">
|
||||
<span class="app-footer__copy"
|
||||
>{{ t('footer.copyright', { year }) }} · {{ t('footer.tagline') }}</span
|
||||
|
|
@ -44,20 +60,73 @@ const docsHref = computed(() => {
|
|||
isolation: isolate;
|
||||
}
|
||||
|
||||
.app-footer__robot {
|
||||
.app-footer__robot-stage {
|
||||
position: absolute;
|
||||
right: clamp(22px, 9vw, 148px);
|
||||
bottom: calc(100% - 4px);
|
||||
right: clamp(24px, 7vw, 112px);
|
||||
bottom: calc(100% - 5px);
|
||||
z-index: 2;
|
||||
width: clamp(76px, 6.2vw, 112px);
|
||||
height: auto;
|
||||
width: clamp(178px, 16vw, 236px);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
transform: translateY(14px) rotate(-2deg);
|
||||
transform-origin: center bottom;
|
||||
transform: translateY(3px) rotate(-1deg);
|
||||
transform-origin: 54% bottom;
|
||||
filter:
|
||||
drop-shadow(0 16px 22px rgba(0, 0, 0, 0.54))
|
||||
drop-shadow(0 0 18px rgba(0, 234, 255, 0.26));
|
||||
drop-shadow(0 14px 18px rgba(0, 0, 0, 0.52))
|
||||
drop-shadow(0 0 14px rgba(130, 255, 0, 0.2));
|
||||
}
|
||||
|
||||
.app-footer__robot {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.app-footer__robot-bubble {
|
||||
position: absolute;
|
||||
top: -28px;
|
||||
left: -18px;
|
||||
z-index: 3;
|
||||
display: block;
|
||||
width: 72px;
|
||||
height: 49px;
|
||||
color: #07111d;
|
||||
font-family: var(--at-font-mono);
|
||||
font-size: 0.62rem;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.62);
|
||||
transform: rotate(-2deg);
|
||||
transform-origin: 72% 74%;
|
||||
filter:
|
||||
drop-shadow(0 3px 0 rgba(0, 0, 0, 0.18))
|
||||
drop-shadow(0 0 9px rgba(255, 215, 0, 0.14));
|
||||
}
|
||||
|
||||
.app-footer__robot-bubble-shape {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.app-footer__robot-bubble-fill {
|
||||
fill: #fff09a;
|
||||
stroke: #050816;
|
||||
stroke-width: 4.6;
|
||||
stroke-linejoin: round;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
|
||||
.app-footer__robot-bubble-text {
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
width: 54px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-footer__inner {
|
||||
|
|
@ -119,7 +188,7 @@ const docsHref = computed(() => {
|
|||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.app-footer__robot {
|
||||
.app-footer__robot-stage {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
import robotAvatarCyan from "~/assets/images/hero/robots/robot-avatar-cyan-v1.webp";
|
||||
|
||||
const { t } = useI18n()
|
||||
const comparisonRobotRef = ref<HTMLElement | null>(null)
|
||||
const showComparisonRobotBubble = ref(false)
|
||||
let comparisonRobotObserver: IntersectionObserver | null = null
|
||||
|
||||
interface CellValue {
|
||||
status: string
|
||||
|
|
@ -245,6 +248,30 @@ const sourceLinks = [
|
|||
{ label: 'Claude pricing', href: 'https://claude.com/pricing' },
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
if (!comparisonRobotRef.value) return
|
||||
|
||||
comparisonRobotObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (!entry?.isIntersecting) return
|
||||
showComparisonRobotBubble.value = true
|
||||
comparisonRobotObserver?.disconnect()
|
||||
comparisonRobotObserver = null
|
||||
},
|
||||
{
|
||||
rootMargin: '0px 0px -12% 0px',
|
||||
threshold: 0.35,
|
||||
},
|
||||
)
|
||||
|
||||
comparisonRobotObserver.observe(comparisonRobotRef.value)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
comparisonRobotObserver?.disconnect()
|
||||
comparisonRobotObserver = null
|
||||
})
|
||||
|
||||
function getCellClass(cell: CellValue): string {
|
||||
switch (cell.status) {
|
||||
case 'yes': return 'comparison-table__cell--yes'
|
||||
|
|
@ -284,14 +311,28 @@ function getStatusIcon(status: string): string {
|
|||
</div>
|
||||
|
||||
<div class="comparison-table__wrap">
|
||||
<img
|
||||
<span
|
||||
ref="comparisonRobotRef"
|
||||
class="comparison-table__robot"
|
||||
:src="robotAvatarCyan"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<Transition name="comparison-robot-bubble">
|
||||
<span
|
||||
v-if="showComparisonRobotBubble"
|
||||
class="comparison-table__robot-bubble"
|
||||
>
|
||||
{{ t("comparison.robotBubble") }}
|
||||
</span>
|
||||
</Transition>
|
||||
<img
|
||||
class="comparison-table__robot-image"
|
||||
:src="robotAvatarCyan"
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
draggable="false"
|
||||
>
|
||||
</span>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -422,17 +463,139 @@ function getStatusIcon(status: string): string {
|
|||
height: auto;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
transform: translateY(14px) scaleX(-1) rotate(2deg);
|
||||
transform: translateY(4px) rotate(-0.5deg);
|
||||
transform-origin: center bottom;
|
||||
animation: comparisonRobotIdle 5.2s ease-in-out infinite;
|
||||
filter:
|
||||
drop-shadow(0 18px 22px rgba(0, 0, 0, 0.5))
|
||||
drop-shadow(0 0 18px rgba(0, 234, 255, 0.26));
|
||||
}
|
||||
|
||||
.comparison-table__robot-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
transform:
|
||||
scaleX(-1)
|
||||
rotate(2deg);
|
||||
transform-origin: center bottom;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.comparison-table__robot::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.comparison-table__robot-bubble {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: calc(100% + 12px);
|
||||
z-index: 5;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
padding: 6px 10px;
|
||||
color: #07111d;
|
||||
font-family: var(--at-font-mono);
|
||||
font-size: 0.66rem;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
background:
|
||||
radial-gradient(circle at 26% 22%, rgba(255, 255, 255, 0.88), rgba(255, 244, 168, 0.86) 66%, rgba(255, 215, 0, 0.84) 100%);
|
||||
border: 2px solid #050816;
|
||||
border-radius: 999px;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 215, 0, 0.28),
|
||||
0 5px 0 rgba(0, 0, 0, 0.2),
|
||||
0 0 12px rgba(255, 215, 0, 0.14);
|
||||
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.62);
|
||||
transform: rotate(-5deg);
|
||||
transform-origin: right bottom;
|
||||
animation: comparisonRobotBubbleFloat 2.6s ease-in-out 0.42s infinite;
|
||||
}
|
||||
|
||||
.comparison-table__robot-bubble::before {
|
||||
position: absolute;
|
||||
top: 52%;
|
||||
right: -30px;
|
||||
width: 32px;
|
||||
height: 18px;
|
||||
content: "";
|
||||
background: #050816;
|
||||
clip-path: polygon(0 0, 100% 50%, 0 100%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.comparison-table__robot-bubble::after {
|
||||
position: absolute;
|
||||
top: 52%;
|
||||
right: -24px;
|
||||
width: 26px;
|
||||
height: 12px;
|
||||
content: "";
|
||||
background: rgba(255, 226, 78, 0.96);
|
||||
clip-path: polygon(0 0, 100% 50%, 0 100%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.comparison-robot-bubble-enter-active,
|
||||
.comparison-robot-bubble-leave-active {
|
||||
transition:
|
||||
opacity 0.26s ease,
|
||||
filter 0.26s ease;
|
||||
}
|
||||
|
||||
.comparison-robot-bubble-enter-active {
|
||||
animation: comparisonRobotBubblePop 0.52s cubic-bezier(0.18, 0.9, 0.2, 1.24);
|
||||
}
|
||||
|
||||
.comparison-robot-bubble-enter-from,
|
||||
.comparison-robot-bubble-leave-to {
|
||||
opacity: 0;
|
||||
filter: blur(2px);
|
||||
}
|
||||
|
||||
@keyframes comparisonRobotIdle {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate3d(0, 4px, 0) rotate(-0.55deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d(1px, 3px, 0) rotate(0.75deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes comparisonRobotBubblePop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(14px, 18px, 0) scale(0.48) rotate(-13deg);
|
||||
}
|
||||
|
||||
58% {
|
||||
opacity: 1;
|
||||
transform: translate3d(-3px, -4px, 0) scale(1.1) rotate(-4deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale(1) rotate(-5deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes comparisonRobotBubbleFloat {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0) rotate(-5deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d(0, -2px, 0) rotate(-4deg);
|
||||
}
|
||||
}
|
||||
|
||||
.comparison-section__sources {
|
||||
max-width: 1040px;
|
||||
margin: 18px auto 0;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const { t, locale } = useI18n();
|
|||
const downloadStore = useDownloadStore();
|
||||
const { data: releaseData, resolve } = useReleaseDownloads();
|
||||
const { trackDownloadClick } = useAnalytics();
|
||||
const { repoUrl, releaseDownloadUrl } = useGithubRepo();
|
||||
const { releaseDownloadUrl } = useGithubRepo();
|
||||
const isMounted = ref(false);
|
||||
const showLinuxRobotMessage = ref(false);
|
||||
const showFallingLinuxRobot = ref(false);
|
||||
|
|
@ -21,6 +21,7 @@ let linuxRobotObserver: IntersectionObserver | null = null;
|
|||
let linuxRobotFallRaf = 0;
|
||||
let linuxRobotFallTimer: number | null = null;
|
||||
let lastLinuxRobotScrollY = 0;
|
||||
let faqLandingResizeObserver: ResizeObserver | null = null;
|
||||
|
||||
function clamp(value: number, min: number, max: number) {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
|
|
@ -64,8 +65,8 @@ function getLinuxRobotFallMetrics() {
|
|||
const target = getPageRect(faqTarget);
|
||||
const robotWidth = clamp(sourceViewport.width, 92, 112);
|
||||
const robotHeight = sourceViewport.height * (robotWidth / sourceViewport.width);
|
||||
const landedPageX = target.left + target.width * 0.3;
|
||||
const landedPageY = target.top + target.height * 0.08 - robotHeight * 0.62;
|
||||
const landedPageX = target.left + target.width * 0.5 - robotWidth * 0.5;
|
||||
const landedPageY = target.top - robotHeight * 0.58;
|
||||
|
||||
return {
|
||||
sourceViewport,
|
||||
|
|
@ -151,6 +152,11 @@ function updateLinuxRobotFall() {
|
|||
const viewportHeight = window.innerHeight;
|
||||
const startScroll = metrics.download.bottom - viewportHeight * 0.72;
|
||||
|
||||
if (linuxRobotFlightState.value === 'landed') {
|
||||
fallingLinuxRobotStyle.value = getLinuxRobotLandedStyle(metrics);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentScrollY < startScroll) {
|
||||
resetLinuxRobotFall({ keepSourceHidden: hasLinuxRobotDeparted.value });
|
||||
return;
|
||||
|
|
@ -165,10 +171,7 @@ function updateLinuxRobotFall() {
|
|||
|
||||
if (linuxRobotFlightState.value === 'idle') {
|
||||
launchLinuxRobotFall(metrics);
|
||||
return;
|
||||
}
|
||||
|
||||
if (linuxRobotFlightState.value === 'landed') return;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
|
@ -177,6 +180,7 @@ onMounted(() => {
|
|||
|
||||
nextTick(() => {
|
||||
const linuxCard = document.querySelector<HTMLElement>('[data-download-os="linux"]');
|
||||
const faqTarget = document.querySelector<HTMLElement>('[data-faq-landing-target]');
|
||||
if (!linuxCard) return;
|
||||
|
||||
linuxRobotObserver = new IntersectionObserver(
|
||||
|
|
@ -193,21 +197,31 @@ onMounted(() => {
|
|||
);
|
||||
|
||||
linuxRobotObserver.observe(linuxCard);
|
||||
|
||||
if (faqTarget) {
|
||||
faqLandingResizeObserver = new ResizeObserver(scheduleLinuxRobotFallUpdate);
|
||||
faqLandingResizeObserver.observe(faqTarget);
|
||||
}
|
||||
|
||||
scheduleLinuxRobotFallUpdate();
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', scheduleLinuxRobotFallUpdate, { passive: true });
|
||||
window.addEventListener('resize', scheduleLinuxRobotFallUpdate);
|
||||
window.visualViewport?.addEventListener('resize', scheduleLinuxRobotFallUpdate);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
linuxRobotObserver?.disconnect();
|
||||
linuxRobotObserver = null;
|
||||
faqLandingResizeObserver?.disconnect();
|
||||
faqLandingResizeObserver = null;
|
||||
if (linuxRobotFallRaf) window.cancelAnimationFrame(linuxRobotFallRaf);
|
||||
linuxRobotFallRaf = 0;
|
||||
clearLinuxRobotFallTimer();
|
||||
window.removeEventListener('scroll', scheduleLinuxRobotFallUpdate);
|
||||
window.removeEventListener('resize', scheduleLinuxRobotFallUpdate);
|
||||
window.visualViewport?.removeEventListener('resize', scheduleLinuxRobotFallUpdate);
|
||||
});
|
||||
|
||||
const platformIcons: Record<string, string> = {
|
||||
|
|
@ -262,12 +276,6 @@ const releaseDate = computed(() => {
|
|||
});
|
||||
});
|
||||
|
||||
const devBranchUrl = computed(() => `${repoUrl.value}/tree/dev`);
|
||||
const devBranchNote = computed(() =>
|
||||
locale.value === 'ru'
|
||||
? 'Самая свежая версия доступна в ветке dev - можно развернуть локально.'
|
||||
: 'Freshest version is available on the dev branch - clone and run it locally.',
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -367,15 +375,6 @@ const devBranchNote = computed(() =>
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
class="download-section__dev-note"
|
||||
:href="devBranchUrl"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
{{ devBranchNote }}
|
||||
</a>
|
||||
|
||||
<p v-if="isMounted && releaseVersion" class="download-section__release-info">
|
||||
v{{ releaseVersion }} · {{ releaseDate }}
|
||||
</p>
|
||||
|
|
@ -578,7 +577,7 @@ const devBranchNote = computed(() =>
|
|||
opacity: 0.96;
|
||||
transform:
|
||||
translate3d(var(--fall-x), var(--fall-y), 0)
|
||||
rotate(-5deg)
|
||||
rotate(355deg)
|
||||
scale(0.95);
|
||||
}
|
||||
}
|
||||
|
|
@ -887,38 +886,6 @@ const devBranchNote = computed(() =>
|
|||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
|
||||
.download-section__dev-note {
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
max-width: min(620px, calc(100vw - 32px));
|
||||
margin: 18px auto 0;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid rgba(0, 240, 255, 0.12);
|
||||
border-radius: 10px;
|
||||
background: rgba(0, 240, 255, 0.035);
|
||||
color: #00f0ff;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 0.76rem;
|
||||
line-height: 1.55;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
opacity: 0.82;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
background 0.2s ease,
|
||||
color 0.2s ease,
|
||||
opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.download-section__dev-note:hover {
|
||||
border-color: rgba(57, 255, 20, 0.24);
|
||||
background: rgba(57, 255, 20, 0.045);
|
||||
color: #39ff14;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes downloadFadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,14 @@ const withBase = (path: string) => `${baseURL.replace(/\/?$/, "/")}${path.replac
|
|||
useCyberHeroParallax(heroRef);
|
||||
|
||||
const releaseVersion = computed(() => releaseData.value?.version || null);
|
||||
const releaseDate = computed(() => {
|
||||
if (!releaseData.value?.pubDate) return "";
|
||||
return new Date(releaseData.value.pubDate).toLocaleDateString(locale.value, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
});
|
||||
const activeHeroMessage = computed(() => heroMessages[activeHeroMessageIndex.value] ?? null);
|
||||
|
||||
const heroDownloadUrl = computed(() => {
|
||||
|
|
@ -114,6 +122,7 @@ onUnmounted(() => {
|
|||
|
||||
<template>
|
||||
<section id="hero" ref="heroRef" class="hero-section cyber-hero section anchor-offset" data-cyber-hero>
|
||||
<CyberHeroMontereyBackground />
|
||||
<div class="cyber-hero__background" aria-hidden="true" />
|
||||
<div class="cyber-hero__wash" aria-hidden="true" />
|
||||
<div class="cyber-hero__gridlines" aria-hidden="true" />
|
||||
|
|
@ -163,6 +172,9 @@ onUnmounted(() => {
|
|||
>
|
||||
<span class="cyber-hero__release">
|
||||
v{{ releaseVersion }}
|
||||
<span v-if="releaseDate" class="cyber-hero__release-date">
|
||||
· {{ releaseDate }}
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { computed, getCurrentInstance, onUnmounted, watch } from "vue";
|
||||
import { computed, getCurrentInstance, onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import type { Ref } from "vue";
|
||||
import { useThemeStore } from "~/stores/theme";
|
||||
|
||||
type ThemeName = "light" | "dark";
|
||||
|
||||
type VuetifyThemeInstance = {
|
||||
global: {
|
||||
name: Ref<string>;
|
||||
|
|
@ -10,40 +12,109 @@ type VuetifyThemeInstance = {
|
|||
change?: (name: string) => void;
|
||||
};
|
||||
|
||||
function isThemeName(value: string | null | undefined): value is ThemeName {
|
||||
return value === "dark" || value === "light";
|
||||
}
|
||||
|
||||
export const useBrowserTheme = () => {
|
||||
const themeStore = useThemeStore();
|
||||
const { $vuetifyTheme } = useNuxtApp();
|
||||
const vuetifyTheme = $vuetifyTheme as VuetifyThemeInstance | null;
|
||||
const documentTheme = ref<ThemeName | null>(null);
|
||||
let mediaQueryHandler: ((event: MediaQueryListEvent) => void) | null = null;
|
||||
let mediaQuery: MediaQueryList | null = null;
|
||||
let themeClassObserver: MutationObserver | null = null;
|
||||
|
||||
const applyVuetifyTheme = (name: "light" | "dark") => {
|
||||
if (!vuetifyTheme) return;
|
||||
if (typeof vuetifyTheme.change === "function") {
|
||||
vuetifyTheme.change(name);
|
||||
} else {
|
||||
vuetifyTheme.global.name.value = name;
|
||||
}
|
||||
const getDocumentTheme = (): ThemeName | null => {
|
||||
if (!import.meta.client) return null;
|
||||
|
||||
const appClass = document.querySelector(".v-application")?.classList;
|
||||
if (appClass?.contains("v-theme--dark")) return "dark";
|
||||
if (appClass?.contains("v-theme--light")) return "light";
|
||||
return null;
|
||||
};
|
||||
|
||||
const applyTheme = (name: "light" | "dark") => {
|
||||
themeStore.setTheme(name, true);
|
||||
const refreshDocumentTheme = () => {
|
||||
documentTheme.value = getDocumentTheme();
|
||||
return documentTheme.value;
|
||||
};
|
||||
|
||||
const applyDocumentTheme = (name: ThemeName) => {
|
||||
if (!import.meta.client) return;
|
||||
|
||||
document.querySelectorAll(".v-application").forEach((app) => {
|
||||
app.classList.toggle("v-theme--dark", name === "dark");
|
||||
app.classList.toggle("v-theme--light", name === "light");
|
||||
});
|
||||
documentTheme.value = name;
|
||||
};
|
||||
|
||||
const getAppliedTheme = (): ThemeName => {
|
||||
const domTheme = getDocumentTheme() ?? documentTheme.value;
|
||||
if (domTheme) return domTheme;
|
||||
|
||||
const vuetifyName = vuetifyTheme?.global.name.value;
|
||||
if (isThemeName(vuetifyName)) return vuetifyName;
|
||||
|
||||
return themeStore.current;
|
||||
};
|
||||
|
||||
const syncStoreFromAppliedTheme = () => {
|
||||
const appliedTheme = getAppliedTheme();
|
||||
if (themeStore.current !== appliedTheme) {
|
||||
themeStore.setTheme(appliedTheme, false);
|
||||
}
|
||||
return appliedTheme;
|
||||
};
|
||||
|
||||
const applyVuetifyTheme = (name: ThemeName) => {
|
||||
if (!vuetifyTheme) return;
|
||||
|
||||
if (vuetifyTheme.change) {
|
||||
vuetifyTheme.change(name);
|
||||
return;
|
||||
}
|
||||
|
||||
vuetifyTheme.global.name.value = name;
|
||||
};
|
||||
|
||||
const applyTheme = (name: ThemeName, fromUser = true) => {
|
||||
applyVuetifyTheme(name);
|
||||
applyDocumentTheme(name);
|
||||
themeStore.setTheme(name, fromUser);
|
||||
return name;
|
||||
};
|
||||
|
||||
const observeDocumentTheme = () => {
|
||||
if (!import.meta.client || themeClassObserver) return;
|
||||
|
||||
const app = document.querySelector(".v-application");
|
||||
if (!app) return;
|
||||
|
||||
refreshDocumentTheme();
|
||||
themeClassObserver = new MutationObserver(() => {
|
||||
refreshDocumentTheme();
|
||||
});
|
||||
themeClassObserver.observe(app, { attributes: true, attributeFilter: ["class"] });
|
||||
};
|
||||
|
||||
const initTheme = () => {
|
||||
if (!import.meta.client) return;
|
||||
const initialTheme = themeStore.getInitialTheme();
|
||||
themeStore.setTheme(initialTheme, false);
|
||||
applyVuetifyTheme(initialTheme);
|
||||
applyTheme(initialTheme, false);
|
||||
|
||||
if (mediaQuery && mediaQueryHandler) {
|
||||
mediaQuery.removeEventListener("change", mediaQueryHandler);
|
||||
mediaQuery = null;
|
||||
mediaQueryHandler = null;
|
||||
}
|
||||
|
||||
if (!themeStore.userSelected) {
|
||||
mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
mediaQueryHandler = (event: MediaQueryListEvent) => {
|
||||
if (!themeStore.userSelected) {
|
||||
const newTheme = event.matches ? "dark" : "light";
|
||||
themeStore.setTheme(newTheme, false);
|
||||
applyVuetifyTheme(newTheme);
|
||||
applyTheme(newTheme, false);
|
||||
}
|
||||
};
|
||||
mediaQuery.addEventListener("change", mediaQueryHandler);
|
||||
|
|
@ -51,27 +122,54 @@ export const useBrowserTheme = () => {
|
|||
};
|
||||
|
||||
const toggleTheme = () => {
|
||||
applyTheme(themeStore.current === "dark" ? "light" : "dark");
|
||||
const appliedTheme = syncStoreFromAppliedTheme();
|
||||
return applyTheme(appliedTheme === "dark" ? "light" : "dark");
|
||||
};
|
||||
|
||||
if (getCurrentInstance()) {
|
||||
onMounted(() => {
|
||||
refreshDocumentTheme();
|
||||
observeDocumentTheme();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (mediaQuery && mediaQueryHandler) {
|
||||
mediaQuery.removeEventListener("change", mediaQueryHandler);
|
||||
}
|
||||
themeClassObserver?.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => themeStore.current,
|
||||
(value) => {
|
||||
applyVuetifyTheme(value as "light" | "dark");
|
||||
applyVuetifyTheme(value);
|
||||
}
|
||||
);
|
||||
|
||||
if (vuetifyTheme) {
|
||||
watch(
|
||||
() => vuetifyTheme.global.name.value,
|
||||
(value) => {
|
||||
if (isThemeName(value) && themeStore.current !== value) {
|
||||
themeStore.setTheme(value, false);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const currentTheme = computed(() => {
|
||||
if (documentTheme.value) return documentTheme.value;
|
||||
|
||||
const vuetifyName = vuetifyTheme?.global.name.value;
|
||||
return isThemeName(vuetifyName) ? vuetifyName : themeStore.current;
|
||||
});
|
||||
|
||||
const isDark = computed(() => currentTheme.value === "dark");
|
||||
|
||||
return {
|
||||
currentTheme: computed(() => themeStore.current),
|
||||
isDark: computed(() => themeStore.current === "dark"),
|
||||
currentTheme,
|
||||
isDark,
|
||||
initTheme,
|
||||
toggleTheme
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import robotAvatarCyan from "~/assets/images/hero/robots/robot-avatar-cyan-v1.webp";
|
||||
import robotAvatarMagenta from "~/assets/images/hero/robots/robot-avatar-magenta-v1.webp";
|
||||
import robotAvatarCyan from "~/assets/images/hero/robots/robot-avatar-cyan-cat-v1.webp";
|
||||
import robotAvatarReviewerTeal from "~/assets/images/hero/robots/robot-avatar-reviewer-teal-v1.webp";
|
||||
import robotAvatarSeatedMagenta from "~/assets/images/hero/robots/robot-avatar-seated-magenta-v1.webp";
|
||||
import robotAvatarYellow from "~/assets/images/hero/robots/robot-avatar-yellow-star-v1.webp";
|
||||
import robotRedPurpleHandshake from "~/assets/images/hero/robots/robot-red-purple-handshake-v1.webp";
|
||||
|
||||
export const HERO_SCENE_BREAKPOINTS = {
|
||||
desktop: 1200,
|
||||
|
|
@ -95,8 +97,8 @@ export const heroAgents: readonly HeroAgent[] = [
|
|||
{
|
||||
id: "developer",
|
||||
label: "Developer",
|
||||
asset: robotAvatarMagenta,
|
||||
accent: "magenta",
|
||||
asset: robotAvatarYellow,
|
||||
accent: "amber",
|
||||
facing: 1,
|
||||
lean: -1,
|
||||
priority: true,
|
||||
|
|
@ -110,12 +112,16 @@ export const heroAgents: readonly HeroAgent[] = [
|
|||
|
||||
export const heroReviewerFeatureCard = {
|
||||
label: "Reviewer",
|
||||
asset: robotAvatarMagenta,
|
||||
accent: "magenta",
|
||||
asset: robotAvatarReviewerTeal,
|
||||
accent: "cyan",
|
||||
status: "Reviewing",
|
||||
tasks: ["Review code", "Check quality", "Request changes"],
|
||||
} as const;
|
||||
|
||||
export const heroCollaborationFeature = {
|
||||
asset: robotRedPurpleHandshake,
|
||||
} as const;
|
||||
|
||||
export const heroMessages: readonly HeroMessage[] = [
|
||||
{
|
||||
id: "plan-ready",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "كيف نقارن",
|
||||
"sectionSubtitle": "مقارنة تفصيلية للمميزات مع أدوات البرمجة بالذكاء الاصطناعي الأخرى.",
|
||||
"feature": "الميزة",
|
||||
"robotBubble": "احكم بنفسك",
|
||||
"features": {
|
||||
"crossTeam": "التواصل بين الفرق",
|
||||
"agentMessaging": "مراسلة بين الوكلاء",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "تنسيق وكلاء الذكاء الاصطناعي للمطورين",
|
||||
"robotBubble": "أنا أنتظر",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "التوثيق"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "Wie wir im Vergleich abschneiden",
|
||||
"sectionSubtitle": "Funktionsvergleich mit anderen KI-Coding-Tools.",
|
||||
"feature": "Funktion",
|
||||
"robotBubble": "Urteile selbst",
|
||||
"features": {
|
||||
"crossTeam": "Teamübergreifende Kommunikation",
|
||||
"agentMessaging": "Agent-zu-Agent-Messaging",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "KI-Agenten-Orchestrierung für Entwickler",
|
||||
"robotBubble": "Ich warte",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "Dokumentation"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "How we compare",
|
||||
"sectionSubtitle": "Feature-by-feature comparison with other AI coding tools.",
|
||||
"feature": "Feature",
|
||||
"robotBubble": "Judge for yourself",
|
||||
"features": {
|
||||
"crossTeam": "Cross-team communication",
|
||||
"agentMessaging": "Agent-to-agent messaging",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "AI agent orchestration for developers",
|
||||
"robotBubble": "I'm waiting",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "Documentation"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "Cómo nos comparamos",
|
||||
"sectionSubtitle": "Comparación detallada de funciones con otras herramientas de programación con IA.",
|
||||
"feature": "Función",
|
||||
"robotBubble": "Juzga tú",
|
||||
"features": {
|
||||
"crossTeam": "Comunicación entre equipos",
|
||||
"agentMessaging": "Mensajería entre agentes",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "Orquestación de agentes IA para desarrolladores",
|
||||
"robotBubble": "Estoy esperando",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "Documentación"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "Comment nous nous comparons",
|
||||
"sectionSubtitle": "Comparaison fonctionnalité par fonctionnalité avec d'autres outils IA.",
|
||||
"feature": "Fonctionnalité",
|
||||
"robotBubble": "Juge par toi-même",
|
||||
"features": {
|
||||
"crossTeam": "Communication inter-équipes",
|
||||
"agentMessaging": "Messagerie entre agents",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "Orchestration d'agents IA pour développeurs",
|
||||
"robotBubble": "J'attends",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "Documentation"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "तुलना करें",
|
||||
"sectionSubtitle": "अन्य AI कोडिंग टूल्स के साथ सुविधा-दर-सुविधा तुलना।",
|
||||
"feature": "सुविधा",
|
||||
"robotBubble": "खुद फैसला करें",
|
||||
"features": {
|
||||
"crossTeam": "क्रॉस-टीम संचार",
|
||||
"agentMessaging": "एजेंट-टू-एजेंट मैसेजिंग",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "डेवलपर्स के लिए AI एजेंट ऑर्केस्ट्रेशन",
|
||||
"robotBubble": "मैं इंतज़ार कर रहा हूँ",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "दस्तावेज़"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "他ツールとの比較",
|
||||
"sectionSubtitle": "他のAIコーディングツールとの機能比較。",
|
||||
"feature": "機能",
|
||||
"robotBubble": "自分で判断して",
|
||||
"features": {
|
||||
"crossTeam": "チーム間コミュニケーション",
|
||||
"agentMessaging": "エージェント間メッセージング",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "開発者向けAIエージェントオーケストレーション",
|
||||
"robotBubble": "待ってるよ",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "ドキュメント"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "Como nos comparamos",
|
||||
"sectionSubtitle": "Comparação detalhada de recursos com outras ferramentas de programação com IA.",
|
||||
"feature": "Recurso",
|
||||
"robotBubble": "Julgue você",
|
||||
"features": {
|
||||
"crossTeam": "Comunicação entre equipes",
|
||||
"agentMessaging": "Mensagens entre agentes",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "Orquestração de agentes IA para desenvolvedores",
|
||||
"robotBubble": "Estou esperando",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "Documentação"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "Сравнение с конкурентами",
|
||||
"sectionSubtitle": "Подробное сравнение возможностей с другими AI-инструментами для разработки.",
|
||||
"feature": "Возможность",
|
||||
"robotBubble": "Суди сам",
|
||||
"features": {
|
||||
"crossTeam": "Межкомандная коммуникация",
|
||||
"agentMessaging": "Обмен сообщениями между агентами",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "Оркестрация ИИ-агентов для разработчиков",
|
||||
"robotBubble": "Я жду",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "Документация"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"sectionTitle": "功能对比",
|
||||
"sectionSubtitle": "与其他 AI 编程工具的逐项功能对比。",
|
||||
"feature": "功能",
|
||||
"robotBubble": "你来判断",
|
||||
"features": {
|
||||
"crossTeam": "跨团队通信",
|
||||
"agentMessaging": "智能体间消息",
|
||||
|
|
@ -98,6 +99,7 @@
|
|||
"footer": {
|
||||
"copyright": "© {year} Agent Teams",
|
||||
"tagline": "面向开发者的 AI 智能体编排",
|
||||
"robotBubble": "我在等你",
|
||||
"links": {
|
||||
"github": "GitHub",
|
||||
"docs": "文档"
|
||||
|
|
|
|||
7
landing/package-lock.json
generated
7
landing/package-lock.json
generated
|
|
@ -6,6 +6,7 @@
|
|||
"": {
|
||||
"name": "agent-teams-landing",
|
||||
"dependencies": {
|
||||
"@firecms/neat": "^0.8.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@nuxtjs/i18n": "^9.5.6",
|
||||
"@pinia/nuxt": "^0.11.3",
|
||||
|
|
@ -1314,6 +1315,12 @@
|
|||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@firecms/neat": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@firecms/neat/-/neat-0.8.0.tgz",
|
||||
"integrity": "sha512-gwvYd63voJa+ZtEt6SW3toJwVx9smisKuXE7vsXvZtlGPzsWpR1lzaltIsijuPkg8Qj/ybS2tEdsttWE7g2KsA==",
|
||||
"license": "MIT AND Commons-Clause"
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"format:fix": "prettier . --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@firecms/neat": "^0.8.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@nuxtjs/i18n": "^9.5.6",
|
||||
"@pinia/nuxt": "^0.11.3",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ export default defineNuxtPlugin({
|
|||
const { initTheme } = useBrowserTheme();
|
||||
const { initLocale } = useLocation();
|
||||
|
||||
// Run after hydration to avoid SSR/CSR mismatches.
|
||||
initTheme();
|
||||
|
||||
nuxtApp.hook("app:mounted", () => {
|
||||
initTheme();
|
||||
initLocale();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import "vuetify/styles";
|
|||
import { createVuetify } from "vuetify";
|
||||
import { aliases, mdi } from "vuetify/iconsets/mdi-svg";
|
||||
|
||||
type ThemeName = "light" | "dark";
|
||||
|
||||
const brand = {
|
||||
cyan: "#00f0ff",
|
||||
magenta: "#ff00ff",
|
||||
|
|
@ -11,9 +13,28 @@ const brand = {
|
|||
darkSurface: "#12121a"
|
||||
};
|
||||
|
||||
function isThemeName(value: string | null | undefined): value is ThemeName {
|
||||
return value === "dark" || value === "light";
|
||||
}
|
||||
|
||||
function resolveInitialTheme(cookieTheme: ThemeName | null): ThemeName {
|
||||
if (import.meta.client) {
|
||||
const saved = localStorage.getItem("theme");
|
||||
if (isThemeName(saved)) return saved;
|
||||
if (cookieTheme) return cookieTheme;
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
}
|
||||
|
||||
return cookieTheme ?? "light";
|
||||
}
|
||||
|
||||
export default defineNuxtPlugin({
|
||||
name: "vuetify",
|
||||
setup(nuxtApp) {
|
||||
const themeCookie = useCookie<ThemeName | null>("theme");
|
||||
const cookieTheme = isThemeName(themeCookie.value) ? themeCookie.value : null;
|
||||
const defaultTheme = resolveInitialTheme(cookieTheme);
|
||||
|
||||
const vuetify = createVuetify({
|
||||
icons: {
|
||||
defaultSet: "mdi",
|
||||
|
|
@ -21,7 +42,7 @@ export default defineNuxtPlugin({
|
|||
sets: { mdi }
|
||||
},
|
||||
theme: {
|
||||
defaultTheme: "dark",
|
||||
defaultTheme,
|
||||
themes: {
|
||||
light: {
|
||||
colors: {
|
||||
|
|
|
|||
|
|
@ -1,30 +1,60 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
type ThemeName = "light" | "dark";
|
||||
const themeCookieName = "theme";
|
||||
|
||||
function isThemeName(value: string | null | undefined): value is ThemeName {
|
||||
return value === "dark" || value === "light";
|
||||
}
|
||||
|
||||
function getCookieTheme(): ThemeName | null {
|
||||
if (!import.meta.client) return null;
|
||||
|
||||
const cookie = document.cookie
|
||||
.split("; ")
|
||||
.find((item) => item.startsWith(`${themeCookieName}=`));
|
||||
const value = cookie ? decodeURIComponent(cookie.split("=").slice(1).join("=")) : null;
|
||||
return isThemeName(value) ? value : null;
|
||||
}
|
||||
|
||||
function persistTheme(theme: ThemeName) {
|
||||
localStorage.setItem(themeCookieName, theme);
|
||||
document.cookie = `${themeCookieName}=${theme}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
||||
}
|
||||
|
||||
export const useThemeStore = defineStore("theme", {
|
||||
state: () => ({
|
||||
current: "dark" as ThemeName,
|
||||
current: "light" as ThemeName,
|
||||
userSelected: false
|
||||
}),
|
||||
actions: {
|
||||
getInitialTheme(): ThemeName {
|
||||
if (!import.meta.client) return "dark";
|
||||
const saved = localStorage.getItem("theme");
|
||||
if (saved === "dark" || saved === "light") {
|
||||
if (!import.meta.client) return "light";
|
||||
|
||||
const saved = localStorage.getItem(themeCookieName);
|
||||
if (isThemeName(saved)) {
|
||||
this.userSelected = true;
|
||||
persistTheme(saved);
|
||||
return saved;
|
||||
}
|
||||
|
||||
const cookieTheme = getCookieTheme();
|
||||
if (cookieTheme) {
|
||||
this.userSelected = true;
|
||||
persistTheme(cookieTheme);
|
||||
return cookieTheme;
|
||||
}
|
||||
|
||||
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
return "dark";
|
||||
}
|
||||
return "dark";
|
||||
return "light";
|
||||
},
|
||||
setTheme(theme: ThemeName, fromUser: boolean) {
|
||||
this.current = theme;
|
||||
if (import.meta.client && fromUser) {
|
||||
this.userSelected = true;
|
||||
localStorage.setItem("theme", theme);
|
||||
persistTheme(theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue