Files
mortgagefi-helper/components/GhibliBackground.tsx
Siavash Sameni 6ae581ab2e feat(ui): Ghibli/Miyazaki reskin + Obsidian docs vault + project audit
UI: warm daylight design system (Tailwind v4 @theme palette, gh-* component
classes, watercolor grain, Zen Maru Gothic + Klee One fonts), animated SSR-safe
GhibliBackground (drifting clouds, meadow hills, soot sprites), and a full reskin
of navbar, connect button, dapp page, loan cards, settings modal, and readme.
Fixes the bg-white-on-dark loan-card inconsistency. Web3/business logic untouched.

Docs: converted docs/ into an Obsidian vault (frontmatter, [[wikilinks]],
callouts, Home MOC, folders Architecture/Operations/Audits) and added a
full-project audit note (Project Audit 2026-06). Redacted a real leaked Schedy
key value from the security audit example (rotate it at Schedy).

Also commits the previously-untracked server layer: app/api (cron + tasks routes)
and lib (redis, ssrf-guard, task-store).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 08:13:53 +04:00

109 lines
3.5 KiB
TypeScript

// Purely decorative, server-rendered Ghibli landscape that sits behind all
// content. No JS, no state — just layered CSS-animated SVG/divs. Positions and
// timings are hardcoded so SSR and client markup match (no hydration drift).
const clouds = [
{ top: "8%", scale: 1.0, duration: "70s", delay: "0s", opacity: 0.9 },
{ top: "18%", scale: 0.7, duration: "95s", delay: "-30s", opacity: 0.75 },
{ top: "30%", scale: 1.3, duration: "120s", delay: "-70s", opacity: 0.85 },
{ top: "46%", scale: 0.55, duration: "85s", delay: "-15s", opacity: 0.6 },
];
// Soot sprites (susuwatari) drifting up from the meadow
const sprites = [
{ left: "12%", size: 10, duration: "16s", delay: "0s" },
{ left: "26%", size: 7, duration: "21s", delay: "-6s" },
{ left: "44%", size: 12, duration: "18s", delay: "-11s" },
{ left: "61%", size: 8, duration: "23s", delay: "-3s" },
{ left: "78%", size: 9, duration: "19s", delay: "-14s" },
{ left: "89%", size: 6, duration: "25s", delay: "-9s" },
];
function Cloud({ opacity }: { opacity: number }) {
return (
<svg viewBox="0 0 200 80" width="200" height="80" style={{ opacity }}>
<g fill="#fffdf7">
<ellipse cx="60" cy="50" rx="55" ry="26" />
<ellipse cx="105" cy="40" rx="45" ry="34" />
<ellipse cx="145" cy="52" rx="42" ry="24" />
<ellipse cx="95" cy="58" rx="70" ry="20" />
</g>
</svg>
);
}
export default function GhibliBackground() {
return (
<div aria-hidden className="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
{/* warm sun glow, lower-left */}
<div
className="gh-anim absolute rounded-full"
style={{
left: "-6rem",
top: "-6rem",
width: "26rem",
height: "26rem",
background:
"radial-gradient(circle, rgba(244,210,122,0.55) 0%, rgba(244,210,122,0) 68%)",
animation: "breathe 9s ease-in-out infinite",
}}
/>
{/* drifting clouds */}
{clouds.map((c, i) => (
<div
key={i}
className="gh-anim absolute left-0"
style={{
top: c.top,
transform: `scale(${c.scale})`,
animation: `drift ${c.duration} linear infinite`,
animationDelay: c.delay,
}}
>
<Cloud opacity={c.opacity} />
</div>
))}
{/* layered meadow hills along the bottom */}
<svg
className="absolute bottom-0 left-0 w-full"
viewBox="0 0 1440 320"
preserveAspectRatio="none"
style={{ height: "38vh", minHeight: "220px" }}
>
<path
fill="#9cb87f"
fillOpacity="0.55"
d="M0,224 C240,160 420,272 720,224 C1020,176 1200,272 1440,208 L1440,320 L0,320 Z"
/>
<path
fill="#6f9a5a"
fillOpacity="0.7"
d="M0,272 C260,224 480,304 760,272 C1040,240 1240,304 1440,272 L1440,320 L0,320 Z"
/>
<path
fill="#466a3a"
fillOpacity="0.85"
d="M0,300 C300,276 520,320 820,300 C1120,280 1280,316 1440,300 L1440,320 L0,320 Z"
/>
</svg>
{/* soot sprites rising from the meadow */}
{sprites.map((s, i) => (
<span
key={i}
className="gh-soot gh-anim"
style={{
left: s.left,
width: `${s.size}px`,
height: `${s.size}px`,
animation: `rise ${s.duration} ease-in-out infinite`,
animationDelay: s.delay,
}}
/>
))}
</div>
);
}