Files
FacereWeb/styles.css
Knowit ea85a01edf Install scene: fix cmd-bar overlap, swap install command
The "CLICK TO COPY" hint was absolute-positioned at top:10/right:14 of
the cmd-box, landing on top of "~/facere — bash" in the bar header.
Move the hint into the bar as a flex sibling so the title and hint
share the row cleanly without overlap.

Replace the placeholder install command with the real installer pulled
from raw.githubusercontent.com, with FACERE_GH_TOKEN as a
"contact us for demo" placeholder.

Also fix deploy/build.sh: `npm init -y` rejects the leading-dot dir
name `.build`, and the preset must be resolved by absolute path since
babel runs from the repo root.
2026-05-08 00:45:21 +08:00

1051 lines
33 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Facere — cinematic dark landing
No nav, no marketing chrome. The 3 attached images are the protagonists. */
:root {
--bg: #02070d;
--bg-2: #050b13;
--cyan: #21eaff;
--cyan-soft: #65f4ff;
--cyan-muted: #2b7e8a;
--cyan-deep: #0a3a44;
--orange: #ff8a1f;
--orange-soft: #ffb347;
--ink: #cfeef5;
--ink-mid: #6da6b1;
--ink-low: #2b5560;
}
* { box-sizing: border-box; }
html, body, #root { margin: 0; padding: 0; background: var(--bg); }
body {
font-family: "IBM Plex Mono", "VT323", ui-monospace, SFMono-Regular, Menlo, monospace;
color: var(--ink);
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
/* ───────────────── Stage / scroll ───────────────── */
.scroll-track {
/* Long total scroll — extra-long product dwell */
height: 700vh;
position: relative;
background: var(--bg);
}
.stage {
position: sticky;
top: 0;
height: 100vh;
overflow: hidden;
background: var(--bg);
}
.scene-stack { position: absolute; inset: 0; }
.scene-slot {
position: absolute;
inset: 0;
will-change: opacity;
transform: translateZ(0);
backface-visibility: hidden;
contain: layout paint;
}
.scene {
position: absolute;
inset: 0;
overflow: hidden;
isolation: isolate;
}
/* ───────────────── Background images ─────────────────
Backgrounds are wrapped in a div we can scale via GSAP for camera push-in. */
.bg-wrap {
position: absolute;
inset: 0;
overflow: hidden;
transform-origin: 65% 50%;
will-change: transform;
transform: translateZ(0);
}
.scene-product .bg-wrap { transform-origin: 50% 60%; }
.scene-install .bg-wrap { transform-origin: 78% 50%; }
/* Image-aspect frame: replicates `background-size: cover` geometry as an
actual box, so any %-positioned overlay (smoke, sparks, chip pulses…)
stays locked to the image content regardless of viewport aspect.
Uses vw/vh because the bg-wrap is always 100vw × 100vh of the stage. */
.image-frame {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translateZ(0);
aspect-ratio: 1672 / 941;
width: max(100vw, calc(100vh * 1672 / 941));
will-change: transform;
}
.bg-image {
position: absolute;
inset: 0;
background-size: 100% 100%;
background-position: center center;
background-repeat: no-repeat;
/* Crisp pixel-art feel without softening the source */
image-rendering: -webkit-optimize-contrast;
}
/* WebP-encoded backgrounds for each scene. Default is the full-res
variant; the mobile media query below swaps in a smaller one so
phones decode ~50KB instead of ~190KB. */
.scene-hero .bg-image { background-image: url("assets/exterior-factory.webp"); }
.scene-product .bg-image { background-image: url("assets/factory-interior.webp"); }
.scene-install .bg-image { background-image: url("assets/pcb-chip.webp"); }
.vignette {
position: absolute;
inset: 0;
pointer-events: none;
background:
radial-gradient(ellipse at 70% 50%, transparent 25%, rgba(2, 7, 13, 0.55) 75%, rgba(2, 7, 13, 0.95) 100%),
linear-gradient(180deg, rgba(2, 7, 13, 0.45) 0%, transparent 30%, transparent 70%, rgba(2, 7, 13, 0.55) 100%);
}
.product-vignette {
background:
radial-gradient(ellipse at 60% 65%, transparent 20%, rgba(2, 7, 13, 0.6) 80%, rgba(2, 7, 13, 0.95) 100%);
}
.haze {
position: absolute;
inset: 0;
pointer-events: none;
background:
radial-gradient(ellipse at 50% 90%, rgba(255, 138, 31, 0.08), transparent 55%),
radial-gradient(ellipse at 50% 105%, rgba(33, 234, 255, 0.05), transparent 60%);
mix-blend-mode: screen;
}
.haze-deep {
background:
radial-gradient(circle at 80% 45%, rgba(33, 234, 255, 0.06), transparent 45%),
radial-gradient(circle at 80% 65%, rgba(255, 138, 31, 0.06), transparent 50%);
mix-blend-mode: screen;
}
/* ───────────────── Grain overlay ───────────────── */
.grain {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 50;
opacity: 0.10;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.8 0 0 0 0 0.95 0 0 0 0 1 0 0 0 0.6 0'/></filter><rect width='240' height='240' filter='url(%23n)'/></svg>");
animation: grain-shift 1.2s steps(6) infinite;
}
@keyframes grain-shift {
0% { transform: translate(0, 0); }
20% { transform: translate(-3%, 1%); }
40% { transform: translate(2%, -2%); }
60% { transform: translate(-1%, 2%); }
80% { transform: translate(3%, -1%); }
100% { transform: translate(0, 0); }
}
/* ───────────────── Pixel logo ───────────────── */
.pixel-logo {
position: relative;
display: inline-block;
}
.pixel-logo-grid {
display: flex;
align-items: flex-start;
}
.pixel-letter {
display: grid;
}
.px {
display: block;
background: transparent;
width: 100%;
height: 100%;
}
.px.on {
background: #2bb7ff;
box-shadow:
0 0 2px rgba(33, 200, 255, 0.6),
0 0 12px rgba(33, 234, 255, 0.25);
animation: pixel-pulse 4.2s ease-in-out infinite;
}
@keyframes pixel-pulse {
0%, 100% { filter: brightness(1); }
50% { filter: brightness(1.18); }
}
/* Glitch ghosts */
.pixel-logo-ghost {
position: absolute;
inset: 0;
pointer-events: none;
mix-blend-mode: screen;
opacity: 0;
}
.ghost-r { animation: glitch-r 12s steps(1) infinite; }
.ghost-c { animation: glitch-c 12s steps(1) infinite; }
/* Many-band signal breakage. Multiple bursts per cycle, each band offset
independently from the others. Some frames near-fully corrupt. */
@keyframes glitch-r {
0%, 6% { opacity: 0; transform: translate(0, 0); clip-path: none; }
8% { opacity: 0.9; transform: translate(8px, 0); clip-path: inset(5% 0 78% 0); }
9% { opacity: 0.7; transform: translate(-4px, 0); clip-path: inset(15% 0 70% 0); }
10% { opacity: 0; }
18% { opacity: 0.85; transform: translate(11px, 0); clip-path: inset(28% 0 58% 0); }
19% { opacity: 0; }
24% { opacity: 0.7; transform: translate(-7px, 0); clip-path: inset(48% 0 38% 0); }
25% { opacity: 0.5; transform: translate(3px, 0); clip-path: inset(52% 0 36% 0); }
26% { opacity: 0; }
/* small quiet patch */
36% { opacity: 0.65; transform: translate(5px, 0); clip-path: inset(62% 0 24% 0); }
37% { opacity: 0; }
44% { opacity: 0.95; transform: translate(-9px, 0); clip-path: inset(72% 0 14% 0); }
45% { opacity: 0.6; transform: translate(4px, 0); clip-path: inset(78% 0 8% 0); }
46% { opacity: 0; }
56% { opacity: 0.55; transform: translate(2px, 0); clip-path: inset(18% 0 70% 0); }
57% { opacity: 0; }
64% { opacity: 0.9; transform: translate(-10px, 0); clip-path: inset(35% 0 50% 0); }
65% { opacity: 0; }
72% { opacity: 0.7; transform: translate(6px, 0); clip-path: inset(58% 0 30% 0); }
73% { opacity: 0; }
/* full corruption flash */
82% { opacity: 0.85; transform: translate(7px, 0); clip-path: none; }
83% { opacity: 0; }
90% { opacity: 0.6; transform: translate(-3px, 0); clip-path: inset(68% 0 22% 0); }
91%, 100% { opacity: 0; transform: translate(0, 0); clip-path: none; }
}
@keyframes glitch-c {
0%, 6% { opacity: 0; transform: translate(0, 0); clip-path: none; }
8% { opacity: 0.85; transform: translate(-8px, 0); clip-path: inset(5% 0 78% 0); }
9% { opacity: 0.65; transform: translate(4px, 0); clip-path: inset(15% 0 70% 0); }
10% { opacity: 0; }
18% { opacity: 0.8; transform: translate(-11px, 0); clip-path: inset(28% 0 58% 0); }
19% { opacity: 0; }
24% { opacity: 0.65; transform: translate(7px, 0); clip-path: inset(48% 0 38% 0); }
25% { opacity: 0.45; transform: translate(-3px, 0); clip-path: inset(52% 0 36% 0); }
26% { opacity: 0; }
36% { opacity: 0.6; transform: translate(-5px, 0); clip-path: inset(62% 0 24% 0); }
37% { opacity: 0; }
44% { opacity: 0.9; transform: translate(9px, 0); clip-path: inset(72% 0 14% 0); }
45% { opacity: 0.55; transform: translate(-4px, 0); clip-path: inset(78% 0 8% 0); }
46% { opacity: 0; }
56% { opacity: 0.5; transform: translate(-2px, 0); clip-path: inset(18% 0 70% 0); }
57% { opacity: 0; }
64% { opacity: 0.85; transform: translate(10px, 0); clip-path: inset(35% 0 50% 0); }
65% { opacity: 0; }
72% { opacity: 0.65; transform: translate(-6px, 0); clip-path: inset(58% 0 30% 0); }
73% { opacity: 0; }
82% { opacity: 0.8; transform: translate(-7px, 0); clip-path: none; }
83% { opacity: 0; }
90% { opacity: 0.55; transform: translate(3px, 0); clip-path: inset(68% 0 22% 0); }
91%, 100% { opacity: 0; transform: translate(0, 0); clip-path: none; }
}
/* Logo sits on top of bg, no nav */
.content-hero {
position: absolute;
inset: 0;
display: flex;
align-items: center;
padding-left: 8vw;
z-index: 5;
}
.logo-wrap {
position: relative;
animation: logo-jitter 12s steps(1) infinite, logo-flicker 10s ease-in-out infinite, logo-dropout 28s steps(1) infinite;
}
/* Brightness flicker — frequent dips like signal loss */
@keyframes logo-flicker {
0%, 100% { filter: brightness(1) saturate(1); }
10% { filter: brightness(1.2) saturate(1.1); }
11% { filter: brightness(0.45); }
12% { filter: brightness(1.1); }
13% { filter: brightness(1); }
28% { filter: brightness(0.6); }
29% { filter: brightness(1.25); }
30% { filter: brightness(0.85); }
46% { filter: brightness(1.15); }
47% { filter: brightness(0.5); }
48% { filter: brightness(1); }
68% { filter: brightness(1.2); }
69% { filter: brightness(0.65); }
70% { filter: brightness(1.05); }
82% { filter: brightness(0.4); }
83% { filter: brightness(1.15); }
84% { filter: brightness(1); }
}
/* Logo body — many independent band shears, frequent bursts. */
@keyframes logo-jitter {
0%, 6%, 100% { transform: translate(0, 0); clip-path: none; }
8% { transform: translate(5px, 0); clip-path: inset(5% 0 78% 0); }
9% { transform: translate(-3px, 0); clip-path: inset(15% 0 70% 0); }
10% { transform: translate(0, 0); clip-path: none; }
18% { transform: translate(-6px, 0); clip-path: inset(28% 0 58% 0); }
19% { transform: translate(2px, 0); clip-path: none; }
24% { transform: translate(4px, 0); clip-path: inset(48% 0 38% 0); }
25% { transform: translate(-2px, 0); clip-path: inset(52% 0 36% 0); }
26% { transform: translate(0, 0); clip-path: none; }
36% { transform: translate(-3px, 0); clip-path: inset(62% 0 24% 0); }
37% { transform: translate(0, 0); clip-path: none; }
44% { transform: translate(7px, 0); clip-path: inset(72% 0 14% 0); }
45% { transform: translate(-4px, 0); clip-path: inset(78% 0 8% 0); }
46% { transform: translate(0, 0); clip-path: none; }
56% { transform: translate(-2px, 0); clip-path: inset(18% 0 70% 0); }
57% { transform: translate(0, 0); clip-path: none; }
64% { transform: translate(8px, 0); clip-path: inset(35% 0 50% 0); }
65% { transform: translate(-3px, 0); clip-path: none; }
72% { transform: translate(-5px, 0); clip-path: inset(58% 0 30% 0); }
73% { transform: translate(2px, 0); clip-path: none; }
82% { transform: translate(-4px, 0) skewX(-0.2deg); }
83% { transform: translate(0, 0); }
90% { transform: translate(3px, 0); clip-path: inset(68% 0 22% 0); }
91% { transform: translate(0, 0); clip-path: none; }
}
/* Independent dropout — small portions disappear briefly (transmission loss) */
@keyframes logo-dropout {
0%, 14% { opacity: 1; clip-path: none; }
15% { opacity: 1; clip-path: polygon(0 0, 100% 0, 100% 18%, 0 18%, 0 28%, 100% 28%, 100% 100%, 0 100%); }
16% { opacity: 1; clip-path: none; }
/* 18% black-out band lower */
33% { clip-path: polygon(0 0, 100% 0, 100% 60%, 0 60%, 0 72%, 100% 72%, 100% 100%, 0 100%); }
34% { clip-path: none; }
/* near-total dropout flash */
52% { opacity: 0.15; }
53% { opacity: 1; }
/* upper third missing */
68% { clip-path: polygon(0 32%, 100% 32%, 100% 100%, 0 100%); }
69% { clip-path: none; }
/* full split */
85% { clip-path: polygon(0 0, 100% 0, 100% 40%, 0 40%, 0 56%, 100% 56%, 100% 100%, 0 100%); }
86% { clip-path: none; }
92% { opacity: 0.4; }
93% { opacity: 1; }
}
/* Pixel-bar breaks — multiple thin dark bands across cycle */
.logo-wrap::after {
content: "";
position: absolute;
left: -80px; right: -80px;
top: 0;
height: 100%;
pointer-events: none;
z-index: 4;
background:
linear-gradient(rgba(2,7,13,0.95), rgba(2,7,13,0.95)) 0 12% / 100% 4% no-repeat,
linear-gradient(rgba(2,7,13,0.95), rgba(2,7,13,0.95)) 0 32% / 100% 3% no-repeat,
linear-gradient(rgba(2,7,13,0.95), rgba(2,7,13,0.95)) 0 50% / 100% 4% no-repeat,
linear-gradient(rgba(2,7,13,0.95), rgba(2,7,13,0.95)) 0 68% / 100% 3% no-repeat,
linear-gradient(rgba(2,7,13,0.95), rgba(2,7,13,0.95)) 0 84% / 100% 4% no-repeat;
opacity: 0;
animation: pixel-break 12s steps(1) infinite;
}
@keyframes pixel-break {
0%, 7% { opacity: 0; }
8% { opacity: 0.9; }
9%, 10% { opacity: 0; }
18% { opacity: 0.85; }
19% { opacity: 0; }
24%, 25% { opacity: 0.85; }
26% { opacity: 0; }
36% { opacity: 0.6; }
37% { opacity: 0; }
44%, 45% { opacity: 0.9; }
46% { opacity: 0; }
56% { opacity: 0.5; }
57% { opacity: 0; }
64% { opacity: 0.85; }
65% { opacity: 0; }
72% { opacity: 0.6; }
73% { opacity: 0; }
82% { opacity: 0.95; }
83% { opacity: 0; }
90% { opacity: 0.55; }
91%, 100% { opacity: 0; }
}
/* Fragmented scanline slices — multiple bands that jump left/right */
.logo-wrap::before {
content: "";
position: absolute;
left: -100px; right: -100px;
top: -10%;
height: 8%;
pointer-events: none;
z-index: 3;
mix-blend-mode: screen;
filter: blur(0.5px);
background:
linear-gradient(90deg,
transparent 0%,
rgba(33,234,255,0.15) 18%,
rgba(33,234,255,0.35) 38%,
transparent 48%,
transparent 56%,
rgba(33,234,255,0.30) 72%,
rgba(33,234,255,0.10) 88%,
transparent 100%);
opacity: 0;
animation: scan-tear 12s steps(1) infinite;
}
@keyframes scan-tear {
0%, 7% { opacity: 0; top: -8%; transform: translateX(0); }
8% { opacity: 0.9; top: 8%; transform: translateX(-12px); }
10% { opacity: 0; }
18% { opacity: 0.85; top: 26%; transform: translateX(14px); }
20% { opacity: 0; }
24% { opacity: 0.95; top: 44%; transform: translateX(-10px); }
26% { opacity: 0; }
36% { opacity: 0.7; top: 60%; transform: translateX(8px); }
37% { opacity: 0; }
44% { opacity: 0.95; top: 74%; transform: translateX(-14px); }
46% { opacity: 0; }
56% { opacity: 0.6; top: 16%; transform: translateX(10px); }
57% { opacity: 0; }
64% { opacity: 0.9; top: 38%; transform: translateX(-8px); }
65% { opacity: 0; }
72% { opacity: 0.7; top: 56%; transform: translateX(12px); }
73% { opacity: 0; }
82% { opacity: 0.95; top: 70%; transform: translateX(-10px); }
83% { opacity: 0; }
90% { opacity: 0.6; top: 88%; transform: translateX(6px); }
91%, 100% { opacity: 0; }
}
.logo-scanlines {
position: absolute;
inset: -8px -16px;
pointer-events: none;
background: repeating-linear-gradient(
180deg,
rgba(33, 234, 255, 0.04) 0px,
rgba(33, 234, 255, 0.04) 1px,
transparent 2px,
transparent 5px
);
opacity: 0.5;
mix-blend-mode: screen;
animation: scan-roll 8s linear infinite;
}
@keyframes scan-roll {
from { background-position: 0 0; }
to { background-position: 0 40px; }
}
/* ───────────────── Smoke ───────────────── */
.smoke-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 4;
}
.smoke-puff {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, rgba(170, 185, 200, 0.42) 0%, rgba(110, 135, 160, 0.22) 45%, transparent 72%);
clip-path: polygon(
25% 0%, 50% 0%, 75% 0%,
100% 25%, 100% 50%, 100% 75%,
75% 100%, 50% 100%, 25% 100%,
0% 75%, 0% 50%, 0% 25%
);
filter: blur(1.5px);
transform: translate(-50%, 0%) scale(0.25);
opacity: 0;
animation: smoke-rise linear infinite;
will-change: transform, opacity;
}
/* Smoke rises STRAIGHT UP from the chimney tip (top: ch.y is the tip).
y=0% at tip → y=-220% at end so it lifts well above. */
@keyframes smoke-rise {
0% { transform: translate(-50%, 0%) scale(0.30); opacity: 0; }
10% { opacity: 0.55; }
60% { opacity: 0.32; }
100% { transform: translate(calc(-50% + var(--drift, 0rem)), -260%) scale(1.4); opacity: 0; }
}
/* ───────────────── Embers / lights ───────────────── */
.ember-layer {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 3;
}
.ember {
position: absolute;
border-radius: 50%;
transform: translate(-50%, -50%);
animation: ember-flicker ease-in-out infinite;
}
.ember-warm {
background: var(--orange);
box-shadow: 0 0 4px rgba(255, 138, 31, 0.9), 0 0 10px rgba(255, 138, 31, 0.5);
}
.ember-soft {
background: var(--orange-soft);
box-shadow: 0 0 3px rgba(255, 179, 71, 0.8), 0 0 8px rgba(255, 179, 71, 0.4);
}
@keyframes ember-flicker {
0%, 100% { opacity: 0.9; transform: translate(-50%, -50%) scale(1); }
40% { opacity: 0.3; transform: translate(-50%, -50%) scale(0.85); }
55% { opacity: 1.0; transform: translate(-50%, -50%) scale(1.05); }
80% { opacity: 0.5; }
}
/* ───────────────── Sparks (interior) ───────────────── */
.spark-layer {
position: absolute;
/* Welding zone in image 2 — center of conveyor */
left: 49.5%;
top: 65.5%;
width: 2px;
height: 2px;
pointer-events: none;
z-index: 4;
}
.spark-core {
position: absolute;
left: -6px; top: -6px;
width: 12px; height: 12px;
border-radius: 50%;
background: radial-gradient(circle, rgba(255, 220, 130, 0.95) 0%, rgba(255, 138, 31, 0.6) 40%, transparent 75%);
filter: blur(1px);
animation: spark-core-pulse 1.4s ease-in-out infinite;
}
@keyframes spark-core-pulse {
0%, 100% { opacity: 0.55; transform: scale(0.85); }
35% { opacity: 1; transform: scale(1.4); }
60% { opacity: 0.7; transform: scale(1); }
}
.spark {
position: absolute;
left: 0; top: 0;
border-radius: 50%;
background: var(--orange-soft);
box-shadow: 0 0 4px rgba(255, 200, 100, 0.9), 0 0 8px rgba(255, 138, 31, 0.6);
animation: spark-fly ease-out infinite;
}
@keyframes spark-fly {
0% { transform: translate(0, 0) scale(1); opacity: 1; }
100% { transform: translate(var(--tx), var(--ty)) scale(0.3); opacity: 0; }
}
/* ───────────────── Robotic arm idle ───────────────── */
.arm-pivot {
position: absolute;
width: 1px; height: 1px;
pointer-events: none;
z-index: 2;
/* The arms in image 2 are roughly here — we just nudge a small overlay glow */
animation: arm-idle 5s ease-in-out infinite;
transform-origin: center;
}
.arm-left {
left: 44%; top: 55%;
background: radial-gradient(circle, rgba(255, 138, 31, 0.5) 0%, transparent 70%);
width: 60px; height: 60px;
margin: -30px 0 0 -30px;
filter: blur(8px);
animation-delay: -1.5s;
}
.arm-right {
left: 56%; top: 55%;
background: radial-gradient(circle, rgba(255, 138, 31, 0.5) 0%, transparent 70%);
width: 60px; height: 60px;
margin: -30px 0 0 -30px;
filter: blur(8px);
}
@keyframes arm-idle {
0%, 100% { opacity: 0.6; transform: translateY(0); }
50% { opacity: 0.95; transform: translateY(-2px); }
}
/* Conveyor light pulse: subtle bright row beneath */
.conveyor-pulse {
position: absolute;
left: 6%; right: 6%;
top: 78%;
height: 3px;
pointer-events: none;
z-index: 2;
background: linear-gradient(90deg,
transparent 0%,
rgba(255, 200, 100, 0) 10%,
rgba(255, 200, 100, 0.45) 50%,
rgba(255, 200, 100, 0) 90%,
transparent 100%);
filter: blur(2px);
animation: conveyor-pulse 4s ease-in-out infinite;
}
@keyframes conveyor-pulse {
0%, 100% { opacity: 0.25; transform: translateX(-10%); }
50% { opacity: 0.7; transform: translateX(10%); }
}
/* ───────────────── Chip pulses (install) ───────────────── */
.chip-pulses {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 3;
}
.chip-pulse {
position: absolute;
width: 4px; height: 4px;
margin: -2px 0 0 -2px;
border-radius: 50%;
background: var(--orange);
box-shadow: 0 0 6px rgba(255, 138, 31, 0.9), 0 0 14px rgba(255, 138, 31, 0.45);
animation: chip-blink ease-in-out infinite;
}
@keyframes chip-blink {
0%, 100% { opacity: 0.95; transform: scale(1); }
50% { opacity: 0.25; transform: scale(0.7); }
}
.chip-glow {
position: absolute;
/* main chip is ~upper-right of image */
left: 78%; top: 38%;
width: 280px; height: 280px;
margin: -140px 0 0 -140px;
border-radius: 50%;
background: radial-gradient(circle, rgba(33, 234, 255, 0.10) 0%, transparent 65%);
filter: blur(10px);
animation: chip-breath 6s ease-in-out infinite;
}
@keyframes chip-breath {
0%, 100% { opacity: 0.6; transform: scale(1); }
50% { opacity: 1; transform: scale(1.15); }
}
/* ───────────────── Content blocks ───────────────── */
.content {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 10;
}
.content > * { pointer-events: auto; }
.content-product {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 6vw;
max-width: none;
width: min(1680px, 92vw);
}
.content-install {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 8vw;
max-width: 780px;
}
.tag {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 13px;
letter-spacing: 0.28em;
color: var(--cyan);
margin-bottom: 32px;
padding: 8px 16px;
border: 1px solid rgba(33, 234, 255, 0.25);
background: rgba(33, 234, 255, 0.03);
width: fit-content;
text-shadow: 0 0 8px rgba(33, 234, 255, 0.4);
}
.tag-dot {
width: 6px; height: 6px;
background: var(--cyan);
box-shadow: 0 0 6px var(--cyan);
animation: tag-blink 1.6s steps(2) infinite;
}
@keyframes tag-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
.headline {
font-family: "VT323", "IBM Plex Mono", monospace;
font-size: clamp(36px, 4vw, 64px);
white-space: nowrap;
line-height: 1.05;
letter-spacing: 0.01em;
color: var(--cyan-soft);
text-shadow:
0 0 12px rgba(33, 234, 255, 0.6),
0 0 32px rgba(33, 234, 255, 0.3);
margin: 0 0 28px;
font-weight: 400;
}
.headline .bracket {
color: var(--cyan-muted);
text-shadow: 0 0 6px rgba(43, 126, 138, 0.5);
font-size: 0.85em;
margin: 0 0.2em;
}
.headline .hl {
color: #fff;
text-shadow:
0 0 12px rgba(33, 234, 255, 0.85),
0 0 30px rgba(33, 234, 255, 0.45);
}
.body, .subheadline {
color: var(--ink-mid);
font-size: clamp(17px, 1.4vw, 22px);
line-height: 1.55;
letter-spacing: 0.04em;
max-width: none;
white-space: nowrap;
margin: 0;
}
.subheadline {
margin-bottom: 32px;
letter-spacing: 0.18em;
font-size: 16px;
text-transform: uppercase;
}
/* ───────────────── Command box ───────────────── */
.cmd-box {
position: relative;
margin: 4px 0 28px;
border: 1px solid rgba(33, 234, 255, 0.22);
background: rgba(2, 7, 13, 0.72);
backdrop-filter: blur(2px);
cursor: pointer;
font-family: "IBM Plex Mono", monospace;
font-size: 17px;
max-width: 720px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
box-shadow:
0 0 0 1px rgba(33, 234, 255, 0.05),
0 0 24px rgba(33, 234, 255, 0.08),
inset 0 0 40px rgba(33, 234, 255, 0.03);
}
.cmd-box:hover {
border-color: rgba(33, 234, 255, 0.5);
box-shadow:
0 0 0 1px rgba(33, 234, 255, 0.15),
0 0 36px rgba(33, 234, 255, 0.18);
}
.cmd-bar {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-bottom: 1px solid rgba(33, 234, 255, 0.15);
background: rgba(10, 58, 68, 0.25);
font-size: 11px;
letter-spacing: 0.18em;
color: var(--cyan-muted);
}
.cmd-led {
width: 8px; height: 8px;
background: var(--cyan-deep);
border: 1px solid rgba(33, 234, 255, 0.3);
}
.cmd-led.led-amber { background: rgba(255, 138, 31, 0.4); border-color: rgba(255, 138, 31, 0.5); }
.cmd-led.led-cyan { background: rgba(33, 234, 255, 0.4); border-color: rgba(33, 234, 255, 0.5); }
.cmd-title { font-size: 11px; margin-left: 4px; opacity: 0.8; }
.cmd-bar-hint {
margin-left: auto;
font-size: 11px;
letter-spacing: 0.24em;
color: var(--cyan-muted);
transition: color 0.2s ease;
}
.cmd-box.copied .cmd-bar-hint { color: var(--cyan); }
.cmd-body {
display: flex;
align-items: center;
gap: 10px;
padding: 18px 18px 20px;
white-space: nowrap;
overflow-x: auto;
color: var(--cyan-soft);
text-shadow: 0 0 8px rgba(33, 234, 255, 0.4);
}
.cmd-prompt { color: var(--orange-soft); }
.cmd-cursor {
display: inline-block;
width: 8px;
height: 16px;
background: var(--cyan);
box-shadow: 0 0 8px rgba(33, 234, 255, 0.7);
animation: cursor-blink 1.05s steps(1) infinite;
}
@keyframes cursor-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
.cmd-box.copied { border-color: rgba(33, 234, 255, 0.55); }
.docs-link {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--cyan);
font-size: 15px;
letter-spacing: 0.16em;
text-transform: uppercase;
border-bottom: 1px solid rgba(33, 234, 255, 0.25);
padding-bottom: 4px;
width: fit-content;
text-shadow: 0 0 8px rgba(33, 234, 255, 0.35);
transition: color 0.2s ease, border-color 0.2s ease;
}
.docs-link:hover {
color: var(--cyan-soft);
border-color: var(--cyan);
}
.docs-link .arrow {
transition: transform 0.25s ease;
}
.docs-link:hover .arrow { transform: translateX(4px); }
/* ───────────────── HUD ───────────────── */
.hud {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 100;
}
.hud-brand {
position: absolute;
top: 24px;
left: 28px;
font-size: 11px;
letter-spacing: 0.4em;
color: var(--cyan);
text-shadow: 0 0 8px rgba(33, 234, 255, 0.5);
}
.hud-meta {
position: absolute;
top: 24px;
right: 28px;
display: flex;
align-items: center;
gap: 12px;
font-size: 11px;
letter-spacing: 0.28em;
color: var(--cyan-muted);
}
.hud-meta-line {
width: 36px; height: 1px;
background: linear-gradient(90deg, transparent, var(--cyan-muted));
}
.hud-nav {
position: absolute;
right: 28px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 18px;
}
.hud-step {
display: flex;
align-items: center;
gap: 10px;
font-size: 10px;
letter-spacing: 0.24em;
color: var(--ink-low);
transition: color 0.4s ease;
}
.hud-step-dot {
width: 6px; height: 6px;
background: transparent;
border: 1px solid currentColor;
border-radius: 50%;
}
.hud-step.is-past { color: var(--cyan-muted); }
.hud-step.is-active { color: var(--cyan); text-shadow: 0 0 8px rgba(33, 234, 255, 0.5); }
.hud-step.is-active .hud-step-dot {
background: var(--cyan);
box-shadow: 0 0 8px var(--cyan);
}
/* Scroll cue */
.scroll-cue {
position: absolute;
bottom: 36px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
z-index: 11;
opacity: 0.7;
}
.scroll-cue-line {
width: 1px;
height: 36px;
background: linear-gradient(180deg, transparent, var(--cyan));
animation: cue-drop 2.4s ease-in-out infinite;
}
@keyframes cue-drop {
0%, 100% { transform: translateY(0); opacity: 0.4; }
50% { transform: translateY(8px); opacity: 1; }
}
.scroll-cue-label {
font-size: 9px;
letter-spacing: 0.4em;
color: var(--cyan-muted);
}
/* ───────────────── Mobile recompose ───────────────── */
@media (max-width: 760px) {
/* Shorter scroll on phones — 700vh of swiping is exhausting.
The GSAP timeline scrubs by progress (0..1) so the choreography
still plays out, just over a shorter total scroll distance. */
.scroll-track { height: 480vh; }
/* Perf: kill the heaviest paint work on mobile GPUs.
- SVG feTurbulence in .grain is the single most expensive thing
on the page; dropping it gives the biggest scroll-jank win.
- mix-blend-mode and backdrop-filter both force off-screen
compositing passes per frame. */
.grain { display: none; }
.haze, .haze-deep, .smoke-puff { mix-blend-mode: normal; }
.cmd-box { backdrop-filter: none; }
/* The blurred arm-glow and chip-glow are decorative — drop the
blurs that force a re-rasterization on every scrub frame. */
.arm-pivot { display: none; }
.conveyor-pulse { filter: none; }
.chip-glow { filter: none; }
.logo-wrap::before { filter: none; }
/* Re-aim the camera so the meaningful part of each landscape image
stays roughly in frame on a portrait viewport. */
.bg-wrap { transform-origin: 70% 60% !important; }
.scene-product .bg-wrap { transform-origin: 55% 70% !important; }
.scene-install .bg-wrap { transform-origin: 70% 55% !important; }
/* Use the smaller WebP variants — ~52KB total instead of ~190KB. */
.scene-hero .bg-image { background-image: url("assets/exterior-factory-mobile.webp"); }
.scene-product .bg-image { background-image: url("assets/factory-interior-mobile.webp"); }
.scene-install .bg-image { background-image: url("assets/pcb-chip-mobile.webp"); }
/* Hero: center the (now responsively sized) logo. */
.content-hero {
align-items: center;
justify-content: center;
padding: 0 6vw;
}
.content-product, .content-install {
padding: 14vh 7vw 14vh;
width: 100%;
max-width: 100%;
justify-content: flex-start;
}
.tag {
font-size: 11px;
letter-spacing: 0.22em;
margin-bottom: 22px;
padding: 7px 12px;
gap: 8px;
}
.headline {
font-size: clamp(30px, 8.5vw, 52px);
white-space: normal;
line-height: 1.1;
margin-bottom: 22px;
}
.body, .subheadline {
white-space: normal;
max-width: 100%;
}
.body { font-size: clamp(15px, 4vw, 18px); line-height: 1.5; }
.subheadline { font-size: 13px; letter-spacing: 0.16em; margin-bottom: 22px; }
.cmd-box { font-size: 14px; max-width: 100%; }
.cmd-bar { padding: 7px 10px; font-size: 10px; letter-spacing: 0.14em; }
.cmd-title { display: none; }
.cmd-bar-hint { font-size: 9px; letter-spacing: 0.18em; }
.cmd-body {
font-size: 13px;
padding: 14px 12px 16px;
/* Allow long install command to wrap on tiny screens instead of scrolling. */
white-space: normal;
word-break: break-all;
overflow-x: visible;
}
.docs-link { font-size: 13px; letter-spacing: 0.14em; }
/* HUD: compact, less metadata, no labels. */
.hud-brand { top: 16px; left: 14px; font-size: 10px; letter-spacing: 0.32em; }
.hud-meta {
top: 16px; right: 14px;
gap: 8px;
font-size: 10px;
letter-spacing: 0.22em;
}
.hud-meta-line { width: 16px; }
.hud-nav { right: 10px; gap: 14px; }
.hud-step-label { display: none; }
.scroll-cue { bottom: 18px; }
.scroll-cue-line { height: 28px; }
.chip-glow { width: 180px; height: 180px; margin: -90px 0 0 -90px; }
.arm-left, .arm-right { width: 40px; height: 40px; margin: -20px 0 0 -20px; }
}
/* Narrow phones — tighten further */
@media (max-width: 480px) {
.content-product, .content-install { padding: 12vh 6vw 12vh; }
.headline { font-size: clamp(26px, 9vw, 40px); margin-bottom: 18px; }
.body { font-size: 14px; }
.tag { font-size: 10px; padding: 6px 10px; margin-bottom: 18px; }
.cmd-body { font-size: 12px; padding: 12px 10px 14px; }
.docs-link { font-size: 12px; }
.hud-meta span:not(.hud-meta-line) { /* keep depth readout but shrink */ font-size: 9px; }
.scroll-cue-label { font-size: 8px; }
}
/* Short / landscape phones — pull content tighter so HUD doesn't collide. */
@media (max-height: 520px) and (max-width: 900px) {
.content-product, .content-install { padding: 9vh 6vw; }
.headline { font-size: clamp(24px, 5vw, 36px); margin-bottom: 14px; }
.scroll-cue { display: none; }
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.px.on,
.ember,
.spark,
.spark-core,
.smoke-puff,
.conveyor-pulse,
.chip-pulse,
.chip-glow,
.ghost-r, .ghost-c,
.logo-wrap,
.logo-scanlines,
.grain,
.arm-pivot,
.scroll-cue-line {
animation: none !important;
}
.smoke-puff { opacity: 0.3; }
}