feat(mobile): add QrTemplate3 Pizzeria, fix PDF export for component system

This commit is contained in:
AyrisAI
2026-08-21 00:25:44 +03:00
parent ccd8c90267
commit 8481a34e61
8 changed files with 1514 additions and 73 deletions
+23 -73
View File
@@ -14,11 +14,10 @@ import * as Clipboard from "expo-clipboard";
import ViewShot from "react-native-view-shot";
import * as Sharing from "expo-sharing";
import * as Print from "expo-print";
import { SvgXml } from "react-native-svg";
import { QR_STAND_PRESETS, type QrStandPreset } from "@menulio/shared";
import { api } from "@/lib/api";
import { getActiveRestaurant, type ActiveRestaurant } from "@/lib/active-restaurant";
import { MOBILE_SVG_TEMPLATES } from "@/lib/svg-templates";
import { COMPONENT_TEMPLATES, TEMPLATE_ASPECT_RATIOS } from "@/components/qr-templates";
interface QrResponse {
id: string;
@@ -42,54 +41,12 @@ export default function QrScreen() {
const viewShotRef = useRef<any>(null);
const activePreset: QrStandPreset = (QR_STAND_PRESETS[selectedKey] || Object.values(QR_STAND_PRESETS)[0])!;
const currentSvgRaw = MOBILE_SVG_TEMPLATES[selectedKey] || MOBILE_SVG_TEMPLATES["qr-4"] || Object.values(MOBILE_SVG_TEMPLATES)[0] || "";
// Inject the live QR code image directly into the SVG's #qr-placeholder group
// Yorum satırlarını temizle + iç içe <g> taglarını doğru saymak için depth tracking kullan
function buildSvgWithQr(svgRaw: string, pngBase64: string): string {
// react-native-svg XML parser <!-- yorum satırlarını --> desteklemez
// Ayrıca text node'lardaki &amp; &lt; gibi entity'leri decode et
const clean = svgRaw
.replace(/<!--[\s\S]*?-->/g, "")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&apos;/g, "'")
.replace(/&quot;/g, '"');
const startMarker = '<g id="qr-placeholder"';
const startIdx = clean.indexOf(startMarker);
if (startIdx === -1) return clean;
// İç içe <g> taglarını sayarak doğru kapanış </g>'yi bul
let depth = 0;
let i = startIdx;
let endIdx = -1;
while (i < clean.length - 3) {
if (clean[i] === "<") {
if (clean[i + 1] === "g" && (clean[i + 2] === " " || clean[i + 2] === ">")) {
depth++;
} else if (clean[i + 1] === "/" && clean[i + 2] === "g" && clean[i + 3] === ">") {
depth--;
if (depth === 0) {
endIdx = i + 4; // </g> uzunluğu = 4
break;
}
}
}
i++;
}
if (endIdx === -1) return clean;
const qrGroup = `<g id="qr-placeholder" transform="translate(90, 215)">
<rect width="220" height="220" rx="20" fill="#FFFFFF"/>
<image href="data:image/png;base64,${pngBase64}" x="10" y="10" width="200" height="200" preserveAspectRatio="xMidYMid meet"/>
</g>`;
return clean.substring(0, startIdx) + qrGroup + clean.substring(endIdx);
}
// Component-based template sistemi
const TemplateComponent = COMPONENT_TEMPLATES[selectedKey] ?? Object.values(COMPONENT_TEMPLATES)[0];
const aspectRatio = TEMPLATE_ASPECT_RATIOS[selectedKey] ?? 1;
const displayWidth = 300;
const displayHeight = Math.round(displayWidth / aspectRatio);
useEffect(() => {
(async () => {
@@ -153,19 +110,12 @@ export default function QrScreen() {
}
async function handleExportPdf() {
if (!qr || !active || !currentSvgRaw) return;
if (!qr || !active) return;
setExportingPdf(true);
try {
const qrImageUri = `data:image/png;base64,${qr.pngBase64}`;
// Inject QR code directly into the SVG for print-ready A5 PDF
const svgWithQr = currentSvgRaw.replace(
/<g id="qr-placeholder"[^>]*>[\s\S]*?<\/g>/,
`<g id="qr-placeholder" transform="translate(90, 215)">
<rect width="220" height="220" rx="20" fill="#FFFFFF" stroke="#E4E4E7" stroke-width="2"/>
<image href="${qrImageUri}" x="10" y="10" width="200" height="200" preserveAspectRatio="xMidYMid meet" />
</g>`
);
// ViewShot ile önce PNG al, sonra A5 HTML içine göm
const pngUri = await viewShotRef.current?.capture();
if (!pngUri) throw new Error("Görsel alınamadı");
const htmlContent = `
<!DOCTYPE html>
@@ -181,14 +131,14 @@ export default function QrScreen() {
background: #000000;
-webkit-print-color-adjust: exact;
}
.svg-wrap {
.img-wrap {
width: 140mm;
height: 200mm;
display: flex;
align-items: center;
justify-content: center;
}
svg {
img {
width: 100%;
height: 100%;
object-fit: contain;
@@ -196,8 +146,8 @@ export default function QrScreen() {
</style>
</head>
<body>
<div class="svg-wrap">
${svgWithQr}
<div class="img-wrap">
<img src="${pngUri}" />
</div>
</body>
</html>
@@ -220,6 +170,7 @@ export default function QrScreen() {
}
}
if (loading) {
return (
<View style={{ flex: 1, backgroundColor: "#FAF8F5", alignItems: "center", justifyContent: "center" }}>
@@ -347,24 +298,23 @@ export default function QrScreen() {
}}
>
<ViewShot ref={viewShotRef} options={{ format: "png", quality: 1.0 }}>
{/* SVG with QR code injected directly — no overlay, pixel-perfect */}
{/* Component-based template — no XML parsing, pixel-perfect */}
<View
style={{
width: 300,
height: 487.5,
width: displayWidth,
height: displayHeight,
borderRadius: 24,
overflow: "hidden",
backgroundColor: "#0C0C0E",
}}
>
{currentSvgRaw ? (
<SvgXml
xml={buildSvgWithQr(currentSvgRaw, qr.pngBase64)}
width={300}
height={487.5}
{TemplateComponent ? (
<TemplateComponent
qrBase64={qr.pngBase64}
width={displayWidth}
height={displayHeight}
/>
) : null}
</View>
</ViewShot>
</View>
@@ -0,0 +1,348 @@
import * as React from "react";
import Svg, {
SvgProps,
Defs,
LinearGradient,
Stop,
Pattern,
Path,
G,
Rect,
Ellipse,
Circle,
Text,
TSpan,
Image,
} from "react-native-svg";
interface QrTemplate3Props extends SvgProps {
qrBase64?: string;
}
const QrTemplate3 = ({ qrBase64, ...props }: QrTemplate3Props) => (
<Svg
viewBox="0 0 400 650"
width={300}
height={487.5}
{...props}
>
<Defs>
<LinearGradient id="card-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
<Stop offset="0%" stopColor="#fdfbf5" />
<Stop offset="50%" stopColor="#faf4e6" />
<Stop offset="100%" stopColor="#f7eed8" />
</LinearGradient>
<LinearGradient id="oven-fire-3" x1="0%" x2="0%" y1="100%" y2="0%">
<Stop offset="0%" stopColor="#c0392b" />
<Stop offset="50%" stopColor="#e67e22" />
<Stop offset="100%" stopColor="#f1c40f" />
</LinearGradient>
<LinearGradient id="wine-grad-3" x1="0%" x2="0%" y1="0%" y2="100%">
<Stop offset="0%" stopColor="#7b1123" />
<Stop offset="100%" stopColor="#4a0512" />
</LinearGradient>
<Pattern
id="checkered-border-3"
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<Path fill="#b92b27" d="M0 0h20v20H0z" />
<Path fill="#fdf8ee" d="M0 0h10v10H0zm10 10h10v10H10z" />
</Pattern>
</Defs>
{/* Arka Plan */}
<Path fill="url(#checkered-border-3)" d="M0 0h400v650H0z" />
{/* Kart */}
<G id="layer-card">
<Rect
width={356}
height={606}
x={22}
y={22}
fill="url(#card-grad-3)"
stroke="#b92b27"
strokeWidth={2}
rx={20}
/>
<Rect
width={344}
height={594}
x={28}
y={28}
fill="none"
stroke="#8c6239"
strokeOpacity={0.5}
strokeWidth={0.8}
rx={15}
/>
<Rect
width={140}
height={24}
x={130}
y={34}
fill="#faf4e6"
stroke="#8c6239"
strokeWidth={1.2}
rx={12}
/>
</G>
{/* İkonlar */}
<G id="layer-icons">
<Path
fill="none"
stroke="#c5a880"
strokeLinecap="round"
strokeWidth={1.5}
d="M68 88q12-13 28-2 6 5 10 2m226 0q-12-13-28-2-6 5-10 2"
/>
{/* Pizza Fırını */}
<G id="pizza-oven" transform="translate(200 192)">
<Rect
width={88}
height={6}
x={-44}
fill="#d7ccc8"
stroke="#5d4037"
rx={2}
/>
<Path
fill="#d7ccc8"
stroke="#5d4037"
strokeWidth={1.5}
d="M-40 0a40 38 0 0 1 80 0Z"
/>
<Path stroke="#8d6e63" d="m-30-25 7 6m6-15 4 8M0-38v9m17-5-4 8m17 1-7 6" />
<Path fill="#3e2723" stroke="#271c19" d="M-28 0a28 26 0 0 1 56 0Z" />
<Path fill="url(#oven-fire-3)" d="M-18 0q3-16 12-10 6-12 12-2 9-6 12 12Z" />
<Path fill="#fff9c4" d="M-10 0q5-10 10-6 5-6 10 6Z" />
</G>
{/* Sol Malzemeler */}
<G id="left-ingredients" transform="translate(108 185)">
<Ellipse
cx={-16}
cy={6}
fill="#d32f2f"
stroke="#8e0000"
strokeWidth={0.8}
rx={14}
ry={10}
transform="rotate(-25 -16 6)"
/>
<Ellipse
cx={-16}
cy={6}
fill="#e53935"
rx={10}
ry={6.5}
transform="rotate(-25 -16 6)"
/>
<Ellipse
cx={2}
cy={7}
fill="#c62828"
stroke="#8e0000"
strokeWidth={0.8}
rx={13}
ry={9}
transform="rotate(-10 2 7)"
/>
<Ellipse cx={2} cy={7} fill="#e53935" rx={9} ry={5.5} transform="rotate(-10 2 7)" />
<Circle cx={2} cy={7} r={1.5} fill="#fdd835" />
<Circle cx={16} cy={-2} r={14} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
<Ellipse cx={12} cy={-6} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M16-16v3m0 0-5 2m5-2 5 2m-5-2-3-2m3 2 3-2"
/>
</G>
{/* Sağ Malzemeler */}
<G id="right-ingredients" transform="translate(290 182)">
<Circle cx={-14} cy={2} r={15} fill="#e53935" stroke="#b71c1c" strokeWidth={0.8} />
<Ellipse cx={-18} cy={-3} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M-14-13v3m0 0-4 2m4-2 4 2"
/>
<Path fill="#2e7d32" stroke="#1b5e20" strokeWidth={0.8} d="M-2 0C8-16 26-12 24 2 22 14 6 10-2 0Z" />
<Path fill="none" stroke="#4caf50" strokeWidth={0.7} d="M-2 0q14-4 26 2" />
<Path fill="#388e3c" stroke="#1b5e20" strokeWidth={0.8} d="M8 6c10-6 24 4 18 16-8 6-18-4-18-16Z" />
<Path fill="none" stroke="#81c784" strokeWidth={0.7} d="M8 6q12 6 18 16" />
</G>
{/* Alt Pizza Dilimi */}
<G id="footer-pizza-slice" transform="rotate(-10 3278.565 44.969)">
<Path fill="#deac68" stroke="#8d6e63" strokeWidth={1.2} d="m0 0 46-18c4 8 6 32 0 40Z" />
<Path fill="#fbc02d" d="m2 0 40-15c3 7 5 25 0 33Z" />
<Circle cx={28} cy={-5} r={5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Circle cx={32} cy={8} r={4.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Circle cx={16} r={3.5} fill="#c62828" stroke="#8e0000" strokeWidth={0.8} />
<Ellipse cx={23} cy={4} fill="#2e7d32" rx={2} ry={4} transform="rotate(30 23 4)" />
<Ellipse cx={34} cy={-11} fill="#2e7d32" rx={2} ry={3} transform="rotate(-40 34 -11)" />
</G>
{/* Şarap Kadehi */}
<G id="footer-wine-glass" transform="translate(345 562)">
<Path fill="none" stroke="#71717a" strokeWidth={1.2} d="M-14-24C-15-8-11 4 0 6 11 4 15-8 14-24Z" />
<Path fill="url(#wine-grad-3)" d="M-13-12C-11 0-7 4 0 5.5 7 4 11 0 13-12Z" />
<Ellipse cy={-12} fill="#991b1b" rx={13} ry={2} />
<Path stroke="#71717a" strokeWidth={1.4} d="M0 6v22" />
<Ellipse cy={28} fill="#e4e4e7" stroke="#71717a" strokeWidth={1.2} rx={11} ry={2} />
<Path fill="none" stroke="#fff" strokeLinecap="round" d="M-10-20c-2 8 1 18 6 23" opacity={0.6} />
</G>
{/* QR Köşe Aksanları */}
<G id="qr-corner-accents">
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M78 227v-22h22" />
<Path fill="none" stroke="#b92b27" strokeLinecap="round" strokeWidth={3} d="M322 227v-22h-22M78 423v22h22" />
<Path fill="none" stroke="#2e7d32" strokeLinecap="round" strokeWidth={3} d="M322 423v22h-22" />
</G>
</G>
{/* QR Placeholder / Gerçek QR */}
<G id="qr-placeholder" transform="translate(90 215)">
<Rect width={220} height={220} fill="#fff" stroke="#e4e4e7" strokeWidth={2} rx={20} />
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10}
y={10}
width={200}
height={200}
/>
) : (
<>
<Rect
width={196}
height={196}
x={12}
y={12}
fill="#fafafa"
stroke="#e4e4e7"
strokeDasharray="4 3"
rx={14}
/>
<Text
x={110}
y={115}
fill="#71717a"
fontFamily="Inter, Arial, sans-serif"
fontSize={12}
fontWeight={700}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */}
<G id="layer-texts">
<Text
x={200}
y={50}
fill="#18181b"
fontFamily="Inter, Arial, sans-serif"
fontSize={11}
fontWeight={800}
letterSpacing={1.2}
textAnchor="middle"
>
{"MASA NO: 01"}
</Text>
<Text
x={200}
y={96}
fill="#3e2723"
fontFamily="'Playfair Display', Georgia, serif"
fontSize={24}
textAnchor="middle"
>
{"PIZZERIA"}
</Text>
<Text
x={200}
y={132}
fill="#18181b"
fontFamily="'Playfair Display', Georgia, serif"
fontSize={31}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"BELLA NAPOLI"}
</Text>
<Text
x={200}
y={222}
fill="#2e7d32"
fontFamily="Inter, Arial, sans-serif"
fontSize={11.5}
fontWeight={800}
letterSpacing={1.8}
textAnchor="middle"
>
{"AUTENTICA CUCINA ITALIANA"}
</Text>
<Text
x={200}
y={474}
fill="#b92b27"
fontFamily="Inter, Arial, sans-serif"
fontSize={13}
fontWeight={700}
letterSpacing={1.5}
textAnchor="middle"
>
{"MENÜYÜ GÖRMEK"}
</Text>
<Text
x={200}
y={494}
fill="#b92b27"
fontFamily="Inter, Arial, sans-serif"
fontSize={13}
fontWeight={700}
letterSpacing={1.5}
textAnchor="middle"
>
{"İÇİN OKUTUN"}
</Text>
<Text x={200} y={546} textAnchor="middle">
<TSpan fill="#3e2723" fontFamily="Inter, Arial, sans-serif" fontSize={10} fontWeight={700}>
{"WiFi: "}
</TSpan>
<TSpan fill="#5d4037" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
{"GourmetBistro"}
</TSpan>
<TSpan fill="#a1a1aa" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
{" | "}
</TSpan>
<TSpan fill="#3e2723" fontFamily="Inter, Arial, sans-serif" fontSize={10} fontWeight={700}>
{"Şifre: "}
</TSpan>
<TSpan fill="#5d4037" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
{"lezzetli01"}
</TSpan>
</Text>
<Text x={200} y={570} textAnchor="middle">
<TSpan fill="#b92b27" fontFamily="Inter, Arial, sans-serif" fontSize={10} fontWeight={700}>
{"BİZİ TAKİP EDİN"}
</TSpan>
<TSpan fill="#a1a1aa" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
{" | "}
</TSpan>
<TSpan fill="#5d4037" fontFamily="Inter, Arial, sans-serif" fontSize={10}>
{"@gourmetbistro"}
</TSpan>
</Text>
</G>
</Svg>
);
export default QrTemplate3;
@@ -0,0 +1,213 @@
import * as React from "react";
import Svg, {
SvgProps,
Path,
Rect,
Circle,
G,
Text,
Image,
} from "react-native-svg";
interface QrTemplateLumiereProps extends SvgProps {
qrBase64?: string;
}
const QrTemplateLumiere = ({ qrBase64, ...props }: QrTemplateLumiereProps) => (
<Svg
viewBox="0 0 600 600"
width={300}
height={300}
{...props}
>
<Path fill="#0b0d13" d="M0 0h600v600H0z" />
<Rect
width={560}
height={560}
x={20}
y={20}
fill="#132034"
stroke="#d4af37"
strokeWidth={1.5}
rx={24}
/>
<Path
fill="none"
stroke="#d4af37"
strokeOpacity={0.85}
strokeWidth={1.5}
d="M56 40h488a16 16 0 0 1 16 16v488a16 16 0 0 1-16 16H56a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16Z"
/>
<Rect
width={504}
height={504}
x={48}
y={48}
fill="none"
stroke="#c5a059"
strokeOpacity={0.45}
strokeWidth={0.8}
rx={8}
/>
{/* Köşe süslemeleri */}
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M40 68a18 18 0 0 0 28-28"
/>
<Circle cx={52} cy={52} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M560 68a18 18 0 0 1-28-28"
/>
<Circle cx={548} cy={52} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M40 532a18 18 0 0 1 28 28"
/>
<Circle cx={52} cy={548} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M560 532a18 18 0 0 0-28 28"
/>
<Circle cx={548} cy={548} r={2} fill="#d4af37" />
{/* Merkez amblem */}
<G transform="translate(300 85)">
<Path
fill="none"
stroke="#d4af37"
strokeLinejoin="round"
strokeWidth={2}
d="M0-26C-8-16-8-6 0 0c8-6 8-16 0-26Z"
/>
<Path fill="#d4af37" d="M0-18C-4-12-4-4 0 0c4-4 4-12 0-18" />
<Path
fill="none"
stroke="#d4af37"
strokeLinejoin="round"
strokeWidth={2}
d="M-3 3c-13-2-25 7-21 21C-12 24-3 12-3 3Zm6 0c13-2 25 7 21 21C12 24 3 12 3 3Z"
/>
<Circle cy={10} r={1.8} fill="#d4af37" />
</G>
{/* Yatay çizgi */}
<Path
stroke="#d4af37"
strokeOpacity={0.4}
strokeWidth={0.8}
d="M120 198h360"
/>
{/* QR Placeholder / Gerçek QR */}
<G transform="translate(190 215)">
<Rect
width={220}
height={220}
fill="#fff"
stroke="#d4af37"
strokeWidth={2}
rx={20}
/>
{qrBase64 ? (
<Image
href={`data:image/png;base64,${qrBase64}`}
x={10}
y={10}
width={200}
height={200}
/>
) : (
<>
<Rect
width={196}
height={196}
x={12}
y={12}
fill="#fafafa"
stroke="#d4af37"
strokeDasharray="4 3"
strokeOpacity={0.4}
rx={14}
/>
<Text
x={110}
y={115}
fill="#71717a"
fontFamily="Inter, Arial, sans-serif"
fontSize={12}
fontWeight={600}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</>
)}
</G>
{/* Metinler */}
<G textAnchor="middle">
<Text
x={300}
y={152}
fill="#d4af37"
fontFamily="'Playfair Display', 'Cinzel', 'Didot', Georgia, serif"
fontSize={38}
fontWeight={500}
letterSpacing={6}
>
{"LUMI\xC8RE"}
</Text>
<Text
x={300}
y={180}
fill="#e5c378"
fontFamily="Inter, Arial, sans-serif"
fontSize={11}
fontWeight={600}
letterSpacing={4}
>
{"RESTAURANT & BAR | MODERN CUISINE"}
</Text>
<Text
x={300}
y={472}
fill="#d4af37"
fontFamily="Inter, Arial, sans-serif"
fontSize={13}
fontWeight={600}
letterSpacing={3}
>
{"SCAN WITH CAMERA"}
</Text>
<Text
x={300}
y={508}
fill="#e2ddd2"
fontFamily="Inter, Arial, sans-serif"
fontSize={10.5}
fontWeight={400}
letterSpacing={1.2}
>
{"Reservations: LumiereDining.com | 240-555-0199"}
</Text>
<Text
x={300}
y={528}
fill="#c5a059"
fontFamily="Inter, Arial, sans-serif"
fontSize={9.5}
fontWeight={500}
letterSpacing={1.8}
>
{"TABLE 01 \u2022 WIFI: Lumiere_Guest"}
</Text>
</G>
</Svg>
);
export default QrTemplateLumiere;
@@ -0,0 +1,23 @@
import type React from "react";
import type { SvgProps } from "react-native-svg";
import QrTemplateLumiere from "./QrTemplateLumiere";
import QrTemplate3 from "./QrTemplate3";
export interface QrTemplateProps extends SvgProps {
qrBase64?: string;
}
// Yeni component eklemek için buraya import edip COMPONENT_TEMPLATES'e kaydet
export const COMPONENT_TEMPLATES: Record<
string,
React.FC<QrTemplateProps>
> = {
"qr-lumiere": QrTemplateLumiere,
"qr-3": QrTemplate3,
};
// Her template'in aspect ratio'su (width/height)
export const TEMPLATE_ASPECT_RATIOS: Record<string, number> = {
"qr-lumiere": 1, // 600x600 = 1:1
"qr-3": 400 / 650, // 400x650 = standart kart
};
+177
View File
@@ -0,0 +1,177 @@
import * as React from "react"
import Svg, { SvgProps, Path, Rect, Circle, G, Text } from "react-native-svg"
const SvgComponent = (props: SvgProps) => (
<Svg xmlns="http://www.w3.org/2000/svg" width={600} height={600} {...props}>
<Path fill="#0b0d13" d="M0 0h600v600H0z" />
<Rect
width={560}
height={560}
x={20}
y={20}
fill="#132034"
stroke="#d4af37"
strokeWidth={1.5}
rx={24}
/>
<Path
fill="none"
stroke="#d4af37"
strokeOpacity={0.85}
strokeWidth={1.5}
d="M56 40h488a16 16 0 0 1 16 16v488a16 16 0 0 1-16 16H56a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16Z"
/>
<Rect
width={504}
height={504}
x={48}
y={48}
fill="none"
stroke="#c5a059"
strokeOpacity={0.45}
strokeWidth={0.8}
rx={8}
/>
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M40 68a18 18 0 0 0 28-28"
/>
<Circle cx={52} cy={52} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M560 68a18 18 0 0 1-28-28"
/>
<Circle cx={548} cy={52} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M40 532a18 18 0 0 1 28 28"
/>
<Circle cx={52} cy={548} r={2} fill="#d4af37" />
<Path
fill="none"
stroke="#d4af37"
strokeWidth={1.5}
d="M560 532a18 18 0 0 0-28 28"
/>
<Circle cx={548} cy={548} r={2} fill="#d4af37" />
<G transform="translate(300 85)">
<Path
fill="none"
stroke="#d4af37"
strokeLinejoin="round"
strokeWidth={2}
d="M0-26C-8-16-8-6 0 0c8-6 8-16 0-26Z"
/>
<Path fill="#d4af37" d="M0-18C-4-12-4-4 0 0c4-4 4-12 0-18" />
<Path
fill="none"
stroke="#d4af37"
strokeLinejoin="round"
strokeWidth={2}
d="M-3 3c-13-2-25 7-21 21C-12 24-3 12-3 3Zm6 0c13-2 25 7 21 21C12 24 3 12 3 3Z"
/>
<Circle cy={10} r={1.8} fill="#d4af37" />
</G>
<Path
stroke="#d4af37"
strokeOpacity={0.4}
strokeWidth={0.8}
d="M120 198h360"
/>
<G transform="translate(190 215)">
<Rect
width={220}
height={220}
fill="#fff"
stroke="#d4af37"
strokeWidth={2}
rx={20}
/>
<Rect
width={196}
height={196}
x={12}
y={12}
fill="#fafafa"
stroke="#d4af37"
strokeDasharray="4 3"
strokeOpacity={0.4}
rx={14}
/>
<Text
x={110}
y={115}
fill="#71717a"
fontFamily="Inter, Arial, sans-serif"
fontSize={12}
fontWeight={600}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</G>
<G textAnchor="middle">
<Text
x={300}
y={152}
fill="#d4af37"
fontFamily="'Playfair Display', 'Cinzel', 'Didot', Georgia, serif"
fontSize={38}
fontWeight={500}
letterSpacing={6}
>
{"LUMI\xC8RE"}
</Text>
<Text
x={300}
y={180}
fill="#e5c378"
fontFamily="Inter, Arial, sans-serif"
fontSize={11}
fontWeight={600}
letterSpacing={4}
>
{"RESTAURANT & BAR | MODERN CUISINE"}
</Text>
<Text
x={300}
y={472}
fill="#d4af37"
fontFamily="Inter, Arial, sans-serif"
fontSize={13}
fontWeight={600}
letterSpacing={3}
>
{"SCAN WITH CAMERA"}
</Text>
<Text
x={300}
y={508}
fill="#e2ddd2"
fontFamily="Inter, Arial, sans-serif"
fontSize={10.5}
fontWeight={400}
letterSpacing={1.2}
>
{"Reservations: LumiereDining.com | 240-555-0199"}
</Text>
<Text
x={300}
y={528}
fill="#c5a059"
fontFamily="Inter, Arial, sans-serif"
fontSize={9.5}
fontWeight={500}
letterSpacing={1.8}
>
{"TABLE 01 \u2022 WIFI: Lumiere_Guest"}
</Text>
</G>
</Svg>
)
export default SvgComponent
+286
View File
@@ -0,0 +1,286 @@
import * as React from "react"
import Svg, {
SvgProps,
Path,
Rect,
G,
Circle,
Ellipse,
Text,
} from "react-native-svg"
const SvgComponent = (props: SvgProps) => (
<Svg xmlns="http://www.w3.org/2000/svg" width={400} height={650} {...props}>
<Path fill="#252c36" d="M0 0h400v650H0z" />
<Rect
width={360}
height={610}
x={20}
y={20}
fill="#f6efe2"
stroke="#3a2312"
strokeWidth={1.5}
rx={20}
/>
<Rect
width={344}
height={594}
x={28}
y={28}
fill="none"
stroke="#8c6239"
strokeOpacity={0.4}
rx={14}
/>
<Rect
width={336}
height={586}
x={32}
y={32}
fill="none"
stroke="#8c6239"
strokeOpacity={0.25}
strokeWidth={0.5}
rx={10}
/>
<G fill="none" stroke="#3a2312" strokeLinecap="round" strokeWidth={1.05}>
<Path d="M36 36c11.25 11.25 18.75 26.25 22.5 48.75" />
<Path
fill="#eadcc7"
d="M43.5 45c-3.75-7.5 7.5-10.5 9-3 0 7.5-9 3-9 3Zm6 11.25C45 51 57 46.5 60 53.25c0 6.75-10.5 3-10.5 3Zm4.5 13.5C60 64.5 69.75 72 64.5 78c-6 4.5-10.5-8.25-10.5-8.25Z"
/>
</G>
<G transform="translate(340 52)">
<Circle r={32} fill="#eadcc7" stroke="#3a2312" strokeWidth={1.2} />
<Circle
r={26}
fill="#f6efe2"
stroke="#8c6239"
strokeDasharray="2 2"
strokeWidth={0.8}
/>
<Circle r={21} fill="#7a4b26" stroke="#3a2312" strokeWidth={1.4} />
<Path fill="#f6efe2" d="M0 10C-8 4-4-6 0-12 4-6 8 4 0 10" />
<Path fill="#7a4b26" d="M0 4C-5 0-2-5 0-8 2-5 5 0 0 4" />
<Circle cy={-14} r={1.8} fill="#f6efe2" />
</G>
<G transform="translate(48 250)">
<Circle r={26} fill="#eadcc7" stroke="#3a2312" />
<Circle r={17} fill="#6a3b18" stroke="#3a2312" strokeWidth={1.2} />
<Path fill="#f6efe2" d="M-5-3c-3-4 5-7 5-1 0-6 8-3 5 1L0 7z" />
<Path
fill="none"
stroke="#3a2312"
strokeWidth={1.5}
d="M17-4c6 0 6 8 0 8"
/>
</G>
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M352 230c-7 21-10.5 49-3.5 77" />
<Path
fill="#eadcc7"
d="M349.2 247.5c8.4-7 15.4 2.1 5.6 7.7-5.6 0-5.6-7.7-5.6-7.7Zm-2.8 24.5c-9.8-8.4-15.4 1.4-7 7 7 0 7-7 7-7Zm2.1 19.6c9.1-7 13.3 2.8 4.9 8.4-4.9 0-4.9-8.4-4.9-8.4Z"
/>
</G>
<Path
fill="none"
stroke="#8c6239"
strokeLinecap="round"
strokeWidth={1.2}
d="M142 66c6-2 12 2 18 0m80 0c6 2 12-2 18 0"
/>
<G fill="none" stroke="#3a2312" strokeWidth={0.91}>
<Path d="M42 601.5c13-26 9.75-52 0-71.5" />
<Path
fill="#eadcc7"
d="M47.2 549.5c10.4-9.75 17.55 0 6.5 7.8Zm2.6 26c11.7-9.75 18.2 1.3 5.2 9.1Z"
/>
</G>
<G
stroke="#3a2312"
strokeWidth={1.2}
transform="rotate(-20 1741.206 -8.75) scale(.8)"
>
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G
stroke="#3a2312"
strokeWidth={1.2}
transform="rotate(35 -862.106 494.982) scale(.8)"
>
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G
stroke="#3a2312"
strokeWidth={1.2}
transform="rotate(-45 602.558 -225.588) scale(.7)"
>
<Ellipse fill="#cbb296" rx={9} ry={6.5} />
<Path fill="none" d="M-7 0q7 3 7 0t7 0" />
</G>
<G transform="translate(325 590)">
<Ellipse
cy={18}
fill="#eadcc7"
stroke="#3a2312"
strokeWidth={1.2}
rx={28}
ry={7}
/>
<Path
fill="#f6efe2"
stroke="#3a2312"
strokeWidth={1.4}
d="M-18 2c0 14 36 14 36 0Z"
/>
<Ellipse
cy={2}
fill="#6a3b18"
stroke="#3a2312"
strokeWidth={1.2}
rx={18}
ry={4}
/>
<Path
fill="none"
stroke="#3a2312"
strokeWidth={1.3}
d="M16 4c8 0 8 8 0 10"
/>
<Path
fill="none"
stroke="#8c6239"
strokeLinecap="round"
d="M-6-4c-2-5 2-8 0-13M2-5c-2-5 2-8 0-14"
/>
</G>
<Path
fill="none"
stroke="#3a2312"
strokeLinecap="round"
strokeWidth={2}
d="M80 205c0-10 0-10 10-10m230 10c0-10 0-10-10-10M80 445c0 10 0 10 10 10m230-10c0 10 0 10-10 10"
/>
<Path stroke="#8c6239" strokeWidth={0.8} d="M145 608h30m50 0h30" />
<G transform="translate(90 215)">
<Rect
width={220}
height={220}
fill="#fbf8f2"
stroke="#d8c7b0"
strokeWidth={1.5}
rx={20}
/>
<Rect
width={200}
height={200}
x={10}
y={10}
fill="#f5efe6"
stroke="#d8c7b0"
strokeDasharray="4 3"
rx={14}
/>
<Circle cx={110} cy={98} r={18} fill="#fbf8f2" stroke="#8c6239" />
<G fill="#3a2312" transform="matrix(.65 0 0 .65 110 98)">
<Ellipse cx={-5} cy={2} rx={7} ry={5} transform="rotate(-30)" />
<Ellipse cx={5} rx={7} ry={5} transform="rotate(40)" />
</G>
<Text
x={110}
y={132}
fill="#8c6239"
fontFamily="Inter, Arial, sans-serif"
fontSize={11}
fontWeight={700}
letterSpacing={1}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</G>
<G textAnchor="middle">
<Text
x={200}
y={70}
fill="#3a2312"
fontFamily="'Playfair Display', Georgia, serif"
fontSize={14}
fontStyle="italic"
fontWeight={700}
letterSpacing={4}
>
{"THE"}
</Text>
<Text
x={200}
y={114}
fill="#2c1a0e"
fontFamily="'Playfair Display', 'Brush Script MT', 'Great Vibes', Georgia, cursive, serif"
fontSize={34}
fontWeight={800}
letterSpacing={0.5}
>
{"The Daily Grind"}
</Text>
<Text
x={200}
y={146}
fill="#3a2312"
fontFamily="Inter, Arial, sans-serif"
fontSize={13}
fontWeight={800}
letterSpacing={3}
>
{"COFFEE & KITCHEN"}
</Text>
<Text
x={200}
y={488}
fill="#2c1a0e"
fontFamily="Inter, Arial, sans-serif"
fontSize={19}
fontWeight={900}
letterSpacing={1.5}
>
{"SCAN TO VIEW MENU"}
</Text>
<Text
x={200}
y={520}
fill="#3a2312"
fontFamily="Inter, Arial, sans-serif"
fontSize={10.5}
fontWeight={700}
letterSpacing={0.5}
>
{"DAILY GRIND CAFE | WIFI: DAILY_GRIND_GUEST"}
</Text>
<Text
x={200}
y={542}
fill="#8c6239"
fontFamily="Inter, Arial, sans-serif"
fontSize={9}
fontWeight={600}
letterSpacing={1}
>
{"\u015E\u0130FRE: coffee2026 \u2022 MASA NO: 04"}
</Text>
<Text
x={200}
y={611}
fill="#3a2312"
fontFamily="'Playfair Display', Georgia, serif"
fontSize={11}
fontStyle="italic"
fontWeight={700}
letterSpacing={2}
>
{"EST. 2018"}
</Text>
</G>
</Svg>
)
export default SvgComponent
+416
View File
@@ -0,0 +1,416 @@
import * as React from "react"
import Svg, {
SvgProps,
Defs,
LinearGradient,
Stop,
Pattern,
Path,
G,
Rect,
Ellipse,
Circle,
Text,
TSpan,
} from "react-native-svg"
/* SVGR has dropped some elements not supported by react-native-svg: style */
const SvgComponent = (props: SvgProps) => (
<Svg width={400} height={650} {...props}>
<Defs>
<LinearGradient id="card-grad" x1="0%" x2="0%" y1="0%" y2="100%">
<Stop offset="0%" stopColor="#fdfbf5" />
<Stop offset="50%" stopColor="#faf4e6" />
<Stop offset="100%" stopColor="#f7eed8" />
</LinearGradient>
<LinearGradient id="oven-fire" x1="0%" x2="0%" y1="100%" y2="0%">
<Stop offset="0%" stopColor="#c0392b" />
<Stop offset="50%" stopColor="#e67e22" />
<Stop offset="100%" stopColor="#f1c40f" />
</LinearGradient>
<LinearGradient id="wine-grad" x1="0%" x2="0%" y1="0%" y2="100%">
<Stop offset="0%" stopColor="#7b1123" />
<Stop offset="100%" stopColor="#4a0512" />
</LinearGradient>
<Pattern
id="checkered-border"
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<Path fill="#b92b27" d="M0 0h20v20H0z" />
<Path fill="#fdf8ee" d="M0 0h10v10H0zm10 10h10v10H10z" />
</Pattern>
</Defs>
<Path
id="layer-background"
fill="url(#checkered-border)"
d="M0 0h400v650H0z"
/>
<G id="layer-card">
<Rect
width={356}
height={606}
x={22}
y={22}
fill="url(#card-grad)"
stroke="#b92b27"
strokeWidth={2}
rx={20}
/>
<Rect
width={344}
height={594}
x={28}
y={28}
fill="none"
stroke="#8c6239"
strokeOpacity={0.5}
strokeWidth={0.8}
rx={15}
/>
<Rect
width={140}
height={24}
x={130}
y={34}
fill="#faf4e6"
stroke="#8c6239"
strokeWidth={1.2}
rx={12}
/>
</G>
<G id="layer-icons">
<Path
fill="none"
stroke="#c5a880"
strokeLinecap="round"
strokeWidth={1.5}
d="M68 88q12-13 28-2 6 5 10 2m226 0q-12-13-28-2-6 5-10 2"
/>
<G id="pizza-oven" transform="translate(200 192)">
<Rect
width={88}
height={6}
x={-44}
fill="#d7ccc8"
stroke="#5d4037"
rx={2}
/>
<Path
fill="#d7ccc8"
stroke="#5d4037"
strokeWidth={1.5}
d="M-40 0a40 38 0 0 1 80 0Z"
/>
<Path
stroke="#8d6e63"
d="m-30-25 7 6m6-15 4 8M0-38v9m17-5-4 8m17 1-7 6"
/>
<Path fill="#3e2723" stroke="#271c19" d="M-28 0a28 26 0 0 1 56 0Z" />
<Path
fill="url(#oven-fire)"
d="M-18 0q3-16 12-10 6-12 12-2 9-6 12 12Z"
/>
<Path fill="#fff9c4" d="M-10 0q5-10 10-6 5-6 10 6Z" />
</G>
<G id="left-ingredients" transform="translate(108 185)">
<Ellipse
cx={-16}
cy={6}
fill="#d32f2f"
stroke="#8e0000"
strokeWidth={0.8}
rx={14}
ry={10}
transform="rotate(-25 -16 6)"
/>
<Ellipse
cx={-16}
cy={6}
fill="#e53935"
rx={10}
ry={6.5}
transform="rotate(-25 -16 6)"
/>
<Ellipse
cx={2}
cy={7}
fill="#c62828"
stroke="#8e0000"
strokeWidth={0.8}
rx={13}
ry={9}
transform="rotate(-10 2 7)"
/>
<Ellipse
cx={2}
cy={7}
fill="#e53935"
rx={9}
ry={5.5}
transform="rotate(-10 2 7)"
/>
<Circle cx={2} cy={7} r={1.5} fill="#fdd835" />
<Circle
cx={16}
cy={-2}
r={14}
fill="#e53935"
stroke="#b71c1c"
strokeWidth={0.8}
/>
<Ellipse cx={12} cy={-6} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M16-16v3m0 0-5 2m5-2 5 2m-5-2-3-2m3 2 3-2"
/>
</G>
<G id="right-ingredients" transform="translate(290 182)">
<Circle
cx={-14}
cy={2}
r={15}
fill="#e53935"
stroke="#b71c1c"
strokeWidth={0.8}
/>
<Ellipse cx={-18} cy={-3} fill="#ff8a80" opacity={0.6} rx={4} ry={2} />
<Path
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={1.5}
d="M-14-13v3m0 0-4 2m4-2 4 2"
/>
<Path
fill="#2e7d32"
stroke="#1b5e20"
strokeWidth={0.8}
d="M-2 0C8-16 26-12 24 2 22 14 6 10-2 0Z"
/>
<Path
fill="none"
stroke="#4caf50"
strokeWidth={0.7}
d="M-2 0q14-4 26 2"
/>
<Path
fill="#388e3c"
stroke="#1b5e20"
strokeWidth={0.8}
d="M8 6c10-6 24 4 18 16-8 6-18-4-18-16Z"
/>
<Path
fill="none"
stroke="#81c784"
strokeWidth={0.7}
d="M8 6q12 6 18 16"
/>
</G>
<G id="footer-pizza-slice" transform="rotate(-10 3278.565 44.969)">
<Path
fill="#deac68"
stroke="#8d6e63"
strokeWidth={1.2}
d="m0 0 46-18c4 8 6 32 0 40Z"
/>
<Path fill="#fbc02d" d="m2 0 40-15c3 7 5 25 0 33Z" />
<Circle
cx={28}
cy={-5}
r={5}
fill="#c62828"
stroke="#8e0000"
strokeWidth={0.8}
/>
<Circle
cx={32}
cy={8}
r={4.5}
fill="#c62828"
stroke="#8e0000"
strokeWidth={0.8}
/>
<Circle
cx={16}
r={3.5}
fill="#c62828"
stroke="#8e0000"
strokeWidth={0.8}
/>
<Ellipse
cx={23}
cy={4}
fill="#2e7d32"
rx={2}
ry={4}
transform="rotate(30 23 4)"
/>
<Ellipse
cx={34}
cy={-11}
fill="#2e7d32"
rx={2}
ry={3}
transform="rotate(-40 34 -11)"
/>
</G>
<G id="footer-wine-glass" transform="translate(345 562)">
<Path
fill="none"
stroke="#71717a"
strokeWidth={1.2}
d="M-14-24C-15-8-11 4 0 6 11 4 15-8 14-24Z"
/>
<Path
fill="url(#wine-grad)"
d="M-13-12C-11 0-7 4 0 5.5 7 4 11 0 13-12Z"
/>
<Ellipse cy={-12} fill="#991b1b" rx={13} ry={2} />
<Path stroke="#71717a" strokeWidth={1.4} d="M0 6v22" />
<Ellipse
cy={28}
fill="#e4e4e7"
stroke="#71717a"
strokeWidth={1.2}
rx={11}
ry={2}
/>
<Path
fill="none"
stroke="#fff"
strokeLinecap="round"
d="M-10-20c-2 8 1 18 6 23"
opacity={0.6}
/>
</G>
<G id="qr-corner-accents">
<Path
fill="none"
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={3}
d="M78 227v-22h22"
/>
<Path
fill="none"
stroke="#b92b27"
strokeLinecap="round"
strokeWidth={3}
d="M322 227v-22h-22M78 423v22h22"
/>
<Path
fill="none"
stroke="#2e7d32"
strokeLinecap="round"
strokeWidth={3}
d="M322 423v22h-22"
/>
</G>
</G>
<G id="qr-placeholder" transform="translate(90 215)">
<Rect
width={220}
height={220}
fill="#fff"
stroke="#e4e4e7"
strokeWidth={2}
rx={20}
/>
<Rect
width={196}
height={196}
x={12}
y={12}
fill="#fafafa"
stroke="#e4e4e7"
strokeDasharray="4 3"
rx={14}
/>
<Text
x={110}
y={115}
fill="#71717a"
dominantBaseline="middle"
fontFamily='"Inter",Arial,sans-serif'
fontSize={12}
fontWeight={700}
letterSpacing={0.5}
textAnchor="middle"
>
{"QR KOD ALANI"}
</Text>
</G>
<G id="layer-texts">
<Text
x={200}
y={50}
fill="#18181b"
fontFamily='"Inter",Arial,sans-serif'
fontSize={11}
fontWeight={800}
letterSpacing={1.2}
textAnchor="middle"
>
{"MASA NO: 01"}
</Text>
<Text
x={200}
y={96}
className="font-brand-arc"
fontSize={24}
textAnchor="middle"
>
{"PIZZERIA"}
</Text>
<Text
x={200}
y={132}
className="font-brand-arc"
fontSize={31}
letterSpacing={1}
textAnchor="middle"
>
{"BELLA NAPOLI"}
</Text>
<Text
x={200}
y={222}
fill="#2e7d32"
fontFamily='"Inter",Arial,sans-serif'
fontSize={11.5}
fontWeight={800}
letterSpacing={1.8}
textAnchor="middle"
>
{"AUTENTICA CUCINA ITALIANA"}
</Text>
<Text x={200} y={474} className="font-cta" textAnchor="middle">
{"MEN\xDCY\xDC G\xD6RMEK"}
</Text>
<Text x={200} y={494} className="font-cta" textAnchor="middle">
{"\u0130\xC7\u0130N OKUTUN"}
</Text>
<Text x={200} y={546} textAnchor="middle">
<TSpan className="font-footer-bold">{"WiFi: "}</TSpan>
<TSpan className="font-footer-reg">{"GourmetBistro"}</TSpan>
<TSpan fill="#a1a1aa" className="font-footer-reg">
{" | "}
</TSpan>
<TSpan className="font-footer-bold">{"\u015Eifre: "}</TSpan>
<TSpan className="font-footer-reg">{"lezzetli01"}</TSpan>
</Text>
<Text x={200} y={570} textAnchor="middle">
<TSpan fill="#b92b27" className="font-footer-bold">
{"B\u0130Z\u0130 TAK\u0130P ED\u0130N"}
</TSpan>
<TSpan fill="#a1a1aa" className="font-footer-reg">
{" | "}
</TSpan>
<TSpan className="font-footer-reg">{"@gourmetbistro"}</TSpan>
</Text>
</G>
</Svg>
)
export default SvgComponent
+28
View File
@@ -77,6 +77,34 @@ export const QR_STAND_PRESETS: Record<string, QrStandPreset> = {
subText: "#6B5A3E",
dividerColor: "rgba(212, 175, 55, 0.3)",
},
"qr-lumiere": {
key: "qr-lumiere",
name: "Lumière Restaurant (QR-L)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-lumiere.svg",
aspectRatio: "1/1",
bgGradient: "radial-gradient(circle at 50% 50%, #132034 0%, #0b0d13 100%)",
plaqueColor: "#132034",
borderAccent: "#d4af37",
accentText: "#d4af37",
headerText: "#FFFFFF",
subText: "#e5c378",
dividerColor: "rgba(212, 175, 55, 0.3)",
},
"qr-3": {
key: "qr-3",
name: "Pizzeria Bella Napoli (QR-3)",
categoryName: "Özel Masa Kartı",
svgTemplateUrl: "/qr-3.svg",
aspectRatio: "400/650",
bgGradient: "radial-gradient(circle at 50% 50%, #faf4e6 0%, #f7eed8 100%)",
plaqueColor: "#fdfbf5",
borderAccent: "#b92b27",
accentText: "#b92b27",
headerText: "#18181b",
subText: "#5d4037",
dividerColor: "rgba(185, 43, 39, 0.3)",
},
};