Files
Claude (Beletage) c0bc2c2f04 Redesign: new logo, fonts, toon-shaded 3D, scroll animations
Visual overhaul addressing render glitches and adding wow factor.

Type:
- Display: Fraunces -> Instrument Serif (more elegant, italic-forward)
- Body:    Inter    -> Geist (tighter, more contemporary)
- Mono:    JetBrains -> Geist Mono

Logo:
- New SVG mark — pairs a gabled vernacular silhouette with a glass-grid
  contemporary tower, divided by a ground line. Communicates "2D3D" through
  architectural language rather than typography.
- Component supports mark / full / inverse variants
- Favicon + apple-touch-icon updated to match
- OG image regenerated with new palette and 3 representative house silhouettes

3D models (houses.ts):
- Switched to MeshToonMaterial with a 3-step gradient LUT — flat designer-toy
  look that's forgiving of low-poly approximations
- Reworked geometry across all six typologies for consistent scale and clean
  composition (real arches via ExtrudeGeometry on cula loggia, proper sasesc
  roof shapes via extruded slope, contemporary slats sized to volume)
- Per-house camera framing
- 2D->3D intro animation when scene first becomes visible — house extrudes
  from a flat plane (on-brand for the 2d3d.ro concept)
- Subtle breathing y-translation while idle
- Hover speeds up the spin smoothly

Animations:
- Word-split + staggered reveal for hero headline
- IntersectionObserver-based reveal-on-scroll for [data-reveal]/[data-reveal-blur]
- Animated number counters (Romanian locale)
- Magnetic hover on CTAs and brand mark
- Cursor-tracking radial glow in hero
- Section dividers with blueprint dimension-line aesthetic
- Marquee strip listing typology names in intro
- Glow-ring (animated conic gradient) on primary CTAs

Palette refined — slightly less saturated terra/wood/sky-mist for a more
sophisticated tone.

All animations respect prefers-reduced-motion. Off-screen 3D scenes still
paused via the prior IntersectionObserver fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 10:54:31 +03:00

117 lines
5.7 KiB
JavaScript

