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>
This commit is contained in:
108
components/GhibliBackground.tsx
Normal file
108
components/GhibliBackground.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
// 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user