From 8ea175fa78b015c887835aead3eb1b6d9b12747a Mon Sep 17 00:00:00 2001 From: Knowit Date: Sun, 3 May 2026 01:41:50 +0800 Subject: [PATCH] Migrate live changes from facere-website and add deploy configs Bring in the newer app.jsx/styles.css/facere.html that were edited directly on the deployed copy, rename Facere.html -> facere.html to match the nginx index, and check in the docker-compose + nginx.conf that drives the container. Co-Authored-By: Claude Opus 4.7 (1M context) --- app.jsx | 74 +++++++++-------- deploy/docker-compose.yml | 10 +++ deploy/nginx.conf | 39 +++++++++ Facere.html => facere.html | 4 +- styles.css | 160 +++++++++++++++++++++---------------- 5 files changed, 182 insertions(+), 105 deletions(-) create mode 100644 deploy/docker-compose.yml create mode 100644 deploy/nginx.conf rename Facere.html => facere.html (92%) diff --git a/app.jsx b/app.jsx index fbbb907..92b8f90 100644 --- a/app.jsx +++ b/app.jsx @@ -151,7 +151,7 @@ function PixelLogoStatic({ word, scale, gap, color }) { Each puff is a chunky, low-res-feeling translucent disc that drifts up + fades. */ function SmokeLayer({ chimneys, scale = 1, intensity = 1 }) { // Each chimney emits N puffs on staggered phases. - const PUFFS_PER = 5; + const PUFFS_PER = 3; const puffs = []; chimneys.forEach((ch, ci) => { for (let p = 0; p < PUFFS_PER; p++) { @@ -312,27 +312,29 @@ function HeroSection({ bgRef, contentRef }) { // Smaller stacks at 71%, 80%, 85.5%. // Image 1: assets/exterior-factory.png const chimneys = [ - { x: 77.5, y: 51.0, size: 22, dur: 7.2, drift: -0.3 }, - { x: 80.5, y: 49.5, size: 32, dur: 8.0, drift: -0.4 }, - { x: 84.5, y: 50.4, size: 30, dur: 7.6, drift: 0.2 }, - { x: 86.5, y: 51.0, size: 22, dur: 7.0, drift: 0.3 }, - { x: 89.5, y: 49.8, size: 32, dur: 8.2, drift: -0.2 }, - { x: 92.0, y: 51.4, size: 22, dur: 7.4, drift: 0.4 }, + { x: 71.5, y: 51.0, size: 22, dur: 7.2, drift: -0.3 }, + { x: 74.5, y: 49.5, size: 32, dur: 8.0, drift: -0.4 }, + { x: 78.5, y: 50.4, size: 30, dur: 7.6, drift: 0.2 }, + { x: 80.5, y: 51.0, size: 22, dur: 7.0, drift: 0.3 }, + { x: 83.5, y: 49.8, size: 32, dur: 8.2, drift: -0.2 }, + { x: 86.0, y: 51.4, size: 22, dur: 7.4, drift: 0.4 }, ]; return (
- {/* Image 1 — exterior factory */} -
- - +
+ {/* Image 1 — exterior factory */} +
+ + +
@@ -357,17 +359,19 @@ function ProductSection({ bgRef, contentRef }) { return (
- {/* Image 2 — factory interior / assembly line */} -
- {/* Robotic arm idle motion: anchored to where the arms sit in the image */} -
-
- - {/* Conveyor pulse strip */} -
+
+ {/* Image 2 — factory interior / assembly line */} +
+ {/* Robotic arm idle motion: anchored to where the arms sit in the image */} +
+
+ + {/* Conveyor pulse strip */} +
+
@@ -409,12 +413,14 @@ function InstallSection({ bgRef, contentRef }) { return (
- {/* Image 3 — PCB / chip */} -
- +
+ {/* Image 3 — PCB / chip */} +
+ +
diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..1e000da --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,10 @@ +services: + facere-web: + image: nginx:1.27-alpine + container_name: facere-web + restart: unless-stopped + ports: + - "9527:80" + volumes: + - ../:/usr/share/nginx/html:ro + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..e8f46bb --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,39 @@ +server { + listen 80; + server_name web.facere.cc _; + + root /usr/share/nginx/html; + index facere.html index.html; + + charset utf-8; + + location = / { + try_files /facere.html =404; + } + + location / { + try_files $uri $uri/ /facere.html; + } + + # CSS/JS/JSX: short cache so future deploys are picked up promptly + location ~* \.(?:css|js|jsx)$ { + expires 5m; + add_header Cache-Control "public, max-age=300, must-revalidate"; + types { text/css css; application/javascript js; application/javascript jsx; } + try_files $uri =404; + } + + # Static media: long cache (filenames are stable / change when content changes) + location ~* \.(?:png|jpe?g|gif|webp|svg|mp4|webm|woff2?|ttf|otf|ico)$ { + expires 7d; + add_header Cache-Control "public, max-age=604800"; + try_files $uri =404; + } + + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + gzip_min_length 1024; + + access_log /var/log/nginx/facere.access.log; + error_log /var/log/nginx/facere.error.log; +} diff --git a/Facere.html b/facere.html similarity index 92% rename from Facere.html rename to facere.html index 5d95743..9b81e78 100644 --- a/Facere.html +++ b/facere.html @@ -9,7 +9,7 @@ - + @@ -28,6 +28,6 @@ - + diff --git a/styles.css b/styles.css index e4174ae..7ba69be 100644 --- a/styles.css +++ b/styles.css @@ -41,7 +41,14 @@ a { color: inherit; text-decoration: none; } background: var(--bg); } .scene-stack { position: absolute; inset: 0; } -.scene-slot { 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; @@ -58,14 +65,29 @@ a { color: inherit; text-decoration: none; } 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: cover; + background-size: 100% 100%; background-position: center center; background-repeat: no-repeat; /* Crisp pixel-art feel without softening the source */ @@ -159,63 +181,63 @@ a { color: inherit; text-decoration: none; } mix-blend-mode: screen; opacity: 0; } -.ghost-r { animation: glitch-r 3.0s steps(1) infinite; } -.ghost-c { animation: glitch-c 3.0s steps(1) infinite; } +.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(40px, 0); clip-path: inset(5% 0 78% 0); } - 9% { opacity: 0.7; transform: translate(-20px, 0); clip-path: inset(15% 0 70% 0); } + 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(55px, 0); clip-path: inset(28% 0 58% 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(-35px, 0); clip-path: inset(48% 0 38% 0); } - 25% { opacity: 0.5; transform: translate(15px, 0); clip-path: inset(52% 0 36% 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(25px, 0); clip-path: inset(62% 0 24% 0); } + 36% { opacity: 0.65; transform: translate(5px, 0); clip-path: inset(62% 0 24% 0); } 37% { opacity: 0; } - 44% { opacity: 0.95; transform: translate(-45px, 0); clip-path: inset(72% 0 14% 0); } - 45% { opacity: 0.6; transform: translate(20px, 0); clip-path: inset(78% 0 8% 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(10px, 0); clip-path: inset(18% 0 70% 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(-50px, 0); clip-path: inset(35% 0 50% 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(30px, 0); clip-path: inset(58% 0 30% 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(35px, 0); clip-path: none; } + 82% { opacity: 0.85; transform: translate(7px, 0); clip-path: none; } 83% { opacity: 0; } - 90% { opacity: 0.6; transform: translate(-15px, 0); clip-path: inset(68% 0 22% 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(-40px, 0); clip-path: inset(5% 0 78% 0); } - 9% { opacity: 0.65; transform: translate(20px, 0); clip-path: inset(15% 0 70% 0); } + 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(-55px, 0); clip-path: inset(28% 0 58% 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(35px, 0); clip-path: inset(48% 0 38% 0); } - 25% { opacity: 0.45; transform: translate(-15px, 0); clip-path: inset(52% 0 36% 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(-25px, 0); clip-path: inset(62% 0 24% 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(45px, 0); clip-path: inset(72% 0 14% 0); } - 45% { opacity: 0.55; transform: translate(-20px, 0); clip-path: inset(78% 0 8% 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(-10px, 0); clip-path: inset(18% 0 70% 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(50px, 0); clip-path: inset(35% 0 50% 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(-30px, 0); clip-path: inset(58% 0 30% 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(-35px, 0); clip-path: none; } + 82% { opacity: 0.8; transform: translate(-7px, 0); clip-path: none; } 83% { opacity: 0; } - 90% { opacity: 0.55; transform: translate(15px, 0); clip-path: inset(68% 0 22% 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; } } @@ -230,7 +252,7 @@ a { color: inherit; text-decoration: none; } } .logo-wrap { position: relative; - animation: logo-jitter 3.0s steps(1) infinite, logo-flicker 2.4s ease-in-out infinite, logo-dropout 7s steps(1) infinite; + 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 { @@ -255,28 +277,28 @@ a { color: inherit; text-decoration: none; } /* Logo body — many independent band shears, frequent bursts. */ @keyframes logo-jitter { 0%, 6%, 100% { transform: translate(0, 0); clip-path: none; } - 8% { transform: translate(25px, 0); clip-path: inset(5% 0 78% 0); } - 9% { transform: translate(-15px, 0); clip-path: inset(15% 0 70% 0); } + 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(-30px, 0); clip-path: inset(28% 0 58% 0); } - 19% { transform: translate(10px, 0); clip-path: none; } - 24% { transform: translate(20px, 0); clip-path: inset(48% 0 38% 0); } - 25% { transform: translate(-10px, 0); clip-path: inset(52% 0 36% 0); } + 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(-15px, 0); clip-path: inset(62% 0 24% 0); } + 36% { transform: translate(-3px, 0); clip-path: inset(62% 0 24% 0); } 37% { transform: translate(0, 0); clip-path: none; } - 44% { transform: translate(35px, 0); clip-path: inset(72% 0 14% 0); } - 45% { transform: translate(-20px, 0); clip-path: inset(78% 0 8% 0); } + 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(-10px, 0); clip-path: inset(18% 0 70% 0); } + 56% { transform: translate(-2px, 0); clip-path: inset(18% 0 70% 0); } 57% { transform: translate(0, 0); clip-path: none; } - 64% { transform: translate(40px, 0); clip-path: inset(35% 0 50% 0); } - 65% { transform: translate(-15px, 0); clip-path: none; } - 72% { transform: translate(-25px, 0); clip-path: inset(58% 0 30% 0); } - 73% { transform: translate(10px, 0); clip-path: none; } - 82% { transform: translate(-20px, 0) skewX(-1deg); } + 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(15px, 0); clip-path: inset(68% 0 22% 0); } + 90% { transform: translate(3px, 0); clip-path: inset(68% 0 22% 0); } 91% { transform: translate(0, 0); clip-path: none; } } @@ -317,7 +339,7 @@ a { color: inherit; text-decoration: none; } 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 3.0s steps(1) infinite; + animation: pixel-break 12s steps(1) infinite; } @keyframes pixel-break { 0%, 7% { opacity: 0; } @@ -365,29 +387,29 @@ a { color: inherit; text-decoration: none; } rgba(33,234,255,0.10) 88%, transparent 100%); opacity: 0; - animation: scan-tear 3.0s steps(1) infinite; + 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(-60px); } + 8% { opacity: 0.9; top: 8%; transform: translateX(-12px); } 10% { opacity: 0; } - 18% { opacity: 0.85; top: 26%; transform: translateX(70px); } + 18% { opacity: 0.85; top: 26%; transform: translateX(14px); } 20% { opacity: 0; } - 24% { opacity: 0.95; top: 44%; transform: translateX(-50px); } + 24% { opacity: 0.95; top: 44%; transform: translateX(-10px); } 26% { opacity: 0; } - 36% { opacity: 0.7; top: 60%; transform: translateX(40px); } + 36% { opacity: 0.7; top: 60%; transform: translateX(8px); } 37% { opacity: 0; } - 44% { opacity: 0.95; top: 74%; transform: translateX(-70px); } + 44% { opacity: 0.95; top: 74%; transform: translateX(-14px); } 46% { opacity: 0; } - 56% { opacity: 0.6; top: 16%; transform: translateX(50px); } + 56% { opacity: 0.6; top: 16%; transform: translateX(10px); } 57% { opacity: 0; } - 64% { opacity: 0.9; top: 38%; transform: translateX(-40px); } + 64% { opacity: 0.9; top: 38%; transform: translateX(-8px); } 65% { opacity: 0; } - 72% { opacity: 0.7; top: 56%; transform: translateX(60px); } + 72% { opacity: 0.7; top: 56%; transform: translateX(12px); } 73% { opacity: 0; } - 82% { opacity: 0.95; top: 70%; transform: translateX(-50px); } + 82% { opacity: 0.95; top: 70%; transform: translateX(-10px); } 83% { opacity: 0; } - 90% { opacity: 0.6; top: 88%; transform: translateX(30px); } + 90% { opacity: 0.6; top: 88%; transform: translateX(6px); } 91%, 100% { opacity: 0; } } .logo-scanlines { @@ -618,18 +640,18 @@ a { color: inherit; text-decoration: none; } flex-direction: column; justify-content: center; padding: 0 8vw; - max-width: 640px; + max-width: 780px; } .tag { display: inline-flex; align-items: center; gap: 10px; - font-size: 11px; + font-size: 13px; letter-spacing: 0.28em; color: var(--cyan); margin-bottom: 32px; - padding: 7px 14px; + padding: 8px 16px; border: 1px solid rgba(33, 234, 255, 0.25); background: rgba(33, 234, 255, 0.03); width: fit-content; @@ -648,7 +670,7 @@ a { color: inherit; text-decoration: none; } .headline { font-family: "VT323", "IBM Plex Mono", monospace; - font-size: clamp(20px, 2vw, 30px); + font-size: clamp(36px, 4vw, 64px); white-space: nowrap; line-height: 1.05; letter-spacing: 0.01em; @@ -656,7 +678,7 @@ a { color: inherit; text-decoration: none; } text-shadow: 0 0 12px rgba(33, 234, 255, 0.6), 0 0 32px rgba(33, 234, 255, 0.3); - margin: 0 0 24px; + margin: 0 0 28px; font-weight: 400; } .headline .bracket { @@ -674,7 +696,7 @@ a { color: inherit; text-decoration: none; } .body, .subheadline { color: var(--ink-mid); - font-size: clamp(13px, 1.05vw, 16px); + font-size: clamp(17px, 1.4vw, 22px); line-height: 1.55; letter-spacing: 0.04em; max-width: none; @@ -684,7 +706,7 @@ a { color: inherit; text-decoration: none; } .subheadline { margin-bottom: 32px; letter-spacing: 0.18em; - font-size: 13px; + font-size: 16px; text-transform: uppercase; } @@ -697,8 +719,8 @@ a { color: inherit; text-decoration: none; } backdrop-filter: blur(2px); cursor: pointer; font-family: "IBM Plex Mono", monospace; - font-size: 14px; - max-width: 560px; + 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), @@ -772,7 +794,7 @@ a { color: inherit; text-decoration: none; } align-items: center; gap: 8px; color: var(--cyan); - font-size: 13px; + font-size: 15px; letter-spacing: 0.16em; text-transform: uppercase; border-bottom: 1px solid rgba(33, 234, 255, 0.25);