Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b513551ca | ||
|
|
57423b1baf | ||
|
|
c41a2e38c9 | ||
|
|
9fd928cce6 | ||
|
|
1e211c1201 | ||
|
|
2fc0641512 |
@@ -5,18 +5,11 @@ import { Container } from "@/components/layout/container";
|
||||
import { HomeHero } from "@/components/layout/home-hero";
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { MagicBentoSection } from "@/components/home/magic-bento-section";
|
||||
import { CapabilitiesSection } from "@/components/home/capabilities-section";
|
||||
import { ContactCtaSection } from "@/components/home/contact-cta-section";
|
||||
import { MarqueeSection } from "@/components/home/marquee-section";
|
||||
import { ProcessSection } from "@/components/home/process-section";
|
||||
import { ProjectsSection } from "@/components/home/projects-section";
|
||||
import type {
|
||||
CapabilitiesSectionCopy,
|
||||
ContactCtaSectionCopy,
|
||||
MarqueeSectionCopy,
|
||||
ProcessSectionCopy,
|
||||
ProjectCardCopy,
|
||||
ProjectsSectionCopy,
|
||||
} from "@/components/home/types";
|
||||
import { buildLocalizedMetadata } from "@/lib/metadata";
|
||||
import { getLocalizedPath, resolveLocale } from "@/lib/locale";
|
||||
@@ -25,10 +18,6 @@ import {
|
||||
getSiteSettings,
|
||||
splitMarqueeRowItems,
|
||||
} from "@/lib/app-config";
|
||||
import {
|
||||
getLocalizedValue,
|
||||
getPublishedPortfolioProjects,
|
||||
} from "@/lib/portfolio";
|
||||
import { type MarqueeRow } from "@/components/layout/stacked-marquee-section";
|
||||
|
||||
type HomePageProps = {
|
||||
@@ -65,10 +54,9 @@ export default async function HomePage({ params }: HomePageProps) {
|
||||
siteSettings.defaultLocale,
|
||||
);
|
||||
|
||||
const [t, marqueeSettings, publishedProjects] = await Promise.all([
|
||||
const [t, marqueeSettings] = await Promise.all([
|
||||
getTranslations({ locale: localeKey, namespace: "homepage" }),
|
||||
getMarqueeSettings(),
|
||||
getPublishedPortfolioProjects(),
|
||||
]);
|
||||
|
||||
const bentoCards = [
|
||||
@@ -112,28 +100,6 @@ export default async function HomePage({ params }: HomePageProps) {
|
||||
description: t("marquee.description"),
|
||||
};
|
||||
|
||||
const projectsCopy: ProjectsSectionCopy = {
|
||||
eyebrow: t("projects.eyebrow"),
|
||||
title: t("projects.title"),
|
||||
description: t("projects.description"),
|
||||
stackLabel: t("projects.stackLabel"),
|
||||
fallbackItems: t.raw("projects.fallbackItems") as ProjectsSectionCopy["fallbackItems"],
|
||||
};
|
||||
|
||||
const capabilitiesCopy: CapabilitiesSectionCopy = {
|
||||
eyebrow: t("capabilities.eyebrow"),
|
||||
title: t("capabilities.title"),
|
||||
description: t("capabilities.description"),
|
||||
items: t.raw("capabilities.items") as CapabilitiesSectionCopy["items"],
|
||||
};
|
||||
|
||||
const processCopy: ProcessSectionCopy = {
|
||||
eyebrow: t("process.eyebrow"),
|
||||
title: t("process.title"),
|
||||
description: t("process.description"),
|
||||
steps: t.raw("process.steps") as ProcessSectionCopy["steps"],
|
||||
};
|
||||
|
||||
const contactCtaCopy: ContactCtaSectionCopy = {
|
||||
eyebrow: t("contactCta.eyebrow"),
|
||||
title: t("contactCta.title"),
|
||||
@@ -157,45 +123,6 @@ export default async function HomePage({ params }: HomePageProps) {
|
||||
|
||||
const portfolioHref = getLocalizedPath(localeKey, "/portfolio", siteSettings.defaultLocale);
|
||||
const contactHref = getLocalizedPath(localeKey, "/contact", siteSettings.defaultLocale);
|
||||
const stackSets = t.raw("projects.stackSets") as string[][];
|
||||
|
||||
const selectedProjects = [
|
||||
...publishedProjects.filter((project) => project.isFeatured),
|
||||
...publishedProjects.filter((project) => !project.isFeatured),
|
||||
].slice(0, 3);
|
||||
|
||||
const projectCards: ProjectCardCopy[] = selectedProjects.map((project, index) => ({
|
||||
title: getLocalizedValue(project.title, localeKey),
|
||||
summary: getLocalizedValue(project.summary, localeKey),
|
||||
stack: stackSets[index] ?? stackSets[stackSets.length - 1] ?? [],
|
||||
href: getLocalizedPath(
|
||||
localeKey,
|
||||
`/portfolio/${project.slug}`,
|
||||
siteSettings.defaultLocale,
|
||||
),
|
||||
cta: t("projects.cta"),
|
||||
meta: [
|
||||
getLocalizedValue(project.category.name, localeKey),
|
||||
getLocalizedValue(project.serviceLabel, localeKey),
|
||||
String(project.projectYear),
|
||||
],
|
||||
featured: index === 0,
|
||||
coverImagePath: project.coverImagePath,
|
||||
}));
|
||||
|
||||
const fallbackProjectCards: ProjectCardCopy[] = projectsCopy.fallbackItems.map((item) => ({
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
stack: item.stack,
|
||||
href: item.href === "/portfolio" ? portfolioHref : item.href === "/contact" ? contactHref : item.href,
|
||||
cta: t("projects.cta"),
|
||||
meta: [t("projects.fallbackMeta")],
|
||||
}));
|
||||
|
||||
const mergedProjectCards = [
|
||||
...projectCards,
|
||||
...fallbackProjectCards.slice(0, Math.max(0, 3 - projectCards.length)),
|
||||
].slice(0, 3);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -226,19 +153,7 @@ export default async function HomePage({ params }: HomePageProps) {
|
||||
<MarqueeSection copy={marqueeCopy} rows={marqueeRows} />
|
||||
</MotionFade>
|
||||
|
||||
<MotionFade delay={0.08}>
|
||||
<ProjectsSection copy={projectsCopy} items={mergedProjectCards} />
|
||||
</MotionFade>
|
||||
|
||||
<MotionFade delay={0.1}>
|
||||
<CapabilitiesSection copy={capabilitiesCopy} />
|
||||
</MotionFade>
|
||||
|
||||
<MotionFade delay={0.12}>
|
||||
<ProcessSection copy={processCopy} />
|
||||
</MotionFade>
|
||||
|
||||
<MotionFade delay={0.14}>
|
||||
<ContactCtaSection copy={contactCtaCopy} contactHref={contactHref} />
|
||||
</MotionFade>
|
||||
</Container>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { StackedMarqueeSection, type MarqueeRow } from "@/components/layout/stacked-marquee-section";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { SectionHeading } from "./section-heading";
|
||||
import type { MarqueeSectionCopy } from "./types";
|
||||
|
||||
@@ -10,18 +9,18 @@ type MarqueeSectionProps = {
|
||||
|
||||
export function MarqueeSection({ copy, rows }: MarqueeSectionProps) {
|
||||
return (
|
||||
<section className="space-y-8 overflow-hidden">
|
||||
<section className="space-y-8">
|
||||
<SectionHeading
|
||||
eyebrow={copy.eyebrow}
|
||||
title={copy.title}
|
||||
description={copy.description}
|
||||
/>
|
||||
|
||||
<AppCard className="overflow-hidden">
|
||||
<div className="rounded-nested border border-border/70 bg-background py-6">
|
||||
<StackedMarqueeSection rows={rows} className="py-0" />
|
||||
</div>
|
||||
</AppCard>
|
||||
{/* Full-bleed band: breaks out of the centered container to span the
|
||||
entire site width instead of sitting inside a card. */}
|
||||
<div className="relative left-1/2 w-screen -translate-x-1/2 overflow-hidden border-y border-border/60 bg-background/40 py-8">
|
||||
<StackedMarqueeSection rows={rows} className="py-0" />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ type FloatingControlsProps = {
|
||||
};
|
||||
|
||||
const pill =
|
||||
"flex h-11 w-11 items-center justify-center rounded-full border border-border/60 bg-background/85 text-foreground/80 shadow-panel backdrop-blur-chrome transition-colors hover:bg-accent hover:text-foreground";
|
||||
"flex h-11 w-11 items-center justify-center rounded-full border border-border/60 bg-background/85 text-foreground/80 backdrop-blur-chrome transition-colors hover:bg-accent hover:text-foreground";
|
||||
|
||||
/** Radius (px) of the arc the items fan out along. */
|
||||
const RADIUS = 92;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
|
||||
import { FloatingControls } from "@/components/layout/floating-controls";
|
||||
import { Dock, DockIcon } from "@/components/ui/dock";
|
||||
@@ -26,15 +28,17 @@ type NavKey = "about" | "portfolio" | "products" | "contact";
|
||||
type NavItem = {
|
||||
key: NavKey;
|
||||
path: string;
|
||||
/** Icon file under /public. Swap these for real macOS app icons anytime. */
|
||||
icon: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
// "Home" is intentionally omitted — the logo (first dock icon) is the home link.
|
||||
const navItems: NavItem[] = [
|
||||
{ key: "about", path: "/about" },
|
||||
{ key: "portfolio", path: "/portfolio" },
|
||||
{ key: "products", path: "/products", disabled: true },
|
||||
{ key: "contact", path: "/contact" },
|
||||
{ key: "about", path: "/about", icon: "/dock/about.svg" },
|
||||
{ key: "portfolio", path: "/portfolio", icon: "/dock/portfolio.svg" },
|
||||
{ key: "products", path: "/products", icon: "/dock/products.svg", disabled: true },
|
||||
{ key: "contact", path: "/contact", icon: "/dock/contact.svg" },
|
||||
];
|
||||
|
||||
function isNavItemActive(currentPath: string, itemPath: string) {
|
||||
@@ -48,78 +52,92 @@ export function SiteDock({ defaultLocale, isSuperAdmin = false }: SiteDockProps)
|
||||
const locale = useLocale();
|
||||
const pathname = usePathname();
|
||||
const t = useTranslations("navigation");
|
||||
const reducedMotion = useReducedMotion();
|
||||
const [bouncing, setBouncing] = useState<string | null>(null);
|
||||
|
||||
const currentPath = stripLocalePrefix(pathname);
|
||||
const homeHref = getLocalizedPath(locale, "/", defaultLocale);
|
||||
|
||||
// macOS-style launch hop on click.
|
||||
const bounceProps = (id: string) =>
|
||||
reducedMotion
|
||||
? {}
|
||||
: {
|
||||
onClick: () => setBouncing(id),
|
||||
animate: bouncing === id ? { y: [0, -18, 0, -6, 0] } : { y: 0 },
|
||||
transition: { duration: 0.5, ease: "easeOut" as const },
|
||||
onAnimationComplete: () => setBouncing((cur) => (cur === id ? null : cur)),
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* ===== Bottom dock (Magic UI) — primary navigation ===== */}
|
||||
{/* ===== Bottom dock — primary navigation ===== */}
|
||||
<div
|
||||
className="pointer-events-none fixed inset-x-0 bottom-0 z-40 flex justify-center px-4"
|
||||
style={{ paddingBottom: "calc(0.9rem + env(safe-area-inset-bottom, 0px))" }}
|
||||
>
|
||||
<Dock
|
||||
direction="bottom"
|
||||
iconSize={52}
|
||||
iconMagnification={72}
|
||||
iconDistance={150}
|
||||
className="pointer-events-auto h-[68px] gap-2.5 border-border/60 bg-background/70 shadow-panel"
|
||||
iconSize={50}
|
||||
iconMagnification={66}
|
||||
iconDistance={140}
|
||||
className="pointer-events-auto h-[66px] gap-3 border-border/60 bg-background/70"
|
||||
>
|
||||
{/* ── Logo (fixed src → no refresh flash) ── */}
|
||||
<DockIcon className="group relative overflow-visible rounded-[26%] bg-white shadow-[0_6px_14px_-6px_rgba(0,0,0,0.5)] ring-1 ring-black/10">
|
||||
<DockIcon className="group relative overflow-visible">
|
||||
<Link
|
||||
href={homeHref}
|
||||
aria-label="mohfarawati — Home"
|
||||
className="flex h-full w-full items-center justify-center rounded-[inherit]"
|
||||
className="flex h-full w-full items-center justify-center"
|
||||
>
|
||||
<Image
|
||||
src="/logos/light-primary.svg"
|
||||
alt="mohfarawati"
|
||||
width={104}
|
||||
height={104}
|
||||
sizes="104px"
|
||||
priority
|
||||
className="h-full w-full rounded-[inherit] object-contain"
|
||||
/>
|
||||
<motion.span
|
||||
className="flex h-full w-full items-center justify-center overflow-hidden rounded-[22%] bg-white ring-1 ring-black/10"
|
||||
{...bounceProps("logo")}
|
||||
>
|
||||
<Image
|
||||
src="/logos/light-primary.svg"
|
||||
alt="mohfarawati"
|
||||
width={104}
|
||||
height={104}
|
||||
sizes="104px"
|
||||
priority
|
||||
className="h-full w-full object-contain"
|
||||
/>
|
||||
</motion.span>
|
||||
</Link>
|
||||
<DockTip label="mohfarawati" />
|
||||
</DockIcon>
|
||||
|
||||
<div className="mx-0.5 h-9 w-px self-center bg-border/70" aria-hidden />
|
||||
|
||||
{/* ── Pages ── */}
|
||||
{navItems.map(({ key, path, disabled }) => {
|
||||
{/* ── Pages (icon art carries its own squircle shape) ── */}
|
||||
{navItems.map(({ key, path, icon: iconSrc, disabled }) => {
|
||||
const itemPath = path || "/";
|
||||
const isActive = isNavItemActive(currentPath, itemPath);
|
||||
const label = t(key);
|
||||
const src = `/dock/${key}${isActive ? "-active" : ""}.svg`;
|
||||
|
||||
const icon = (
|
||||
<Image
|
||||
src={src}
|
||||
alt={label}
|
||||
width={104}
|
||||
height={104}
|
||||
sizes="104px"
|
||||
className={cn("h-full w-full rounded-[inherit] object-contain", disabled && "opacity-55")}
|
||||
/>
|
||||
<motion.span
|
||||
className="flex h-full w-full items-center justify-center"
|
||||
{...(disabled ? {} : bounceProps(key))}
|
||||
>
|
||||
<Image
|
||||
src={iconSrc}
|
||||
alt={label}
|
||||
width={104}
|
||||
height={104}
|
||||
sizes="104px"
|
||||
className={cn("h-full w-full object-contain", disabled && "opacity-55")}
|
||||
/>
|
||||
</motion.span>
|
||||
);
|
||||
|
||||
return (
|
||||
<DockIcon
|
||||
key={key}
|
||||
className={cn(
|
||||
"group relative overflow-visible rounded-[26%] transition-shadow duration-200",
|
||||
isActive
|
||||
? "shadow-[0_12px_26px_-8px_hsl(var(--primary)/0.65)]"
|
||||
: "shadow-[0_6px_14px_-6px_rgba(0,0,0,0.5)] hover:shadow-[0_10px_22px_-8px_rgba(0,0,0,0.6)]",
|
||||
)}
|
||||
>
|
||||
<DockIcon key={key} className="group relative overflow-visible">
|
||||
{disabled ? (
|
||||
<span
|
||||
aria-disabled="true"
|
||||
className="flex h-full w-full cursor-not-allowed items-center justify-center rounded-[inherit]"
|
||||
className="flex h-full w-full cursor-not-allowed items-center justify-center"
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
@@ -128,11 +146,20 @@ export function SiteDock({ defaultLocale, isSuperAdmin = false }: SiteDockProps)
|
||||
href={getLocalizedPath(locale, itemPath, defaultLocale)}
|
||||
aria-label={label}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className="flex h-full w-full items-center justify-center rounded-[inherit]"
|
||||
className="flex h-full w-full items-center justify-center"
|
||||
>
|
||||
{icon}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* macOS running indicator */}
|
||||
{isActive ? (
|
||||
<span
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute -bottom-[7px] left-1/2 h-[5px] w-[5px] -translate-x-1/2 rounded-full bg-foreground/70"
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<DockTip label={label} soon={disabled ? t("soon") : undefined} />
|
||||
</DockIcon>
|
||||
);
|
||||
@@ -150,7 +177,7 @@ function DockTip({ label, soon }: { label: string; soon?: string }) {
|
||||
return (
|
||||
<span
|
||||
role="tooltip"
|
||||
className="pointer-events-none absolute bottom-[calc(100%+0.85rem)] left-1/2 -translate-x-1/2 translate-y-1 whitespace-nowrap rounded-lg border border-border/60 bg-background/90 px-3 py-1.5 text-sm font-semibold text-foreground opacity-0 shadow-md backdrop-blur-chrome transition-all duration-150 group-hover:translate-y-0 group-hover:opacity-100"
|
||||
className="pointer-events-none absolute bottom-[calc(100%+0.85rem)] left-1/2 -translate-x-1/2 translate-y-1 whitespace-nowrap rounded-lg border border-border/60 bg-background/90 px-3 py-1.5 text-sm font-semibold text-foreground opacity-0 backdrop-blur-chrome transition-all duration-150 group-hover:translate-y-0 group-hover:opacity-100"
|
||||
>
|
||||
{label}
|
||||
{soon ? <span className="ms-1.5 text-xs tracking-wide text-primary">{soon}</span> : null}
|
||||
|
||||
@@ -123,10 +123,12 @@ const DockIcon = ({
|
||||
[size, targetSize, size],
|
||||
);
|
||||
|
||||
// Snappy, near-instant tracking with a touch of smoothing — closer to how the
|
||||
// real macOS dock follows the cursor than a soft/bouncy spring.
|
||||
const scaleSize = useSpring(sizeTransform, {
|
||||
mass: 0.1,
|
||||
stiffness: 150,
|
||||
damping: 12,
|
||||
stiffness: 280,
|
||||
damping: 22,
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ff8a5f"/><stop offset="1" stop-color="#e5431f"/></linearGradient>
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="50" cy="41" r="11"/><path d="M30 72 C30 57, 70 57, 70 72"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 853 B |
@@ -4,9 +4,9 @@
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="21.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="50" cy="41" r="11"/><path d="M30 72 C30 57, 70 57, 70 72"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 853 B |
@@ -1,12 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ff8a5f"/><stop offset="1" stop-color="#e5431f"/></linearGradient>
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="27" y="35" width="46" height="30" rx="6"/><path d="M30 40 L50 54 L70 40"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 865 B |
@@ -4,9 +4,9 @@
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="21.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="27" y="35" width="46" height="30" rx="6"/><path d="M30 40 L50 54 L70 40"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 865 B |
@@ -1,13 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ff8a5f"/><stop offset="1" stop-color="#e5431f"/></linearGradient>
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="#fff">
|
||||
<rect x="32" y="32" width="15" height="15" rx="4"/><rect x="53" y="32" width="15" height="15" rx="4"/>
|
||||
<rect x="32" y="53" width="15" height="15" rx="4"/><rect x="53" y="53" width="15" height="15" rx="4"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 909 B |
@@ -1,13 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#454b57"/><stop offset="1" stop-color="#1c2027"/></linearGradient>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#4aa8ff"/><stop offset="1" stop-color="#7b5cff"/></linearGradient>
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.30"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
<clipPath id="card"><rect x="27" y="31" width="46" height="38" rx="7"/></clipPath>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="#fff">
|
||||
<rect x="32" y="32" width="15" height="15" rx="4"/><rect x="53" y="32" width="15" height="15" rx="4"/>
|
||||
<rect x="32" y="53" width="15" height="15" rx="4"/><rect x="53" y="53" width="15" height="15" rx="4"/></g>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="21.25" fill="none" stroke="#fff" stroke-opacity="0.18" stroke-width="1.5"/>
|
||||
<rect x="27" y="31" width="46" height="38" rx="7" fill="#ffffff"/>
|
||||
<g clip-path="url(#card)">
|
||||
<circle cx="40" cy="44" r="5.5" fill="#ffc23d"/>
|
||||
<path d="M27 69 L43 52 L53 61 L61 51 L73 69 Z" fill="#37b877"/>
|
||||
<path d="M55 69 L66 57 L73 64 L73 69 Z" fill="#2c9c86"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 909 B After Width: | Height: | Size: 1.0 KiB |
@@ -1,12 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ff8a5f"/><stop offset="1" stop-color="#e5431f"/></linearGradient>
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M50 28 L71 40 L71 60 L50 72 L29 60 L29 40 Z"/><path d="M29 40 L50 52 L71 40"/><path d="M50 52 L50 72"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 894 B |
@@ -4,9 +4,9 @@
|
||||
<linearGradient id="gl" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#fff" stop-opacity="0.26"/><stop offset="0.5" stop-color="#fff" stop-opacity="0"/></linearGradient>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="28" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="27.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#bg)"/>
|
||||
<rect x="0" y="0" width="100" height="100" rx="22" fill="url(#gl)"/>
|
||||
<rect x="0.75" y="0.75" width="98.5" height="98.5" rx="21.25" fill="none" stroke="#fff" stroke-opacity="0.14" stroke-width="1.5"/>
|
||||
<g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M50 28 L71 40 L71 60 L50 72 L29 60 L29 40 Z"/><path d="M29 40 L50 52 L71 40"/><path d="M50 52 L50 72"/></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 894 B |