diff --git a/app/(public)/sign-in/page.tsx b/app/(public)/sign-in/page.tsx index 8cf2ff6..6863dd7 100644 --- a/app/(public)/sign-in/page.tsx +++ b/app/(public)/sign-in/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import Image from "next/image"; import Link from "next/link"; import { redirect } from "next/navigation"; @@ -6,7 +7,63 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { createClient } from "@/db/server"; +import { env } from "@/env/client"; +export const metadata: Metadata = { + title: "Sign In", + alternates: { + canonical: "/sign-in", + }, + icons: { + apple: "/static/favicon/apple-touch-icon.png", + other: [ + { + rel: "alternate icon", + sizes: "48x48", + url: "/static/favicon/favicon.ico", + }, + { + rel: "icon", + type: "image/svg+xml", + sizes: "any", + url: "/static/favicon/favicon.svg", + }, + { + rel: "icon", + type: "image/png", + sizes: "32x32", + url: "/static/favicon/favicon-32x32.png", + }, + { + rel: "icon", + type: "image/png", + sizes: "16x16", + url: "/static/favicon/favicon-16x16.png", + }, + { + rel: "mask-icon", + color: "#0e0813", + url: "/public/static/favicon/safari-pinned-tab.svg", + }, + ], + }, + openGraph: { + title: "Sign In - rikhall.proj", + description: "The sign in page for the NextJS template.", + url: env.NEXT_PUBLIC_BASE_URL, + images: "/next-data/og?title=Sign%20In", + siteName: "RikardTemplate", + locale: "en_US", + type: "website", + }, + twitter: { + title: "Sign In - rikhall.proj", + description: "The sign in page for the NextJS template.", + images: "/next-data/og?title=Sign%20In", + card: "summary_large_image", + creator: "@rikhall_", + }, +}; export default function SignIn() { const signIn = async (formData: FormData) => { "use server"; diff --git a/app/(public)/sign-up/page.tsx b/app/(public)/sign-up/page.tsx index 3838dc7..d167008 100644 --- a/app/(public)/sign-up/page.tsx +++ b/app/(public)/sign-up/page.tsx @@ -1,3 +1,4 @@ +import type { Metadata } from "next"; import { headers } from "next/headers"; import Image from "next/image"; import Link from "next/link"; @@ -7,6 +8,63 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { createClient } from "@/db/server"; +import { env } from "@/env/client"; + +export const metadata: Metadata = { + title: "Sign Up", + alternates: { + canonical: "/sign-in", + }, + icons: { + apple: "/static/favicon/apple-touch-icon.png", + other: [ + { + rel: "alternate icon", + sizes: "48x48", + url: "/static/favicon/favicon.ico", + }, + { + rel: "icon", + type: "image/svg+xml", + sizes: "any", + url: "/static/favicon/favicon.svg", + }, + { + rel: "icon", + type: "image/png", + sizes: "32x32", + url: "/static/favicon/favicon-32x32.png", + }, + { + rel: "icon", + type: "image/png", + sizes: "16x16", + url: "/static/favicon/favicon-16x16.png", + }, + { + rel: "mask-icon", + color: "#0e0813", + url: "/public/static/favicon/safari-pinned-tab.svg", + }, + ], + }, + openGraph: { + title: "Sign Up - rikhall.proj", + description: "Register an account with the NextJS template.", + url: env.NEXT_PUBLIC_BASE_URL, + images: "/next-data/og?title=Sign%20Up", + siteName: "RikardTemplate", + locale: "en_US", + type: "website", + }, + twitter: { + title: "Sign Up - rikhall.proj", + description: "Register an account with the NextJS template.", + images: "/next-data/og?title=Sign%20Up", + card: "summary_large_image", + creator: "@rikhall_", + }, +}; export default function Component() { const signUp = async (formData: FormData) => { diff --git a/app/favicon.ico b/app/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/app/favicon.ico and /dev/null differ diff --git a/app/layout.tsx b/app/layout.tsx index f0f9e62..14b186a 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,14 +2,29 @@ import { Analytics } from "@vercel/analytics/react"; import { SpeedInsights } from "@vercel/speed-insights/next"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; -import "./globals.css"; +import "./globals.css"; +import { env } from "@/env/client"; import { VERCEL_ENV } from "@/next.constants.mjs"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + metadataBase: new URL(env.NEXT_PUBLIC_BASE_URL), + title: { + template: "%s | rikhall.proj", + default: "NextJS Template | rikhall.proj", + }, + description: "This is a Next.JS template made by Rikard Hallberg.", + manifest: "/site.webmanifest", + creator: "Rikard Hallberg", + publisher: "Vercel", + referrer: "origin-when-cross-origin", + generator: "Next.js", + applicationName: "rikhall.proj", + authors: [{ name: "Rikard Hallberg", url: "https://rikardhallberg.com" }], + other: { + "msapplication-TileColor": "#0e0813", + }, }; export default function RootLayout({ diff --git a/app/next-data/og/route.tsx b/app/next-data/og/route.tsx new file mode 100644 index 0000000..c22b680 --- /dev/null +++ b/app/next-data/og/route.tsx @@ -0,0 +1,65 @@ +import { ImageResponse } from "next/og"; + +// import HexagonGrid from "@/components/Icons/HexagonGrid"; +// import JsIconWhite from "@/components/Icons/Logos/JsIconWhite"; + +import { VERCEL_ENV, VERCEL_REVALIDATE } from "@/next.constants.mjs"; + +// This is the Route Handler for the `GET` method which handles the request +// for generating OpenGrapgh images for Blog Posts and Pages +// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers +export const GET = async (request: Request) => { + const { searchParams } = new URL(request.url); + + // ?title= + const hasTitle = searchParams.has("title"); + const title = hasTitle + ? searchParams.get("title")?.slice(0, 100) + : "Next.js Project Template"; + return new ImageResponse( + ( + <div tw="relative flex items-center justify-center bg-black w-[1200px] h-[600px]"> + <div tw="absolute mx-auto flex max-w-xl flex-col text-center items-center text-3xl font-semibold text-white"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="128" + height="128" + fill="none" + > + <path + fill="currentColor" + fillRule="evenodd" + d="M0 0h14c2.122 0 4.157 1.054 5.657 2.929C21.157 4.804 22 7.348 22 10s-.843 5.196-2.343 7.071S16.122 20 14 20l2.667 4h4.668L19 19.997c1.227-.914 2.402-2.063 3.477-4.286L32 31.999h-6L23.668 28h-4.335L22 32h-6L8 20H6v12H0V0Zm12 14a4 4 0 0 0 0-8H6v8h6Z" + clipRule="evenodd" + /> + </svg> + <h1 tw="font-bold" style={{ fontWeight: 700 }}> + {title} + </h1> + </div> + </div> + ), + { + width: 1200, + height: 600, + } + ); +}; + +// We want to use `edge` runtime when using Vercel +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#runtime +export const runtime = VERCEL_ENV ? "edge" : "nodejs"; + +// In this case we want to catch-all possible requests. This ensures that we always generate and +// serve the OpenGrapgh images independently on the locale +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams +export const dynamicParams = true; + +// Enforces that this route is used as static rendering +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic +export const dynamic = "auto"; + +// Ensures that this endpoint is invalidated and re-executed every X minutes +// so that when new deployments happen, the data is refreshed +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate +export const revalidate = VERCEL_REVALIDATE; diff --git a/app/page.tsx b/app/page.tsx index 5e09f81..0d3e210 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,66 @@ +import type { Metadata } from "next"; import Image from "next/image"; +import { env } from "@/env/client"; + +export const metadata: Metadata = { + title: "NextJS Template", + alternates: { + canonical: "/", + }, + icons: { + apple: "/static/favicon/apple-touch-icon.png", + other: [ + { + rel: "alternate icon", + sizes: "48x48", + url: "/static/favicon/favicon.ico", + }, + { + rel: "icon", + type: "image/svg+xml", + sizes: "any", + url: "/static/favicon/favicon.svg", + }, + { + rel: "icon", + type: "image/png", + sizes: "32x32", + url: "/static/favicon/favicon-32x32.png", + }, + { + rel: "icon", + type: "image/png", + sizes: "16x16", + url: "/static/favicon/favicon-16x16.png", + }, + { + rel: "mask-icon", + color: "#0e0813", + url: "/public/static/favicon/safari-pinned-tab.svg", + }, + ], + }, + openGraph: { + title: "NextJS Template - rikhall.proj", + description: + "This is a Next.JS template designed and made by Rikard Hallberg.", + url: env.NEXT_PUBLIC_BASE_URL, + images: "/static/og.png", + siteName: "RikardTemplate", + locale: "en_US", + type: "website", + }, + twitter: { + title: "NextJS Template - rikhall.proj", + description: + "This is a Next.JS template designed and made by Rikard Hallberg.", + images: "/static/og.png", + card: "summary_large_image", + creator: "@rikhall_", + }, +}; + export default function Home() { return ( <main className="flex min-h-screen flex-col items-center justify-between p-24"> diff --git a/app/robots.ts b/app/robots.ts new file mode 100644 index 0000000..7f1b5dc --- /dev/null +++ b/app/robots.ts @@ -0,0 +1,18 @@ +import type { MetadataRoute } from "next"; + +// This allows us to generate a `robots.txt` file dynamically based on the needs of the Node.js Website +// @see https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots +const robots = (): MetadataRoute.Robots => ({ + rules: [ + { + userAgent: "*", + disallow: ["/dashboard/"], + }, + ], +}); + +export default robots; + +// Enforces that this route is used as static rendering +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic +export const dynamic = "error"; diff --git a/app/sitemap.ts b/app/sitemap.ts new file mode 100644 index 0000000..67cb259 --- /dev/null +++ b/app/sitemap.ts @@ -0,0 +1,36 @@ +import type { MetadataRoute } from "next"; + +import { BASE_PATH, BASE_URL } from "@/next.constants.mjs"; + +// This is the combination of the Application Base URL and Base PATH +const baseUrlAndPath = `${BASE_URL}${BASE_PATH}`; + +// This allows us to generate a `sitemap.xml` file dynamically based on the needs of the Node.js Website +// Next.js Sitemap Generation doesn't support `alternate` refs yet +// @see https://github.com/vercel/next.js/discussions/55646 +const sitemap = async (): Promise<MetadataRoute.Sitemap> => { + const staticUrls = [ + "", + "legal/terms", + "legal/support-terms", + "legal/sla", + "legal/privacy-policy", + "sign-in", + "sign-up", + "about", + "help", + ]; + const currentDate = new Date().toISOString(); + + return [...staticUrls].map((route) => ({ + url: `${baseUrlAndPath}${route}`, + lastModified: currentDate, + changeFrequency: "always", + })); +}; + +export default sitemap; + +// Enforces that this route is used as static rendering +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic +export const dynamic = "error"; diff --git a/next.constants.mjs b/next.constants.mjs index deb33fa..331c264 100644 --- a/next.constants.mjs +++ b/next.constants.mjs @@ -45,3 +45,13 @@ export const THEME_STORAGE_KEY = "theme"; * Note that this is a custom Environment Variable that can be defined by us when necessary */ export const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH || ""; + +/** + * This is used for defining a default time of when `next-data` and other dynamically generated + * but static-enabled pages should be regenerated. + * + * Note that this is a custom Environment Variable that can be defined by us when necessary + */ +export const VERCEL_REVALIDATE = Number( + process.env.NEXT_PUBLIC_VERCEL_REVALIDATE_TIME || 300 +); diff --git a/public/browserconfig.xml b/public/browserconfig.xml new file mode 100644 index 0000000..38fd0ab --- /dev/null +++ b/public/browserconfig.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<browserconfig> + <msapplication> + <tile> + <square150x150logo src="/static/favicon/mstile-150x150.png"/> + <TileColor>#0e0813</TileColor> + </tile> + </msapplication> +</browserconfig> diff --git a/public/security.txt b/public/security.txt new file mode 100644 index 0000000..cd1e69b --- /dev/null +++ b/public/security.txt @@ -0,0 +1,2 @@ +Contact: r15.hallberg@gmail.com +Policy: https://github.com/rikhall1515/nextjs-project-template/security \ No newline at end of file diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..e6648e6 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,23 @@ +{ + "name": "NextJS Template - rikhall.proj", + "short_name": "rikhall1515_template", + "icons": [ + { + "src": "/static/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "/static/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable any" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#0e0813", + "background_color": "#0e0813", + "description": "This is a Next.JS template designed and made by Rikard Hallberg." +} diff --git a/public/static/favicon/android-chrome-192x192.png b/public/static/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..96db05b Binary files /dev/null and b/public/static/favicon/android-chrome-192x192.png differ diff --git a/public/static/favicon/android-chrome-512x512.png b/public/static/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..b2254ea Binary files /dev/null and b/public/static/favicon/android-chrome-512x512.png differ diff --git a/public/static/favicon/apple-touch-icon.png b/public/static/favicon/apple-touch-icon.png new file mode 100644 index 0000000..9836efb Binary files /dev/null and b/public/static/favicon/apple-touch-icon.png differ diff --git a/public/static/favicon/favicon-16x16.png b/public/static/favicon/favicon-16x16.png new file mode 100644 index 0000000..518ef32 Binary files /dev/null and b/public/static/favicon/favicon-16x16.png differ diff --git a/public/static/favicon/favicon-32x32.png b/public/static/favicon/favicon-32x32.png new file mode 100644 index 0000000..9ca9614 Binary files /dev/null and b/public/static/favicon/favicon-32x32.png differ diff --git a/public/static/favicon/favicon.ico b/public/static/favicon/favicon.ico new file mode 100644 index 0000000..5884ad7 Binary files /dev/null and b/public/static/favicon/favicon.ico differ diff --git a/public/static/favicon/favicon.svg b/public/static/favicon/favicon.svg new file mode 100644 index 0000000..2b25e01 --- /dev/null +++ b/public/static/favicon/favicon.svg @@ -0,0 +1,30 @@ +<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0_1215_1294)"> +<rect width="50" height="50" rx="4" fill="#0E0813"/> +<rect width="50" height="50" rx="4" fill="url(#paint0_radial_1215_1294)" fill-opacity="0.2"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M9 9H15H22.9993C22.9995 9 22.9997 9 23 9C25.1215 9.00021 27.156 10.0537 28.6562 11.9288C30.1565 13.8041 30.9993 16.3475 30.9993 18.9996C30.9993 21.6516 30.1565 24.1951 28.6562 26.0703C27.156 27.9454 25.1215 28.9989 23.0001 28.9991L25.6669 32.9993H30.1251L27.8518 29.2684C29.0784 28.3545 30.4143 27.1554 31.469 24.9484L41.0002 41.0001H35.0003L32.4996 36.8962V36.9991H28.3336L31 40.9987H25.0001L17.0001 28.9992V28.9989H15V40.9986H9V28.9989V22.9992V14.9997V9ZM22.9993 22.9992V22.9988H20.9992C23.2083 22.9987 24.9992 21.208 24.9992 18.9989C24.9992 16.8142 23.2475 15.0385 21.0719 14.9997H15V22.9992H22.9993Z" fill="white"/> +<g filter="url(#filter0_d_1215_1294)"> +<rect x="22" y="22" width="28" height="28" rx="14" fill="#0E0813"/> +<path d="M28 37H34C34.2652 37 34.5196 36.8946 34.7071 36.7071C34.8946 36.5196 35 36.2652 35 36V28C35 27.7348 34.8946 27.4804 34.7071 27.2929C34.5196 27.1054 34.2652 27 34 27H28C27.7348 27 27.4804 27.1054 27.2929 27.2929C27.1054 27.4804 27 27.7348 27 28V36C27 36.2652 27.1054 36.5196 27.2929 36.7071C27.4804 36.8946 27.7348 37 28 37ZM27 44C27 44.2652 27.1054 44.5196 27.2929 44.7071C27.4804 44.8946 27.7348 45 28 45H34C34.2652 45 34.5196 44.8946 34.7071 44.7071C34.8946 44.5196 35 44.2652 35 44V40C35 39.7348 34.8946 39.4804 34.7071 39.2929C34.5196 39.1054 34.2652 39 34 39H28C27.7348 39 27.4804 39.1054 27.2929 39.2929C27.1054 39.4804 27 39.7348 27 40V44ZM37 44C37 44.2652 37.1054 44.5196 37.2929 44.7071C37.4804 44.8946 37.7348 45 38 45H44C44.2652 45 44.5196 44.8946 44.7071 44.7071C44.8946 44.5196 45 44.2652 45 44V37C45 36.7348 44.8946 36.4804 44.7071 36.2929C44.5196 36.1054 44.2652 36 44 36H38C37.7348 36 37.4804 36.1054 37.2929 36.2929C37.1054 36.4804 37 36.7348 37 37V44ZM38 34H44C44.2652 34 44.5196 33.8946 44.7071 33.7071C44.8946 33.5196 45 33.2652 45 33V28C45 27.7348 44.8946 27.4804 44.7071 27.2929C44.5196 27.1054 44.2652 27 44 27H38C37.7348 27 37.4804 27.1054 37.2929 27.2929C37.1054 27.4804 37 27.7348 37 28V33C37 33.2652 37.1054 33.5196 37.2929 33.7071C37.4804 33.8946 37.7348 34 38 34Z" fill="white"/> +</g> +</g> +<defs> +<filter id="filter0_d_1215_1294" x="15.5833" y="15.5833" width="40.8333" height="40.8333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset/> +<feGaussianBlur stdDeviation="3.20833"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.56 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1215_1294"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1215_1294" result="shape"/> +</filter> +<radialGradient id="paint0_radial_1215_1294" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(25 25) rotate(90) scale(39.566)"> +<stop stop-color="#7013E6"/> +<stop offset="1" stop-opacity="0"/> +</radialGradient> +<clipPath id="clip0_1215_1294"> +<rect width="50" height="50" rx="4" fill="white"/> +</clipPath> +</defs> +</svg> diff --git a/public/static/favicon/mstile-144x144.png b/public/static/favicon/mstile-144x144.png new file mode 100644 index 0000000..b33d374 Binary files /dev/null and b/public/static/favicon/mstile-144x144.png differ diff --git a/public/static/favicon/mstile-150x150.png b/public/static/favicon/mstile-150x150.png new file mode 100644 index 0000000..cfd967d Binary files /dev/null and b/public/static/favicon/mstile-150x150.png differ diff --git a/public/static/favicon/mstile-310x150.png b/public/static/favicon/mstile-310x150.png new file mode 100644 index 0000000..d07ab10 Binary files /dev/null and b/public/static/favicon/mstile-310x150.png differ diff --git a/public/static/favicon/mstile-310x310.png b/public/static/favicon/mstile-310x310.png new file mode 100644 index 0000000..fe70f67 Binary files /dev/null and b/public/static/favicon/mstile-310x310.png differ diff --git a/public/static/favicon/mstile-70x70.png b/public/static/favicon/mstile-70x70.png new file mode 100644 index 0000000..ed9ab29 Binary files /dev/null and b/public/static/favicon/mstile-70x70.png differ diff --git a/public/static/favicon/safari-pinned-tab.svg b/public/static/favicon/safari-pinned-tab.svg new file mode 100644 index 0000000..c01faee --- /dev/null +++ b/public/static/favicon/safari-pinned-tab.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<svg version="1.0" xmlns="http://www.w3.org/2000/svg" + width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000" + preserveAspectRatio="xMidYMid meet"> +<metadata> +Created by potrace 1.14, written by Peter Selinger 2001-2017 +</metadata> +<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)" +fill="#000000" stroke="none"> +<path d="M440 6984 c-162 -35 -305 -147 -380 -299 -64 -129 -60 94 -58 -3217 +l3 -3013 22 -66 c58 -169 200 -309 368 -362 l70 -22 3035 0 3035 0 70 22 c171 +54 314 197 368 368 l22 70 0 3035 0 3035 -22 70 c-53 168 -193 310 -362 368 +l-66 22 -3020 2 c-2569 1 -3030 -1 -3085 -13z m2931 -1259 c430 -75 781 -442 +913 -955 42 -164 51 -236 50 -430 0 -205 -13 -303 -65 -482 l-32 -108 -91 -46 +c-287 -145 -550 -378 -729 -646 l-72 -107 -62 -7 -62 -6 31 -45 c35 -52 37 +-42 -32 -207 -78 -191 -125 -410 -138 -652 l-7 -136 -348 521 -347 521 -140 0 +-140 0 0 -840 0 -840 -420 0 -420 0 0 2240 0 2240 1014 0 c845 0 1027 -2 1097 +-15z m1457 -2525 c18 -11 41 -34 52 -52 19 -32 20 -52 20 -628 0 -576 -1 -596 +-20 -628 -11 -18 -34 -41 -52 -52 -32 -19 -52 -20 -488 -20 -436 0 -456 1 +-488 20 -18 11 -41 34 -52 52 -19 32 -20 52 -20 628 0 576 1 596 20 628 11 18 +34 41 52 52 32 19 52 20 488 20 436 0 456 -1 488 -20z m1400 0 c18 -11 41 -34 +52 -52 19 -31 20 -51 20 -418 0 -367 -1 -387 -20 -418 -11 -18 -34 -41 -52 +-52 -32 -19 -52 -20 -488 -20 -436 0 -456 1 -488 20 -18 11 -41 34 -52 52 -19 +31 -20 51 -20 418 0 367 1 387 20 418 11 18 34 41 52 52 32 19 52 20 488 20 +436 0 456 -1 488 -20z m0 -1260 c18 -11 41 -34 52 -52 19 -32 20 -52 20 -558 +0 -506 -1 -526 -20 -558 -11 -18 -34 -41 -52 -52 -32 -19 -52 -20 -488 -20 +-436 0 -456 1 -488 20 -18 11 -41 34 -52 52 -19 32 -20 52 -20 558 0 506 1 +526 20 558 11 18 34 41 52 52 32 19 52 20 488 20 436 0 456 -1 488 -20z +m-1400 -420 c18 -11 41 -34 52 -52 19 -31 20 -51 20 -348 0 -297 -1 -317 -20 +-348 -11 -18 -34 -41 -52 -52 -32 -19 -52 -20 -488 -20 -436 0 -456 1 -488 20 +-18 11 -41 34 -52 52 -19 31 -20 51 -20 348 0 297 1 317 20 348 11 18 34 41 +52 52 32 19 52 20 488 20 436 0 456 -1 488 -20z"/> +<path d="M2100 4340 l0 -561 478 3 c463 4 479 4 535 26 176 66 301 191 359 +360 30 88 32 249 4 332 -31 91 -75 166 -136 229 -65 66 -121 102 -219 140 +l-66 26 -477 3 -478 3 0 -561z"/> +</g> +</svg> diff --git a/public/static/og.png b/public/static/og.png new file mode 100644 index 0000000..179c797 Binary files /dev/null and b/public/static/og.png differ