import sharp from 'sharp';
import { mkdir } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const outDir = join(__dirname, '..', 'public');
await mkdir(outDir, { recursive: true });
const W = 1200, H = 630;
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#190b06"/>
<stop offset="1" stop-color="#2e160d"/>
</linearGradient>
<radialGradient id="sun" cx="0.5" cy="0.35" r="0.7">
<stop offset="0" stop-color="#f0c39b" stop-opacity="0.45"/>
<stop offset="1" stop-color="#f0c39b" stop-opacity="0"/>
</radialGradient>
<linearGradient id="shimmer" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#7d361c"/>
<stop offset="0.5" stop-color="#e69d6a"/>
<stop offset="1" stop-color="#7d361c"/>
</linearGradient>
</defs>
<rect width="${W}" height="${H}" fill="url(#bg)"/>
<rect width="${W}" height="${H}" fill="url(#sun)"/>
<!-- Subtle grid -->
<g stroke="#fbf8f1" stroke-opacity="0.04">
${Array.from({ length: 20 }).map((_, i) => `<line x1="${i * 60}" y1="0" x2="${i * 60}" y2="${H}"/>`).join('')}
${Array.from({ length: 11 }).map((_, i) => `<line x1="0" y1="${i * 60}" x2="${W}" y2="${i * 60}"/>`).join('')}
</g>
<!-- 3 stylized houses across the ground -->
<g>
<!-- Maramures -->
<g transform="translate(140, 460)">
<rect x="-55" y="-10" width="110" height="60" fill="#7a4a23"/>
<rect x="-55" y="50" width="110" height="6" fill="#1f150c"/>
<polygon points="-72,-10 0,-180 72,-10" fill="#2a160b"/>
<polygon points="-2,-200 2,-200 0,-180" fill="#2a160b"/>
<line x1="-1" y1="-200" x2="-1" y2="-218" stroke="#1f150c" stroke-width="2"/>
<line x1="-9" y1="-210" x2="9" y2="-210" stroke="#1f150c" stroke-width="2"/>
</g>
<!-- Cula -->
<g transform="translate(420, 460)">
<rect x="-55" y="-10" width="110" height="60" fill="#c8b380"/>
<rect x="-60" y="-90" width="120" height="80" fill="#e7d6b3"/>
<polygon points="-66,-90 0,-150 66,-90" fill="#5a3220"/>
<g fill="#1a0d05">
<rect x="-44" y="-50" width="20" height="22"/>
<rect x="-10" y="-50" width="20" height="22"/>
<rect x="24" y="-50" width="20" height="22"/>
</g>
</g>
<!-- Contemporary -->
<g transform="translate(720, 460)">
<rect x="-90" y="-10" width="180" height="60" fill="#a8a29a"/>
<rect x="-90" y="50" width="180" height="6" fill="#1f150c"/>
<rect x="-85" y="-70" width="115" height="60" fill="#f3ecdc"/>
<rect x="38" y="-50" width="50" height="40" fill="#b58f5d"/>
<rect x="-78" y="-58" width="100" height="40" fill="#9ec5d9" opacity="0.7"/>
<polygon points="-90,-70 30,-90 30,-70" fill="#2e160d"/>
<g fill="#0d0805" opacity="0.85">
<rect x="-78" y="-100" width="20" height="3" transform="rotate(-3, -78, -100)"/>
<rect x="-50" y="-101" width="20" height="3" transform="rotate(-3, -50, -101)"/>
<rect x="-22" y="-102" width="20" height="3" transform="rotate(-3, -22, -102)"/>
</g>
</g>
</g>
<!-- Title — Instrument Serif look (use serif fallback) -->
<g transform="translate(80, 130)">
<text font-family="'Instrument Serif', Georgia, serif" font-size="24" fill="#e69d6a" letter-spacing="6">PATRIMONIU CONSTRUIT · 2026</text>
<text y="100" font-family="'Instrument Serif', Georgia, serif" font-size="92" font-weight="400" fill="#fbf8f1">Arhitectura</text>
<text y="200" font-family="'Instrument Serif', Georgia, serif" font-size="92" font-weight="400" fill="url(#shimmer)" font-style="italic">României</text>
<text y="270" font-family="'Instrument Serif', Georgia, serif" font-size="36" fill="#fbf8f1" opacity="0.8" font-style="italic">de la cula la zgârie-nori</text>
</g>
<!-- Bottom strip -->
<g transform="translate(80, ${H - 60})">
<text font-family="'Geist', system-ui, sans-serif" font-weight="500" font-size="20" fill="#f9e3d0" opacity="0.85">2d3d.ro</text>
<text x="${W - 200}" font-family="'Geist', system-ui, sans-serif" font-size="20" fill="#f9e3d0" opacity="0.7">un proiect Beletage</text>
</g>
</svg>
`;
await sharp(Buffer.from(svg)).png({ quality: 92 }).toFile(join(outDir, 'og.png'));
console.log('Generated og.png');
// Also apple-touch-icon — matches favicon design
const ati = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180">
<rect width="180" height="180" rx="40" fill="#190b06"/>
<rect x="18" y="142" width="144" height="6" fill="#2e160d"/>
<path d="M28 142 L28 84 L62 50 L96 84 L96 142 Z" fill="#d97942"/>
<path d="M62 50 L62 110 L96 142 M62 110 L28 142" stroke="#2e160d" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/>
<rect x="50" y="110" width="24" height="32" fill="#2e160d"/>
<rect x="56" y="116" width="6" height="6" fill="#fae8da"/>
<rect x="64" y="116" width="6" height="6" fill="#fae8da"/>
<rect x="106" y="62" width="50" height="80" fill="#fae8da"/>
<path d="M106 62 L131 40 L156 62" fill="#2e160d"/>
<g fill="#2e160d">
<rect x="114" y="74" width="8" height="8"/><rect x="127" y="74" width="8" height="8"/><rect x="140" y="74" width="8" height="8"/>
<rect x="114" y="92" width="8" height="8"/><rect x="127" y="92" width="8" height="8"/><rect x="140" y="92" width="8" height="8"/>
<rect x="114" y="110" width="8" height="8"/><rect x="127" y="110" width="8" height="8"/><rect x="140" y="110" width="8" height="8"/>
</g>
</svg>
`;
await sharp(Buffer.from(ati)).resize(180, 180).png().toFile(join(outDir, 'apple-touch-icon.png'));
console.log('Generated apple-touch-icon.png');