Compare commits
89 Commits
main
..
4c3e35efbf
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c3e35efbf | |||
| dccd816f6e | |||
| d57cded06d | |||
| 57ef1426ee | |||
| e68f433f1e | |||
| 6f433e08ce | |||
| f55ae8a267 | |||
| e5726437ed | |||
| 08c852a9e8 | |||
| 91d0e55d3c | |||
| de16fcea56 | |||
| f9c5d4c421 | |||
| f1c6bfdf87 | |||
| d1d6867130 | |||
| e41ef2fceb | |||
| 146c8d5279 | |||
| 187fd302fb | |||
| 44c03ff4c6 | |||
| 3216d79bd0 | |||
| cf26695719 | |||
| d1e4e93e4a | |||
| 6fb457c7ce | |||
| 13f3312bb2 | |||
| f9833fa5eb | |||
| cf74b0f7ef | |||
| 93bb879baf | |||
| 015acaf14d | |||
| f49a25cfc8 | |||
| 21eb2e8c0c | |||
| 1cdce4c223 | |||
| 94b60be9b6 | |||
| 2df825187e | |||
| c513605de6 | |||
| 0f1ece8a3e | |||
| db17e56559 | |||
| 5e7a63230b | |||
| dcd19b6da0 | |||
| 4b0f214568 | |||
| 5b11664a3f | |||
| ff1188b4b2 | |||
| 7c331406a5 | |||
| 0ab1e1068d | |||
| f76477db98 | |||
| 96c053d5ff | |||
| 68431e6b9c | |||
| f86661bbba | |||
| 5d59e2171d | |||
| 82eb0657e2 | |||
| 613ab7aef9 | |||
| 38a7f83096 | |||
| 74ab286f15 | |||
| 67d82f05f1 | |||
| fe1c2cc218 | |||
| 3a8a3987b1 | |||
| d1c4d505fe | |||
| 7b7e02ad28 | |||
| f76f3dd96e | |||
| 2a09d65863 | |||
| ee391e128f | |||
| f9072c3cfc | |||
| 3ee3879db7 | |||
| 1e04a7be6f | |||
| d025f7e01b | |||
| 46230f2140 | |||
| 4c1a6ed9f6 | |||
| d1a235369d | |||
| f52a13171c | |||
| feb9b21ff8 | |||
| 58b74bb801 | |||
| 5e931f15d3 | |||
| 6168a386fa | |||
| ced2d230a3 | |||
| 54ac6555c4 | |||
| 31864f616a | |||
| da8a16c416 | |||
| ba4a3c0af2 | |||
| 9105249c87 | |||
| e02321f5c3 | |||
| fe3454f046 | |||
| 116ea85df7 | |||
| 59d6095278 | |||
| 91e60bb090 | |||
| e4b941a1dc | |||
| dba9d51133 | |||
| e800314589 | |||
| 93baa3b6b0 | |||
| d4128840b9 | |||
| 31b24de86c | |||
| f0129ecc76 |
@@ -3,7 +3,3 @@
|
||||
*.gif filter=lfs diff=lfs merge=lfs -text
|
||||
*.woff2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
||||
*.JPEG filter=lfs diff=lfs merge=lfs -text
|
||||
*.webm filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
@@ -19,31 +19,41 @@ jobs:
|
||||
|
||||
- name: 🔄 Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
lfs: true
|
||||
|
||||
- name: 🔢 Prepare cache keys
|
||||
- name: 🔢 Calculate cache IDs
|
||||
run: |
|
||||
# Calculate cache IDs for Git LFS and PNPM
|
||||
git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
|
||||
LFS_CACHE_ID=$(cat .lfs-assets-id | md5sum)-v1
|
||||
PNPM_CACHE_ID=$(cat pnpm-lock.yaml | md5sum)-v1
|
||||
echo "LFS_CACHE_ID=$LFS_CACHE_ID" >> $GITHUB_ENV
|
||||
echo "PNPM_STORE_PATH=$(pnpm store path)" >> $GITHUB_ENV
|
||||
echo "PNPM_CACHE_ID=$PNPM_CACHE_ID" >> $GITHUB_ENV
|
||||
|
||||
- name: 🗄️ Cache Git LFS objects
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .git/lfs
|
||||
key: ${{ runner.os }}-lfs-${{ env.LFS_CACHE_ID }}
|
||||
|
||||
- name: 🛠️ Cache PNPM dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.PNPM_STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: 📷 Cache Astro Images
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules/.astro
|
||||
key: ${{ runner.os }}-astro-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-astro-
|
||||
key: ${{ runner.os }}-astro-v1
|
||||
|
||||
- name: 🔄 Pull Git LFS files
|
||||
run: git lfs pull
|
||||
|
||||
- name: 🏗️ Build site
|
||||
run: |
|
||||
# Install dependencies, build, and generate site output
|
||||
pnpm i && pnpm build
|
||||
|
||||
- name: 🔑 Configure rclone
|
||||
@@ -61,8 +71,8 @@ jobs:
|
||||
- name: 🚀 Deploy Changed Files via rclone
|
||||
run: |
|
||||
echo "Uploading _astro assets"
|
||||
rclone sync --update -v --progress --size-only --fast-list --stats 2s --stats-one-line ./dist/_astro sftp-remote:${REMOTE_DIR}/_astro --transfers 4
|
||||
rclone sync --update -v --progress --size-only --stats 2s --stats-one-line ./dist/_astro sftp-remote:${REMOTE_DIR}/_astro --transfers 4
|
||||
echo "Uploading the rest"
|
||||
rclone sync --update -v --progress --exclude _astro/** --fast-list --stats 2s --stats-one-line ./dist/ sftp-remote:${REMOTE_DIR} --transfers 4
|
||||
rclone sync --update -v --progress --exclude _astro/** --stats 2s --stats-one-line ./dist/ sftp-remote:${REMOTE_DIR} --transfers 4
|
||||
env:
|
||||
REMOTE_DIR: ${{ vars.REMOTE_DIR }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:25-alpine
|
||||
FROM node:21-alpine
|
||||
|
||||
# Install necessary packages
|
||||
RUN apk add --no-cache \
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { defineConfig } from "astro/config";
|
||||
import { filterSitemapByDefaultLocale, i18n } from "astro-i18n-aut/integration";
|
||||
import { defineConfig } from 'astro/config';
|
||||
import { i18n, filterSitemapByDefaultLocale } from "astro-i18n-aut/integration";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
import Icons from "unplugin-icons/vite";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import glsl from "vite-plugin-glsl";
|
||||
import Icons from 'unplugin-icons/vite'
|
||||
import mdx from '@astrojs/mdx';
|
||||
import glsl from 'vite-plugin-glsl';
|
||||
|
||||
import svelte from "@astrojs/svelte";
|
||||
import UnoCSS from "unocss/astro";
|
||||
import UnoCSS from 'unocss/astro'
|
||||
|
||||
const defaultLocale = "de";
|
||||
const locales = {
|
||||
@@ -14,7 +14,7 @@ const locales = {
|
||||
de: "de",
|
||||
};
|
||||
|
||||
const DEFAULT_LAYOUT = "@layouts/Post.astro";
|
||||
const DEFAULT_LAYOUT = '@layouts/Post.astro';
|
||||
|
||||
function setDefaultLayout() {
|
||||
return function (_, file) {
|
||||
@@ -23,13 +23,11 @@ function setDefaultLayout() {
|
||||
};
|
||||
}
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: "https://max-richter.dev",
|
||||
trailingSlash: "never",
|
||||
prefetch: true,
|
||||
image: {
|
||||
remotePatterns: [{ protocol: "https" }],
|
||||
},
|
||||
build: {
|
||||
format: "file",
|
||||
},
|
||||
@@ -37,28 +35,23 @@ export default defineConfig({
|
||||
plugins: [
|
||||
glsl(),
|
||||
Icons({
|
||||
compiler: "svelte",
|
||||
compiler: 'svelte',
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
watch: {
|
||||
ignored: ["**/node_modules/**", "**/dist/**", "**/.git/**"],
|
||||
},
|
||||
},
|
||||
},
|
||||
markdown: {
|
||||
remarkPlugins: [setDefaultLayout],
|
||||
remarkPlugins: [setDefaultLayout]
|
||||
},
|
||||
integrations: [
|
||||
i18n({
|
||||
exclude: ["pages/**/*.json.ts", "pages/api/**/*"],
|
||||
exclude: ["pages/**/*.json.ts", "pages/api/**/*",],
|
||||
locales,
|
||||
defaultLocale,
|
||||
}),
|
||||
mdx(),
|
||||
svelte(),
|
||||
UnoCSS({
|
||||
injectReset: true,
|
||||
injectReset: true
|
||||
}),
|
||||
sitemap({
|
||||
i18n: {
|
||||
@@ -67,5 +60,5 @@ export default defineConfig({
|
||||
},
|
||||
filter: filterSitemapByDefaultLocale({ defaultLocale }),
|
||||
}),
|
||||
],
|
||||
]
|
||||
});
|
||||
|
||||
@@ -10,31 +10,33 @@
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.6",
|
||||
"@astrojs/mdx": "^4.3.13",
|
||||
"@astrojs/svelte": "^7.2.4",
|
||||
"astro": "^5.16.6",
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/mdx": "^4.0.8",
|
||||
"@astrojs/svelte": "^7.0.4",
|
||||
"@astrojs/tailwind": "^6.0.0",
|
||||
"astro": "^5.3.0",
|
||||
"astro-i18n-aut": "^0.7.3",
|
||||
"exifreader": "^4.33.1",
|
||||
"svelte": "^5.46.1",
|
||||
"svelte-gestures": "^5.2.2",
|
||||
"exifreader": "^4.26.1",
|
||||
"svelte": "^5.20.1",
|
||||
"svelte-gestures": "^5.1.3",
|
||||
"tailwindcss": "^4.0.6",
|
||||
"thumbhash": "^0.1.1",
|
||||
"typescript": "^5.9.3"
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/sitemap": "^3.6.0",
|
||||
"@iconify-json/tabler": "^1.2.26",
|
||||
"@astrojs/sitemap": "^3.2.1",
|
||||
"@iconify-json/tabler": "^1.2.16",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@unocss/preset-icons": "^66.5.12",
|
||||
"@unocss/reset": "^66.5.12",
|
||||
"astro-font": "^1.1.0",
|
||||
"@unocss/preset-icons": "^65.5.0",
|
||||
"@unocss/reset": "^65.5.0",
|
||||
"astro-font": "^1.0.0",
|
||||
"markdown-it": "^14.1.0",
|
||||
"ogl": "^1.0.11",
|
||||
"prettier": "^3.7.4",
|
||||
"prettier": "^3.5.1",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"sharp": "^0.34.5",
|
||||
"unocss": "^66.5.12",
|
||||
"unplugin-icons": "^22.5.0",
|
||||
"vite-plugin-glsl": "^1.5.5"
|
||||
"sharp": "^0.33.5",
|
||||
"unocss": "^65.5.0",
|
||||
"unplugin-icons": "^22.0.0",
|
||||
"vite-plugin-glsl": "^1.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- exifreader
|
||||
- sharp
|
||||
@@ -1,43 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let searchTerm = "";
|
||||
let staticContainer: HTMLElement | null;
|
||||
|
||||
onMount(async () => {
|
||||
staticContainer = document.getElementById("resource-list-static");
|
||||
});
|
||||
|
||||
function search() {
|
||||
if (!staticContainer) return;
|
||||
|
||||
const lowerCaseSearchTerm = searchTerm.toLowerCase().trim();
|
||||
|
||||
if (!lowerCaseSearchTerm) {
|
||||
[...staticContainer?.children].forEach((element: HTMLElement) => {
|
||||
element.style.display = "";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
for (const element of staticContainer?.children) {
|
||||
const el = element as HTMLElement;
|
||||
const isMatch = el.dataset.searchTerm?.includes(lowerCaseSearchTerm);
|
||||
if (isMatch) {
|
||||
el.style.display = "";
|
||||
} else {
|
||||
el.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: search();
|
||||
</script>
|
||||
|
||||
<div class="search-wrapper my-4 noise relative">
|
||||
<input
|
||||
type="text"
|
||||
bind:value={searchTerm}
|
||||
placeholder={`Search...`}
|
||||
class="w-full p-2 gradient border-1 border-neutral rounded noise" />
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { markdownToText, readDuration } from "@helpers/markdown";
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
import { Card } from "./card";
|
||||
import { useTranslatedPath, useTranslations } from "@i18n/utils";
|
||||
import Image from "@components/Image.astro";
|
||||
@@ -15,7 +15,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const {
|
||||
data: { title, cover, icon, date, rating, author },
|
||||
data: { title, cover, icon },
|
||||
collection,
|
||||
body,
|
||||
id,
|
||||
@@ -25,34 +25,13 @@ const translatePath = useTranslatedPath(Astro.url);
|
||||
const t = useTranslations(Astro.url);
|
||||
|
||||
const link = translatePath(`/${collection}/${id.split("/")[0]}`);
|
||||
|
||||
const image = cover as unknown;
|
||||
|
||||
const hasCover = typeof image === "string" ? !!image?.length : !!cover?.src;
|
||||
---
|
||||
|
||||
<Card
|
||||
classes={`grid gradient border-1 border-neutral overflow-hidden ${hasCover ? "grid-rows-[200px_1fr] xs:grid-rows-none xs:grid-cols-[1fr_200px]" : ""}`}>
|
||||
classes={`grid gradient border-1 border-neutral overflow-hidden ${cover ? "grid-rows-[200px_1fr] xs:grid-rows-none xs:grid-cols-[1fr_200px]" : ""}`}>
|
||||
<Card.Content classes="px-8 py-7 order-last xs:order-first">
|
||||
{
|
||||
(date || body || rating !== undefined) && (
|
||||
<Card.Metadata
|
||||
date={date}
|
||||
readDuration={readDuration(body)}
|
||||
rating={rating}
|
||||
author={author}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<Card.Title classes="text-4xl flex items-center gap-2">
|
||||
{
|
||||
icon &&
|
||||
(icon?.length > 5 ? (
|
||||
<img class="h-6 w-6" src={icon} />
|
||||
) : (
|
||||
<span class="p-r-4 text-md">{icon}</span>
|
||||
))
|
||||
}
|
||||
{icon && <img src={icon} class="h-6 w-6" />}
|
||||
{title}
|
||||
</Card.Title>
|
||||
<Card.Description>
|
||||
@@ -61,16 +40,14 @@ const hasCover = typeof image === "string" ? !!image?.length : !!cover?.src;
|
||||
<Card.ReadMoreButton link={link} text={t("read-more")} />
|
||||
</Card.Content>
|
||||
{
|
||||
hasCover && (
|
||||
cover && (
|
||||
<a href={link}>
|
||||
<Image
|
||||
hash
|
||||
loader={false}
|
||||
src={cover as ImageMetadata}
|
||||
src={cover}
|
||||
alt={"cover for " + title}
|
||||
class="h-full right-0 object-cover object-center rounded-none border-l border-neutral"
|
||||
pictureClass="h-full"
|
||||
thumbnail
|
||||
class="right-0 h-full object-cover object-center rounded-none border-l border-neutral"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
---
|
||||
import type { ImageMetadata } from "astro";
|
||||
import { Picture as AstroImage } from "astro:assets";
|
||||
import { inferRemoteSize } from "astro/assets/utils";
|
||||
import { getProcessedImage } from "@helpers/image";
|
||||
import { generateThumbHash, getExifData } from "@helpers/image";
|
||||
interface Props {
|
||||
src: ImageMetadata & { fsPath?: string; src?: string };
|
||||
src: ImageMetadata;
|
||||
alt: string;
|
||||
pictureClass?: string;
|
||||
class?: string;
|
||||
@@ -12,29 +11,6 @@ interface Props {
|
||||
hash?: boolean;
|
||||
loader?: boolean;
|
||||
maxWidth?: number;
|
||||
thumbnail?: boolean;
|
||||
}
|
||||
|
||||
async function checkImage(
|
||||
image: ImageMetadata,
|
||||
): Promise<{ height: number; width: number } | undefined> {
|
||||
const src = typeof image === "string" ? image : image.src;
|
||||
if (!src) return;
|
||||
try {
|
||||
if (src.startsWith("/@fs") || src.startsWith("/_astro")) return image;
|
||||
const res = await inferRemoteSize(src);
|
||||
if (res.format) {
|
||||
image.format = res.format;
|
||||
return res;
|
||||
} else {
|
||||
console.log("Failed to load: ", src);
|
||||
}
|
||||
return;
|
||||
} catch (err) {
|
||||
console.log("\n");
|
||||
console.log("Failed to fetch: ", src);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -44,16 +20,13 @@ const {
|
||||
hash = true,
|
||||
alt,
|
||||
maxWidth,
|
||||
thumbnail = false,
|
||||
} = Astro.props;
|
||||
|
||||
const imageOk = await checkImage(image);
|
||||
let thumbhash = hash ? await generateThumbHash(image) : "";
|
||||
|
||||
const { thumbhash, exif } = imageOk
|
||||
? await getProcessedImage(image)
|
||||
: { thumbhash: undefined, exif: undefined };
|
||||
let exif = await getExifData(image);
|
||||
|
||||
const definedSizes = [
|
||||
const sizes = [
|
||||
{
|
||||
width: 240,
|
||||
media: "(max-width: 360px)",
|
||||
@@ -69,33 +42,21 @@ const definedSizes = [
|
||||
{
|
||||
width: image.width,
|
||||
},
|
||||
];
|
||||
|
||||
const sizes = thumbnail
|
||||
? [definedSizes[1]]
|
||||
: definedSizes.filter((size) => !maxWidth || size.width <= maxWidth);
|
||||
].filter((size) => !maxWidth || size.width <= maxWidth);
|
||||
---
|
||||
|
||||
{
|
||||
imageOk ? (
|
||||
<AstroImage
|
||||
src={image}
|
||||
alt={alt}
|
||||
data-thumbhash={thumbhash}
|
||||
data-exif={JSON.stringify(exif)}
|
||||
width={imageOk?.width}
|
||||
height={imageOk?.height}
|
||||
pictureAttributes={{
|
||||
class: `${hash ? "block h-full relative" : ""} ${loader ? "thumb" : ""} ${pictureClass}`,
|
||||
}}
|
||||
class={`${Astro.props.class} w-full`}
|
||||
widths={sizes.map((size) => size.width)}
|
||||
sizes={sizes
|
||||
.map((size) => `${size.media || "100vw"} ${size.width}px`)
|
||||
.join(", ")}>
|
||||
<slot />
|
||||
</AstroImage>
|
||||
) : (
|
||||
<div>{JSON.stringify({ imageOk: imageOk, image })}</div>
|
||||
)
|
||||
}
|
||||
<AstroImage
|
||||
src={image}
|
||||
alt={alt}
|
||||
data-thumbhash={thumbhash}
|
||||
data-exif={JSON.stringify(exif)}
|
||||
pictureAttributes={{
|
||||
class: `${hash ? "block h-full relative" : ""} ${loader ? "thumb" : ""} ${pictureClass}`,
|
||||
}}
|
||||
class={Astro.props.class}
|
||||
widths={sizes.map((size) => size.width)}
|
||||
sizes={sizes
|
||||
.map((size) => `${size.media || "100vw"} ${size.width}px`)
|
||||
.join(", ")}>
|
||||
<slot />
|
||||
</AstroImage>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
let progress: number[] = [];
|
||||
let currentIndex = -1;
|
||||
const maxZoom = 5;
|
||||
import { useSwipe } from "svelte-gestures";
|
||||
import { swipe } from "svelte-gestures";
|
||||
|
||||
const mod = (a: number, b: number) => ((a % b) + b) % b;
|
||||
|
||||
@@ -218,9 +218,7 @@
|
||||
{#each images as _, i}
|
||||
<button
|
||||
aria-label={`Image ${i + 1}`}
|
||||
class="rounded bg-light"
|
||||
class:active={currentIndex === i}
|
||||
class:bg-light={currentIndex === i}
|
||||
on:click={() => {
|
||||
currentIndex = i;
|
||||
}}></button>
|
||||
@@ -228,30 +226,16 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="left flex-1 bg-light"
|
||||
aria-label="previous image"
|
||||
on:click={() => addIndex(- 1)}>
|
||||
<span class="i-tabler-arrow-left" />
|
||||
</button>
|
||||
<button
|
||||
class="right flex-1 bg-light"
|
||||
aria-label="next image"
|
||||
on:click={() => addIndex( + 1)}>
|
||||
<span class="i-tabler-arrow-right" />
|
||||
</button>
|
||||
<button
|
||||
class="close bg-light"
|
||||
aria-label="close gallery"
|
||||
on:click={() => setIndex(-1)}>
|
||||
<span class="i-tabler-x" />
|
||||
</button>
|
||||
<button class="left" on:click={() => addIndex(-1)}><</button>
|
||||
<button class="right" on:click={() => addIndex(+1)}>></button>
|
||||
<button class="close" on:click={() => setIndex(-1)}>X</button>
|
||||
|
||||
{#if currentIndex > -1}
|
||||
<div
|
||||
{...useSwipe(handleSwipe)}
|
||||
class="image"
|
||||
use:swipe
|
||||
role="dialog"
|
||||
on:swipe={handleSwipe}
|
||||
on:wheel|passive={handleScroll}
|
||||
on:mousemove={handleMouseMove}
|
||||
on:pointermove={handlePointerMove}>
|
||||
@@ -295,7 +279,6 @@
|
||||
transform-origin: left;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-wrapper {
|
||||
position: fixed;
|
||||
z-index: 199;
|
||||
@@ -393,31 +376,27 @@
|
||||
}
|
||||
|
||||
.gallery-wrapper > button {
|
||||
border-radius: 6px;
|
||||
padding: 0;
|
||||
background: var(--neutral-800);
|
||||
color: white;
|
||||
border-radius: 0px;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
font-size: 1.5rem;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.close {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.left {
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.right {
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,28 +8,23 @@
|
||||
let height: number;
|
||||
let loaded = false;
|
||||
|
||||
function hide(index: number) {
|
||||
const img = images[index];
|
||||
function hide(img: HTMLPictureElement) {
|
||||
img.classList.remove("active");
|
||||
}
|
||||
|
||||
const heightCache = [];
|
||||
function show(index: number) {
|
||||
const img = images[index];
|
||||
function show(img: HTMLPictureElement) {
|
||||
img.classList.add("active");
|
||||
const _img = img.querySelector("img") || img;
|
||||
const _img = img.querySelector("img");
|
||||
if (!_img) return;
|
||||
_img.addEventListener("load", () => {
|
||||
img.classList.remove("thumb-loading");
|
||||
_img.style.opacity = "1";
|
||||
});
|
||||
altText = _img["alt"] ?? _img.getAttribute("alt") ?? "";
|
||||
if (heightCache[index]) {
|
||||
height = heightCache[index];
|
||||
}
|
||||
if (_img?.alt) altText = _img.alt;
|
||||
else altText = "";
|
||||
height = _img.getBoundingClientRect().height;
|
||||
setTimeout(() => {
|
||||
height = heightCache[index] ?? _img.getBoundingClientRect().height;
|
||||
heightCache[index] = height;
|
||||
height = _img.getBoundingClientRect().height;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@@ -37,16 +32,16 @@
|
||||
function setIndex(i: number) {
|
||||
if (i < 0) i = images.length - 1;
|
||||
if (i >= images.length) i = 0;
|
||||
hide(index);
|
||||
hide(images[index]);
|
||||
index = i;
|
||||
show(index);
|
||||
show(images[index]);
|
||||
}
|
||||
|
||||
$: if (slot && !images?.length) {
|
||||
images = Array.from(slot.querySelectorAll("picture"));
|
||||
if (images?.length) {
|
||||
images.forEach((_, i) => hide(i));
|
||||
show(index);
|
||||
images.forEach(hide);
|
||||
show(images[index]);
|
||||
images[index].onload = () => {
|
||||
loaded = true;
|
||||
height = images[index].getBoundingClientRect().height;
|
||||
@@ -60,27 +55,33 @@
|
||||
|
||||
<div
|
||||
class="wrapper grid overflow-hidden rounded-xl border border-neutral"
|
||||
class:title={true}
|
||||
class:title
|
||||
class:not-loaded={!loaded}
|
||||
class:loaded
|
||||
style={`--height:${height}px`}>
|
||||
<div class="flex items-center p-x-4 p-y-6 bg justify-between">
|
||||
<h3>{title}</h3>
|
||||
style={`--height:${height}px`}
|
||||
>
|
||||
{#if title}
|
||||
<div class="flex items-center p-x-4 p-y-6 bg justify-between">
|
||||
<h3>{title}</h3>
|
||||
|
||||
<div
|
||||
class="overflow-hidden rounded-md bg-light gap-2 flex p-2 border border-light">
|
||||
<button
|
||||
class="flex-1 i-tabler-arrow-left"
|
||||
aria-label="previous image"
|
||||
on:click={() => setIndex(index - 1)}></button>
|
||||
<button
|
||||
class="flex-1 i-tabler-arrow-right"
|
||||
aria-label="next image"
|
||||
on:click={() => setIndex(index + 1)}></button>
|
||||
<div
|
||||
class="overflow-hidden rounded-md bg-light gap-2 flex p-2 border border-light"
|
||||
>
|
||||
<button
|
||||
class="flex-1 i-tabler-arrow-left"
|
||||
aria-label="previous image"
|
||||
on:click={() => setIndex(index - 1)}
|
||||
/>
|
||||
<button
|
||||
class="flex-1 i-tabler-arrow-right"
|
||||
aria-label="next image"
|
||||
on:click={() => setIndex(index + 1)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="images border-block border-neutral" bind:this={slot}>
|
||||
<div class="images border-t-1 border-b-1 border-neutral" bind:this={slot}>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="px-4 flex items-center place-content-between bg">
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
interface Props {
|
||||
title: string;
|
||||
cover?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
cover,
|
||||
description = "A personal blog and portfolio by Max Richter.",
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content={Astro.url} />
|
||||
{cover && <meta property="og:image" content={cover} />}
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:site_name" content="Max Richter" />
|
||||
@@ -24,8 +24,8 @@ const paths = [
|
||||
text: t("nav.photos"),
|
||||
},
|
||||
{
|
||||
link: translatePath("/resources"),
|
||||
text: t("nav.resources"),
|
||||
link: translatePath("/videos"),
|
||||
text: t("nav.videos"),
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { markdownToText } from "@helpers/markdown";
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
import { useTranslatedPath } from "@i18n/utils";
|
||||
import type { InferEntrySchema } from "astro:content";
|
||||
|
||||
@@ -21,13 +21,7 @@ const { post } = Astro.props;
|
||||
<a href={tp(`/${post.collection}/${post.id.split("/")[0]}`)}>
|
||||
<h2
|
||||
class="text-2xl flex gap-2 items-center line-clamp text-ellipsis overflow-hidden">
|
||||
{
|
||||
post.data.icon?.length > 3 ? (
|
||||
<img src={post.data.icon} class="h-6" />
|
||||
) : post.data.icon?.length ? (
|
||||
<span>{post.data.icon}</span>
|
||||
) : null
|
||||
}
|
||||
{post.data.icon && <img src={post.data.icon} class="h-6" />}
|
||||
{post.data.title}
|
||||
</h2>
|
||||
<p class="text-ellipsis overflow-hidden line-clamp-2">
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let date: string | Date;
|
||||
export let readDuration: number | undefined;
|
||||
export let rating: string | number | undefined;
|
||||
export let author: string | undefined;
|
||||
|
||||
const toDate = (d: string | Date) =>
|
||||
typeof d === "string" ? new Date(d) : d;
|
||||
|
||||
const iso = (d: string | Date) => {
|
||||
if (!d) return "";
|
||||
const v = toDate(d);
|
||||
if (!v?.getTime) return "";
|
||||
return isNaN(v.getTime()) ? "" : v.toISOString();
|
||||
};
|
||||
|
||||
function formatRating(rating: string | number) {
|
||||
if (typeof rating === "number") {
|
||||
return "⭐".repeat(rating);
|
||||
}
|
||||
return rating;
|
||||
}
|
||||
|
||||
const formatDate = (d: string | Date) => {
|
||||
try {
|
||||
return new Intl.DateTimeFormat("de-DE", {
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}).format(toDate(d));
|
||||
} catch (err) {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if rating || date || readDuration || author}
|
||||
<div class="flex flex-wrap gap-3 wrapper">
|
||||
{#if rating}
|
||||
<div class="text-sm bg-light">{formatRating(rating)}</div>
|
||||
{/if}
|
||||
{#if date}
|
||||
<time class="text-sm bg-light" datetime={iso(date)}
|
||||
>{formatDate(date)}</time>
|
||||
{/if}
|
||||
{#if readDuration > 1}
|
||||
<div class="text-sm bg-light">{readDuration} mins read</div>
|
||||
{/if}
|
||||
{#if author}
|
||||
<div class="text-sm bg-light">{author}</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.wrapper > * {
|
||||
padding: 2px 11px;
|
||||
border-radius: 14px;
|
||||
font-size: 11px;
|
||||
opacity: 0.7;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@@ -6,6 +6,6 @@
|
||||
<a
|
||||
href={link}
|
||||
data-astro-prefetch
|
||||
class="mt-auto bg-light p-2 text-s rounded-md px-4 flex flex-0 items-center gap-2 w-fit"
|
||||
class="bg-light p-2 text-s rounded-md px-4 flex flex-0 items-center gap-2 w-fit"
|
||||
>{text}<span class="i-tabler-arrow-right inline-block w-4 h-4"></span>
|
||||
</a>
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import Wrapper from "./Wrapper.svelte";
|
||||
import Image from "./Image.svelte";
|
||||
import Content from "./Content.svelte";
|
||||
import Title from "./Title.svelte";
|
||||
import Description from "./Description.svelte";
|
||||
import ReadMoreButton from "./ReadMoreButton.svelte";
|
||||
import Metadata from "./Metadata.svelte";
|
||||
import Wrapper from './Wrapper.svelte';
|
||||
import Image from './Image.svelte';
|
||||
import Content from './Content.svelte';
|
||||
import Title from './Title.svelte';
|
||||
import Description from './Description.svelte';
|
||||
import ReadMoreButton from './ReadMoreButton.svelte';
|
||||
|
||||
const Card = Wrapper as typeof Wrapper & {
|
||||
Image: typeof Image;
|
||||
Metadata: typeof Metadata;
|
||||
Content: typeof Content;
|
||||
Title: typeof Title;
|
||||
Description: typeof Description;
|
||||
ReadMoreButton: typeof ReadMoreButton;
|
||||
};
|
||||
}
|
||||
|
||||
Card.Image = Image;
|
||||
Card.Metadata = Metadata;
|
||||
Card.Content = Content;
|
||||
Card.Title = Title;
|
||||
Card.Description = Description;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
import * as memorium from "@helpers/memorium";
|
||||
import { markdownToHtml } from "@helpers/markdown";
|
||||
import Image from "@components/Image.astro";
|
||||
import type { ImageMetadata } from "astro";
|
||||
|
||||
const { resource } = Astro.props;
|
||||
---
|
||||
|
||||
<h1 class="text-4xl">{resource?.content?.headline}</h1>
|
||||
<div>
|
||||
{
|
||||
resource?.content?.image && (
|
||||
<Image
|
||||
hash
|
||||
src={
|
||||
{ src: memorium.getImageUrl(resource.content.image) } as ImageMetadata
|
||||
}
|
||||
alt="Cover for {resource?.content?.name}"
|
||||
class="rounded-2xl overflow-hidden"
|
||||
pictureClass="rounded-2xl box-shadow"
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col gap-4"
|
||||
set:html={markdownToHtml(resource?.content?.articleBody)}
|
||||
/>
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
import { Code } from 'astro:components';
|
||||
import Recipe from "./Recipe.astro";
|
||||
import Article from "./Article.astro";
|
||||
import Review from "./Review.astro";
|
||||
const { resource } = Astro.props;
|
||||
const type = resource?.content?._type ?? "unknown";
|
||||
---
|
||||
|
||||
{type === "Recipe" && <Recipe resource={resource} />}
|
||||
{type === "Article" && <Article resource={resource} />}
|
||||
{type === "Review" && <Review resource={resource} />}
|
||||
|
||||
{
|
||||
type === "unknown" && (
|
||||
<div>
|
||||
<h3>Unknown resource type</h3>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<details >
|
||||
<summary class="flex"><span class="i-tabler-code w-6 h-6 inline"/></summary>
|
||||
<Code code={JSON.stringify(resource??"{}", null, 2)} lang="json" theme="dark-plus" />
|
||||
</details>
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
import * as memorium from "@helpers/memorium";
|
||||
import { markdownToHtml } from "@helpers/markdown";
|
||||
import Image from "@components/Image.astro";
|
||||
import type { ImageMetadata } from "astro";
|
||||
|
||||
const { resource } = Astro.props;
|
||||
const ingredients = resource?.content?.recipeIngredient || [];
|
||||
const instructions = resource?.content?.recipeInstructions || [];
|
||||
---
|
||||
|
||||
<h1 class="text-4xl">{resource?.content?.name}</h1>
|
||||
<div>
|
||||
{
|
||||
resource?.content?.image && (
|
||||
<Image
|
||||
hash
|
||||
src={
|
||||
{ src: memorium.getImageUrl(resource.content.image) } as ImageMetadata
|
||||
}
|
||||
alt="Cover for {resource?.content?.name}"
|
||||
class="rounded-2xl overflow-hidden"
|
||||
pictureClass="rounded-2xl box-shadow"
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-col gap-4 px-4"
|
||||
set:html={markdownToHtml(resource?.content?.description ?? "")}
|
||||
/>
|
||||
<h2 class="text-2xl px-4">Ingredients</h2>
|
||||
<ul class="list-disc px-10">
|
||||
{
|
||||
ingredients.filter((s:string) => !!s?.length).map((ingredient: string) => (
|
||||
<li set:html={markdownToHtml(ingredient)} />
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<h2 class="text-2xl px-4">Steps</h2>
|
||||
<ol class="list-decimal px-10">
|
||||
{
|
||||
instructions.filter((s:string) => !!s?.length).map((ingredient: string) => (
|
||||
<li set:html={markdownToHtml(ingredient)} />
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
import * as memorium from "@helpers/memorium";
|
||||
import { markdownToHtml } from "@helpers/markdown";
|
||||
import Image from "@components/Image.astro";
|
||||
import type { ImageMetadata } from "astro";
|
||||
|
||||
const { resource } = Astro.props;
|
||||
|
||||
function formatRating(rating: string | number) {
|
||||
if (typeof rating === "number") {
|
||||
return "⭐".repeat(rating);
|
||||
}
|
||||
return rating;
|
||||
}
|
||||
---
|
||||
|
||||
<div>
|
||||
{
|
||||
resource?.content?.image && (
|
||||
<Image
|
||||
hash
|
||||
src={
|
||||
{ src: memorium.getImageUrl(resource.content.image) } as ImageMetadata
|
||||
}
|
||||
alt="Cover for {resource?.content?.name}"
|
||||
class="rounded-2xl overflow-hidden"
|
||||
pictureClass="rounded-2xl w-1/2 mr-4 mb-4 float-left box-shadow"
|
||||
/>
|
||||
)
|
||||
}
|
||||
<h1 class="text-4xl mb-4">
|
||||
{resource?.content?.itemReviewed?.name || "Unknown Name"}
|
||||
</h1>
|
||||
{
|
||||
resource?.content?.reviewRating?.ratingValue !== undefined && (
|
||||
<div>{formatRating(resource?.content?.reviewRating?.ratingValue)}</div>
|
||||
)
|
||||
}
|
||||
<div set:html={markdownToHtml(resource?.content?.reviewBody)} />
|
||||
</div>
|
||||
@@ -1,41 +1,33 @@
|
||||
import { glob } from "astro/loaders";
|
||||
import { defineCollection, type ImageFunction, z } from "astro:content";
|
||||
import { glob } from 'astro/loaders';
|
||||
import { defineCollection, z, type ImageFunction } from 'astro:content';
|
||||
|
||||
const defaultSchema = ({ image }: { image: ImageFunction }) => z.object({
|
||||
title: z.string(),
|
||||
date: z.date(),
|
||||
cover: image().optional(),
|
||||
links: z.array(z.array(z.string())).optional(),
|
||||
coverAlt: z.string().optional(),
|
||||
toc: z.boolean().optional(),
|
||||
description: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
featured: z.boolean().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
_layout: z.enum(['normal', 'transparent']).optional(),
|
||||
})
|
||||
|
||||
const defaultSchema = ({ image }: { image: ImageFunction }) =>
|
||||
z.object({
|
||||
title: z.string(),
|
||||
date: z.date(),
|
||||
cover: image().optional(),
|
||||
links: z.array(z.array(z.string())).optional(),
|
||||
rating: z.union([z.string(), z.number()]).optional(),
|
||||
coverAlt: z.string().optional(),
|
||||
toc: z.boolean().optional(),
|
||||
description: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
author: z.string().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
featured: z.boolean().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
_layout: z.enum(["normal", "transparent"]).optional(),
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
"blog": defineCollection({
|
||||
loader: glob({ pattern: "**/[^_]*.{md,mdx}", base: "./src/content/blog" }),
|
||||
'blog': defineCollection({
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/blog" }),
|
||||
schema: defaultSchema,
|
||||
}),
|
||||
"projects": defineCollection({
|
||||
loader: glob({
|
||||
pattern: "**/[^_]*.{md,mdx}",
|
||||
base: "./src/content/projects",
|
||||
}),
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/projects" }),
|
||||
schema: defaultSchema,
|
||||
}),
|
||||
"photos": defineCollection({
|
||||
loader: glob({
|
||||
pattern: "**/[^_]*.{md,mdx}",
|
||||
base: "./src/content/photos",
|
||||
}),
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/photos" }),
|
||||
schema: defaultSchema,
|
||||
}),
|
||||
})
|
||||
};
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
ReferrerUrl=https://ezgif.com/gif-to-webm/ezgif-1-af9e72fcc6.gif
|
||||
HostUrl=https://ezgif.com/save/ezgif-1-78ce3365b7.webm
|
||||
@@ -0,0 +1,4 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
ReferrerUrl=https://ezgif.com/gif-to-webm/ezgif-1-74cf771d87.gif
|
||||
HostUrl=https://ezgif.com/save/ezgif-1-93f790072e.webm
|
||||
@@ -0,0 +1,4 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
ReferrerUrl=https://ezgif.com/gif-to-webm/ezgif-1-70f1c50104.gif
|
||||
HostUrl=https://ezgif.com/save/ezgif-1-28f4d917d4.webm
|
||||
@@ -2,9 +2,6 @@
|
||||
title: "Erasmus Valencia"
|
||||
date: 2022-09-02
|
||||
cover: ./images/MAX_8218 - MAX_8230.jpg
|
||||
toc: true
|
||||
icon: 🍊
|
||||
tags: ["valencia", "erasmus"]
|
||||
---
|
||||
|
||||
import Image from "@components/Image.astro"
|
||||
|
||||
@@ -3,8 +3,6 @@ title: "Erasmus Valencia"
|
||||
date: 2022-09-02
|
||||
cover: ./images/MAX_8218 - MAX_8230.jpg
|
||||
toc: true
|
||||
icon: 🍊
|
||||
tags: ["valencia", "erasmus"]
|
||||
---
|
||||
|
||||
import Image from "@components/Image.astro"
|
||||
|
||||
|
Before Width: | Height: | Size: 132 B After Width: | Height: | Size: 6.5 MiB |
|
Before Width: | Height: | Size: 132 B After Width: | Height: | Size: 6.9 MiB |
|
Before Width: | Height: | Size: 132 B After Width: | Height: | Size: 8.2 MiB |
@@ -3,9 +3,7 @@ title: Madeira
|
||||
date: 2025-02-16
|
||||
license: "CC-BY-SA:4.0"
|
||||
comments: true
|
||||
icon: 🏝️
|
||||
cover: ./images/MAX_0603.jpg
|
||||
tags: ["madeira", "travel"]
|
||||
---
|
||||
|
||||
import Image from "@components/Image.astro";
|
||||
|
||||
@@ -3,9 +3,7 @@ title: Madeira
|
||||
date: 2025-02-16
|
||||
license: "CC-BY-SA:4.0"
|
||||
comments: true
|
||||
icon: 🏝️
|
||||
cover: ./images/MAX_0603.jpg
|
||||
tags: ["madeira", "travel"]
|
||||
---
|
||||
|
||||
import Image from "@components/Image.astro";
|
||||
|
||||
@@ -3,8 +3,6 @@ title: "Peaks of the Balkans"
|
||||
date: 2024-06-19
|
||||
cover: ./images/MAX_9861.jpg
|
||||
license: "CC-BY-SA:4.0"
|
||||
icon: 🏔️
|
||||
tags: ["balkans", "travel"]
|
||||
comments: true
|
||||
---
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ title: "Peaks of the Balkans"
|
||||
date: 2024-06-19
|
||||
cover: ./images/MAX_9861.jpg
|
||||
license: "CC-BY-SA:4.0"
|
||||
icon: 🏔️
|
||||
tags: ["balkans", "hiking", "travel"]
|
||||
comments: true
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
WEBVTT
|
||||
|
||||
00:00.000 --> 00:01.000
|
||||
Voyaging
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
WEBVTT
|
||||
|
||||
00:00.000 --> 00:09.000
|
||||
Guten Morgen, liebe Familie. Guten Morgen, wir haben gerade 6.42 Uhr.
|
||||
|
||||
00:13.000 --> 00:20.000
|
||||
Das ist mein Frühstück. Das ist ein Bulgur von gestern. Kalt jetzt.
|
||||
|
||||
00:20.000 --> 00:27.000
|
||||
Hier, da drüben ist das alte Refugio. Man darf hier nur über 1600 campen.
|
||||
|
||||
00:27.000 --> 00:33.000
|
||||
Und da unten ist das neue Refugio irgendwo. Das ist die Aussicht.
|
||||
|
||||
00:33.000 --> 00:36.000
|
||||
Da hinten ist das Wolkenmeer.
|
||||
|
||||
00:36.000 --> 00:42.000
|
||||
Hinten sind die Boys oder eigentlich nur noch ein Boy, der Kilian.
|
||||
|
||||
00:42.000 --> 00:48.000
|
||||
Weil Kilian und ich ziehen jetzt alleine weiter. Der Rest will ich mehr.
|
||||
|
||||
00:48.000 --> 00:52.000
|
||||
Er hat keine Lust mehr. Die gehen jetzt runter ins Tal.
|
||||
|
||||
00:52.000 --> 00:57.000
|
||||
Wir sind alle ein bisschen verschnuppt.
|
||||
|
||||
00:57.000 --> 01:00.000
|
||||
Niki ist aber so richtig verschnuppt.
|
||||
|
||||
01:00.000 --> 01:04.000
|
||||
Und Joni hatte irgendwas mit Gottseriehtes gestern.
|
||||
|
||||
01:04.000 --> 01:09.000
|
||||
Deswegen werden Kilian und ich jetzt den Berg allein weiter bekämpfen.
|
||||
|
||||
01:10.000 --> 01:13.000
|
||||
Und für uns geht es jetzt heute nach Vega Banjo.
|
||||
|
||||
01:13.000 --> 01:18.000
|
||||
1000 Höhenmeter runter, 800 hoch.
|
||||
|
||||
01:18.000 --> 01:22.000
|
||||
Es wird heute wahrscheinlich so ein 11-Stunden-Ritt.
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
WEBVTT
|
||||
|
||||
00:00.000 --> 00:09.000
|
||||
Good morning, dear family. Good morning, it’s currently 6:42 a.m.
|
||||
|
||||
00:13.000 --> 00:20.000
|
||||
This is my breakfast. This is bulgur from yesterday. Cold now.
|
||||
|
||||
00:20.000 --> 00:27.000
|
||||
Here, over there is the old refugio. You’re only allowed to camp above 1,600 meters here.
|
||||
|
||||
00:27.000 --> 00:33.000
|
||||
And down there somewhere is the new refugio. That’s the view.
|
||||
|
||||
00:33.000 --> 00:36.000
|
||||
Back there is the sea of clouds.
|
||||
|
||||
00:36.000 --> 00:42.000
|
||||
In the back are the boys, or actually just one boy now, Kilian.
|
||||
|
||||
00:42.000 --> 00:48.000
|
||||
Because Kilian and I are continuing on alone now. The rest don’t want to continue anymore.
|
||||
|
||||
00:48.000 --> 00:52.000
|
||||
They’ve had enough. They’re heading down into the valley now.
|
||||
|
||||
00:52.000 --> 00:57.000
|
||||
We’re all a bit sniffly.
|
||||
|
||||
00:57.000 --> 01:00.000
|
||||
But Niki is really properly sick.
|
||||
|
||||
01:00.000 --> 01:04.000
|
||||
And Joni had something serious yesterday.
|
||||
|
||||
01:04.000 --> 01:09.000
|
||||
So Kilian and I are going to keep fighting our way up the mountain alone.
|
||||
|
||||
01:10.000 --> 01:13.000
|
||||
And today we’re heading to Vega Banjo.
|
||||
|
||||
01:13.000 --> 01:18.000
|
||||
1,000 meters of descent, 800 meters up.
|
||||
|
||||
01:18.000 --> 01:22.000
|
||||
Today will probably be an 11-hour grind.
|
||||
@@ -1,44 +0,0 @@
|
||||
WEBVTT
|
||||
|
||||
00:00.000 --> 00:25.000
|
||||
Mit die erste Nacht im Wald hinter uns, ja, ja, wir sind jetzt angezogen, die Schuhe haben wir jetzt für den ganzen Spaß gebraucht am Morgen, 6.30 Uhr war der Tag, 2 Stunden, das ist ein Prozess, es ist halt alles nass,
|
||||
|
||||
00:26.000 --> 00:35.000
|
||||
sich da irgendwie das Essen irgendwann in die Sockenquellen, in die Nassen Schuhequellen, in die Nasse Hosequellen, alles zusammenpacken.
|
||||
|
||||
00:36.000 --> 00:38.000
|
||||
Ja, die Nassen Schlafsack einpacken.
|
||||
|
||||
00:39.000 --> 00:44.000
|
||||
Ich glaube, in der Sonne ist das alles etwas geiler und geht etwas schneller und ist bestimmt auch ein bisschen trockener.
|
||||
|
||||
00:45.000 --> 00:49.000
|
||||
Ja, ich hoffe mal, dass wir das heute Abend herausfinden werden.
|
||||
|
||||
00:50.000 --> 00:52.000
|
||||
Oh, Sonne, Sonne heute irgendwann.
|
||||
|
||||
00:53.000 --> 00:57.000
|
||||
Könnte das da hinten blauer Himmel sein? Man weiß es nicht genau, eventuell.
|
||||
|
||||
00:58.000 --> 00:59.000
|
||||
Ja, das ist ja.
|
||||
|
||||
01:00.000 --> 01:02.000
|
||||
Auf jeden Fall, das ist unsere Behausung.
|
||||
|
||||
01:03.000 --> 01:04.000
|
||||
Ich meine, eine Nacht im Märchenwald hier, ne?
|
||||
|
||||
01:05.000 --> 01:06.000
|
||||
Eine Nacht im Märchenwald.
|
||||
|
||||
01:07.000 --> 01:10.000
|
||||
Und wir wurden nicht verhext von der Hexe im Hexenhaus.
|
||||
|
||||
01:11.000 --> 01:12.000
|
||||
Ne.
|
||||
|
||||
01:13.000 --> 01:14.000
|
||||
Jo.
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
WEBVTT
|
||||
|
||||
00:00.000 --> 00:25.000
|
||||
With the first night in the forest behind us, yes, yes, we’re dressed now, we needed the shoes for all the fun this morning, it was 6:30 a.m., two hours—that’s a process, everything is just wet,
|
||||
|
||||
00:26.000 --> 00:35.000
|
||||
somehow packing the food into the socks, into the wet shoes, into the wet pants, packing everything together.
|
||||
|
||||
00:36.000 --> 00:38.000
|
||||
Yes, packing up the wet sleeping bag.
|
||||
|
||||
00:39.000 --> 00:44.000
|
||||
I think in the sun it’s all a bit nicer, goes a bit faster, and is probably a bit drier too.
|
||||
|
||||
00:45.000 --> 00:49.000
|
||||
Yes, I hope we’ll find that out this evening.
|
||||
|
||||
00:50.000 --> 00:52.000
|
||||
Oh, sun, sun will come today at some point.
|
||||
|
||||
00:53.000 --> 00:57.000
|
||||
Could that be blue sky back there? You can’t tell exactly, maybe.
|
||||
|
||||
00:58.000 --> 00:59.000
|
||||
Yes, that’s…
|
||||
|
||||
01:00.000 --> 01:02.000
|
||||
Anyway, this is our shelter.
|
||||
|
||||
01:03.000 --> 01:04.000
|
||||
I mean, one night in a fairy-tale forest here, right?
|
||||
|
||||
01:05.000 --> 01:06.000
|
||||
One night in a fairy-tale forest.
|
||||
|
||||
01:07.000 --> 01:10.000
|
||||
And we weren’t cursed by the witch in the witch’s house.
|
||||
|
||||
01:11.000 --> 01:12.000
|
||||
No.
|
||||
|
||||
01:13.000 --> 01:14.000
|
||||
Yep.
|
||||
@@ -1,173 +0,0 @@
|
||||
---
|
||||
title: Picos de Europa
|
||||
date: 2025-10-25
|
||||
license: "CC-BY-SA:4.0"
|
||||
comments: true
|
||||
icon: 🏔️
|
||||
cover: ./images/20250527_125228.jpg
|
||||
tags: ["picos-de-europa", "spain", "hiking", "travel"]
|
||||
---
|
||||
|
||||
Unsorted pictures from our hike through the Picos de Europa.
|
||||
|
||||
import Image from "@components/Image.astro";
|
||||
import ImageGallery from "@components/ImageGallery.svelte";
|
||||
import ImageSlider from "@components/ImageSlider.svelte";
|
||||
|
||||
<ImageGallery client:load />
|
||||
|
||||
## May 27th
|
||||
|
||||
import image1 from "images/20250527_125228.jpg";
|
||||
import image16 from "images/PXL_20250527_101057540.MP.jpg";
|
||||
import image17 from "images/PXL_20250527_100728883.jpg";
|
||||
|
||||
<ImageSlider title="First day, arrival at the sea." client:load>
|
||||
<Image src={image1} alt="A cow on a meadow in front of the sea" />
|
||||
<Image
|
||||
src={image17}
|
||||
alt="A person on flysch rock in front of a cave at the sea"
|
||||
/>
|
||||
<Image
|
||||
src={image16}
|
||||
alt="A person in a cave from which you can see the sea"
|
||||
/>
|
||||
</ImageSlider>
|
||||
|
||||
## May 28th
|
||||
|
||||
import image15 from "images/PXL_20250528_121633744.MP.jpg";
|
||||
import image19 from "images/20250528_164715.jpg";
|
||||
|
||||
<ImageSlider title="First day of hiking" client:load>
|
||||
<Image src={image15} alt="Us after the first 300 meters" />
|
||||
<Image
|
||||
src={image19}
|
||||
alt="Many mountain goats that like to lick the salt from the skin"
|
||||
/>
|
||||
</ImageSlider>
|
||||
|
||||
## May 29th
|
||||
|
||||
Hard ascent
|
||||
|
||||
import videoUrl1 from "images/PXL_20250529_125633973_small_x265.mp4";
|
||||
import image12 from "images/PXL_20250530_082919731.jpg";
|
||||
import image13 from "images/PXL_20250529_201559403.jpg";
|
||||
|
||||
<video src={videoUrl1} controls alt="" />
|
||||
<Image alt="" src={image12} alt="Finally arrived at the Refugio" />
|
||||
|
||||
## May 30-31
|
||||
|
||||
Thunderstorm and rest day
|
||||
|
||||
import image11 from "images/PXL_20250530_110041174.jpg";
|
||||
import image10 from "images/PXL_20250530_135631186.MP.jpg";
|
||||
import image8 from "images/PXL_20250530_170114907.MP.jpg";
|
||||
import image9 from "images/PXL_20250530_141432767.jpg";
|
||||
import image18 from "images/IMG-20250531-WA0020.jpeg";
|
||||
|
||||
<ImageSlider title="Thunderstorm and rest day" client:load>
|
||||
<Image src={image11} alt="Departure to the mountains" />
|
||||
<Image
|
||||
src={image10}
|
||||
alt="Surprisingly saved under the tarp after a thunderstorm"
|
||||
/>
|
||||
<Image src={image9} alt="Second departure to the mountains" />
|
||||
<Image src={image8} alt="Waiting for the second thunderstorm" />
|
||||
<Image src={image18} alt="Arrival at the Refugio" />
|
||||
</ImageSlider>
|
||||
|
||||
## June 1st
|
||||
|
||||
We continue as a pair.
|
||||
|
||||
import videoUrl2 from "images/PXL_20250601_044050514_small_x265.mp4";
|
||||
import videoUrl2TrackDe from "images/PXL_20250601_044050514_small_x265.vtt";
|
||||
import videoUrl2TrackEn from "images/PXL_20250601_044050514_small_x265_en.vtt";
|
||||
import image3 from "images/PXL_20250601_124002445.jpg";
|
||||
import image4 from "images/PXL_20250601_095307910.jpg";
|
||||
import image5 from "images/PXL_20250601_082528934.jpg";
|
||||
import image2 from "images/PXL_20250601_132646197.jpg";
|
||||
|
||||
<video src={videoUrl2} controls alt="14">
|
||||
<track
|
||||
type="caption"
|
||||
label="German"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl2TrackDe}
|
||||
/>
|
||||
<track
|
||||
type="caption"
|
||||
label="English"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl2TrackEn}
|
||||
default
|
||||
/>
|
||||
</video>
|
||||
<ImageSlider title="Over the peaks" client:load>
|
||||
<Image src={image4} alt="First ascent-done, about 1000 meters in altitude" />
|
||||
<Image src={image5} alt="In the middle of the mountain basin" />
|
||||
<Image src={image3} alt="On the descent" />
|
||||
<Image src={image2} alt="Immersed in the fog" />
|
||||
</ImageSlider>
|
||||
|
||||
## June 2nd
|
||||
|
||||
Tarping in the forest and wet-hiking then drying out in the hotel room
|
||||
|
||||
import image21 from "images/PXL_20250602_064221551.jpg";
|
||||
import videoUrl3 from "images/PXL_20250602_064236132_small_x265.mp4";
|
||||
import videoUrl3TrackDe from "images/PXL_20250602_064236132_small_x265.vtt";
|
||||
import videoUrl3TrackEn from "images/PXL_20250602_064236132_small_x265_en.vtt";
|
||||
|
||||
<Image alt="" src={image21} alt="20" />
|
||||
|
||||
<video src={videoUrl3} controls alt="">
|
||||
<track
|
||||
type="caption"
|
||||
label="German"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl3TrackDe}
|
||||
/>
|
||||
<track
|
||||
type="caption"
|
||||
label="English"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl3TrackEn}
|
||||
default
|
||||
/>
|
||||
</video>
|
||||
|
||||
## June 3rd
|
||||
|
||||
Ascent to lofty heights
|
||||
|
||||
import image22 from "images/PXL_20250603_083830771.jpg";
|
||||
import image23 from "images/PXL_20250603_093606665.jpg";
|
||||
import image25 from "images/PXL_20250603_194635918.jpg";
|
||||
import image26 from "images/PXL_20250603_194636284.jpg";
|
||||
|
||||
<ImageSlider title="Over the peaks" client:load>
|
||||
<Image src={image22} alt="The ascent was completely in the fog" />
|
||||
<Image src={image23} alt="Then came the first snowfields" />
|
||||
<Image src={image25} alt="Arrived at the Refugio" />
|
||||
<Image src={image26} alt="Our camp for the night" />
|
||||
</ImageSlider>
|
||||
|
||||
## June 4th
|
||||
|
||||
Overnight stay at Refugio Los Cabrones
|
||||
|
||||
## June 5th
|
||||
|
||||
Final-descent
|
||||
|
||||
import image27 from "images/PXL_20250605_100650266.jpg";
|
||||
|
||||
<Image alt="" src={image27} alt="The last walk down the mountain" />
|
||||
@@ -1,174 +0,0 @@
|
||||
---
|
||||
title: Picos de Europa
|
||||
date: 2025-10-25
|
||||
license: "CC-BY-SA:4.0"
|
||||
comments: true
|
||||
icon: 🏔️
|
||||
cover: ./images/20250527_125228.jpg
|
||||
tags: ["picos-de-europa", "spain", "hiking", "travel"]
|
||||
---
|
||||
|
||||
Unsortierte Bilder aus unserer Wanderung durch die Picos de Europa.
|
||||
|
||||
import Image from "@components/Image.astro";
|
||||
import ImageGallery from "@components/ImageGallery.svelte";
|
||||
import ImageSlider from "@components/ImageSlider.svelte";
|
||||
|
||||
<ImageGallery client:load />
|
||||
|
||||
## 27. May
|
||||
|
||||
Erster Tag, Ankunft am Meer.
|
||||
|
||||
import image1 from "images/20250527_125228.jpg";
|
||||
import image16 from "images/PXL_20250527_101057540.MP.jpg";
|
||||
import image17 from "images/PXL_20250527_100728883.jpg";
|
||||
|
||||
<ImageSlider client:load>
|
||||
<Image src={image1} alt="Bild einer Kuh auf einer Wiese vor dem Meer" />
|
||||
<Image
|
||||
src={image17}
|
||||
alt="Person auf flysch gestein vor einer Höhle am Meer"
|
||||
/>
|
||||
<Image
|
||||
src={image16}
|
||||
alt="Bild von Person in einer Höhle aus der man das Meer sieht"
|
||||
/>
|
||||
</ImageSlider>
|
||||
|
||||
## 28. May
|
||||
|
||||
Erster Wandertag
|
||||
|
||||
import image15 from "images/PXL_20250528_121633744.MP.jpg";
|
||||
import image19 from "images/20250528_164715.jpg";
|
||||
|
||||
<ImageSlider client:load>
|
||||
<Image src={image15} alt="Wir nach den ersten 300 Metern" />
|
||||
<Image
|
||||
src={image19}
|
||||
alt="Viele Bergziegen die gerne das Salz von der Haut lecken"
|
||||
/>
|
||||
</ImageSlider>
|
||||
|
||||
## 29. May
|
||||
|
||||
Harter Aufstieg
|
||||
|
||||
import videoUrl1 from "images/PXL_20250529_125633973_small_x265.mp4";
|
||||
import image12 from "images/PXL_20250530_082919731.jpg";
|
||||
import image13 from "images/PXL_20250529_201559403.jpg";
|
||||
|
||||
<video src={videoUrl1} controls alt="" />
|
||||
<Image alt="" src={image12} alt="Endlich am Refugio angekommen" />
|
||||
|
||||
## 30-31. May
|
||||
|
||||
import image11 from "images/PXL_20250530_110041174.jpg";
|
||||
import image10 from "images/PXL_20250530_135631186.MP.jpg";
|
||||
import image8 from "images/PXL_20250530_170114907.MP.jpg";
|
||||
import image9 from "images/PXL_20250530_141432767.jpg";
|
||||
import image18 from "images/IMG-20250531-WA0020.jpeg";
|
||||
|
||||
<ImageSlider title="Gewitter und Restday" client:load>
|
||||
<Image src={image11} alt="Aufbruch in die Berge" />
|
||||
<Image
|
||||
src={image10}
|
||||
alt="Überrascht nach einem Gewitter unters Tarp gerettet"
|
||||
/>
|
||||
<Image src={image9} alt="Zweiter Aufbruch in die Berge" />
|
||||
<Image src={image8} alt="Zweites Gewitter abwarten" />
|
||||
<Image src={image18} alt="Ankommen am Refugio" />
|
||||
</ImageSlider>
|
||||
|
||||
## 1. Juni
|
||||
|
||||
Es geht zu zweit weiter.
|
||||
|
||||
import videoUrl2 from "images/PXL_20250601_044050514_small_x265.mp4";
|
||||
import videoUrl2TrackDe from "images/PXL_20250601_044050514_small_x265.vtt";
|
||||
import videoUrl2TrackEn from "images/PXL_20250601_044050514_small_x265_en.vtt";
|
||||
import image3 from "images/PXL_20250601_124002445.jpg";
|
||||
import image4 from "images/PXL_20250601_095307910.jpg";
|
||||
import image5 from "images/PXL_20250601_082528934.jpg";
|
||||
import image2 from "images/PXL_20250601_132646197.jpg";
|
||||
|
||||
<video src={videoUrl2} controls alt="14">
|
||||
<track
|
||||
type="caption"
|
||||
label="Deutsch"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl2TrackDe}
|
||||
default
|
||||
/>
|
||||
<track
|
||||
type="caption"
|
||||
label="Englisch"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl2TrackEn}
|
||||
/>
|
||||
</video>
|
||||
<ImageSlider title="Über die Gipfel" client:load>
|
||||
<Image src={image4} alt="Erster Aufstieg geschafft, circa 1000 Höhenmeter" />
|
||||
<Image src={image5} alt="Mittem im Bergkessel" />
|
||||
<Image src={image3} alt="Auf dem Abstieg" />
|
||||
<Image src={image2} alt="Eingetaucht in den Nebel" />
|
||||
</ImageSlider>
|
||||
|
||||
## 2. Juni
|
||||
|
||||
Tarpen im Wald und nass-wandern dann Austrocken im Hotelzimmer
|
||||
|
||||
import image21 from "images/PXL_20250602_064221551.jpg";
|
||||
import videoUrl3 from "images/PXL_20250602_064236132_small_x265.mp4";
|
||||
import videoUrl3TrackDe from "images/PXL_20250602_064236132_small_x265.vtt";
|
||||
import videoUrl3TrackEn from "images/PXL_20250602_064236132_small_x265_en.vtt";
|
||||
|
||||
<Image alt="" src={image21} alt="20" />
|
||||
<video src={videoUrl3} controls alt="">
|
||||
<track
|
||||
type="caption"
|
||||
label="Deutsch"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl3TrackDe}
|
||||
default
|
||||
/>
|
||||
<track
|
||||
type="caption"
|
||||
label="Englisch"
|
||||
kind="subtitles"
|
||||
srclang="de"
|
||||
src={videoUrl3TrackEn}
|
||||
/>
|
||||
</video>
|
||||
|
||||
## 3. Juni
|
||||
|
||||
Aufstieg in lichte Höhen
|
||||
|
||||
import image22 from "images/PXL_20250603_083830771.jpg";
|
||||
import image23 from "images/PXL_20250603_093606665.jpg";
|
||||
import image25 from "images/PXL_20250603_194635918.jpg";
|
||||
import image26 from "images/PXL_20250603_194636284.jpg";
|
||||
|
||||
<ImageSlider title="Über die Gipfel" client:load>
|
||||
<Image src={image22} alt="Der Aufstieg war komplett im Nebel" />
|
||||
<Image src={image23} alt="Dann kamen die ersten Schneefelder" />
|
||||
<Image src={image25} alt="Angekommen am Refugio" />
|
||||
<Image src={image26} alt="Unser Camp für die Nacht" />
|
||||
</ImageSlider>
|
||||
|
||||
## 4. Juni
|
||||
|
||||
Übernachten im Refugio Los Cabrones
|
||||
|
||||
## 5. Juni
|
||||
|
||||
Finaler Abstieg
|
||||
|
||||
import image27 from "images/PXL_20250605_100650266.jpg";
|
||||
|
||||
<Image alt="" src={image27} alt="The last walk down the mountain" />
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
title: "Invoice"
|
||||
date: 2023-08-21
|
||||
cover: ./images/bg.jpg
|
||||
icon: "/projects/invoice.svg"
|
||||
tags: ["sveltekit", "unocss", "prisma", "sqlite"]
|
||||
toc: true
|
||||
links:
|
||||
[
|
||||
["live", "https://invoice.app.max-richter.dev"],
|
||||
["git", "https://git.max-richter.dev/max/invoice"],
|
||||
]
|
||||
---
|
||||
|
||||
import bg from './images/bg.jpg'
|
||||
import invoices from './images/invoices.png'
|
||||
import customers from './images/customers.png'
|
||||
import editCustomers from './images/edit-customer.png'
|
||||
import editProfile from './images/edit-profile.png'
|
||||
import overview from './images/overview.png'
|
||||
import ImageSlider from '@components/ImageSlider.svelte'
|
||||
import Image from '@components/Image.astro'
|
||||
|
||||
|
||||
# Introduction
|
||||
|
||||
In my free time, I like to take on small jobs, such as deliveries, setups, and pickups for others.
|
||||
|
||||
An unavoidable part of these tasks is creating invoices in PDF format. Initially, I followed a manual process and created my first invoices in Figma. But as programmers often say:
|
||||
|
||||
> Why do something manually in 5 minutes when I can automate it in 24 hours?
|
||||
|
||||
This thought led to my latest hobby project – **"Invoice."**
|
||||
|
||||
<ImageSlider title="Invoice Screens" client:load>
|
||||
<Image src={invoices} alt="Invoices" />
|
||||
<Image src={customers} alt="Customers" />
|
||||
<Image src={editCustomers} alt="Edit Customers" />
|
||||
<Image src={editProfile} alt="Edit Profile" />
|
||||
<Image src={overview} alt="Overview" />
|
||||
</ImageSlider>
|
||||
|
||||
# Development
|
||||
|
||||
During development, I always kept the principle 'K.I.S.S.' in mind: Keep it simple, stupid. For this project, that meant choosing "boring" but well-known technologies:
|
||||
|
||||
## [🚀 SvelteKit](https://kit.svelte.dev)
|
||||
For an efficient and reactive user interface.
|
||||
|
||||
## [🎨 UNOcss](https://unocss.dev/)
|
||||
The faster alternative to Tailwind.
|
||||
|
||||
## [🌍 TypesafeI18n](https://github.com/ivanhofer/typesafe-i18n)
|
||||
To enable multilingual support without complex logic.
|
||||
|
||||
## [🛠️ Prisma](https://prisma.io)
|
||||
As a database access layer for smooth data management.
|
||||
|
||||
## [🗃️ SQLite](https://www.sqlite.org/index.html)
|
||||
As a reliable backend, ideal for small projects.
|
||||
|
||||
## [📄 Playwright](https://playwright.dev)
|
||||
For generating PDFs.
|
||||
@@ -22,6 +22,8 @@ import ImageSlider from '@components/ImageSlider.svelte'
|
||||
import Image from '@components/Image.astro'
|
||||
|
||||
|
||||
# Einleitung
|
||||
|
||||
In meiner Freizeit übernehme ich gerne kleinere Aufträge und erledige Botengänge, Aufbauten und Abholungen für andere.
|
||||
|
||||
Ein unvermeidlicher Bestandteil dieser Tätigkeiten ist das Erstellen von Rechnungen im PDF-Format. Anfangs habe ich mich dem manuellen Prozess hingegeben und die ersten Rechnungen in Figma erstellt. Doch wie es unter Programmierer*innen oft heißt:
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
---
|
||||
title: "K.A.R.L"
|
||||
date: 2021-04-01
|
||||
cover: ./images/Indicatrices_of_Distortion.png
|
||||
license: "CC-BY-SA:4.0"
|
||||
featured: true
|
||||
toc: true
|
||||
icon: /projects/karl/favicon.png
|
||||
links:
|
||||
[
|
||||
["live", "https://max-richter.dev/karl"],
|
||||
["git", "https://git.max-richter.dev/max/karl"],
|
||||
]
|
||||
---
|
||||
|
||||
import Crosswalk from "./images/crosswalk.jpg"
|
||||
import CrosswalkMask from "./images/crosswalk_mask.png"
|
||||
import Image from "@components/Image.astro"
|
||||
import Distorion from "./images/Indicatrices_of_Distortion.png"
|
||||
import ImageGallery from "@components/ImageGallery.svelte"
|
||||
|
||||
<ImageGallery client:load/>
|
||||
|
||||
*[Header by Justin Kunimune - Own work, CC BY-SA 4.0](https://commons.wikimedia.org/w/index.php?curid=66467577*)*
|
||||
|
||||
|
||||
> K.A.R.L is a web app that helps to divide 360-degree panoramas into sections (sky, ground, trees, etc.) and then determine the proportion of each section in the overall image.
|
||||
|
||||
# Introduction
|
||||
|
||||
The project emerged from a collaboration with two friends. One of them is in the conceptual phase of his bachelor's thesis (Geography), which deals with the impact of vegetation on urban climate. To this end, he conducted albedo measurements at various locations in Cologne—essentially measuring "how much light comes from the sky, and how much of it is reflected by the ground." To put these measurements into context, he took 360-degree panoramas at each measurement location, which look something like this:
|
||||
|
||||
<Image src={Crosswalk} alt="Image of a crosswalk" caption="Image from hdrihaven.com" />
|
||||
|
||||
He then needed data on what percentage of the view was occupied by vegetation, sky, and ground. To measure this, he manually created a segmentation map in Gimp, which looks something like this:
|
||||
|
||||
<Image src={CrosswalkMask} alt="Segmentation map" />
|
||||
|
||||
# Problem Statement
|
||||
|
||||
If we naively count the pixels of each color and use that to calculate a percentage distribution, we encounter the classic distortion problem that humanity has faced for centuries with maps. Spheres do not like being represented in two dimensions, which always leads to distortions, as visualized in the following image.
|
||||
|
||||
<Image src={Distorion} alt="Indicatrices of Distortion" caption="by Justin Kunimune - Own work, CC BY-SA 4.0" />
|
||||
|
||||
Fortunately, this distortion only occurs in width, so we need a formula that provides a weight for the height of a pixel to compensate for this distortion. After many attempts, we arrived at this formula:
|
||||
|
||||
```javascript
|
||||
/*
|
||||
height: height of the image in pixels
|
||||
calibrationFactor: 1.333, somehow this works, don't ask why, we don't either
|
||||
y: y position of the pixel
|
||||
*/
|
||||
const pixelValue = Math.cos(
|
||||
(((360 / height ** 2) * y ** 2 + (-360 / height) * y + 90) / 360) *
|
||||
(2 + calibrationFactor) *
|
||||
Math.PI
|
||||
)
|
||||
```
|
||||
|
||||
This formula is actually designed to calculate the distance between two longitudes for a given latitude, but it works very well for our purposes.
|
||||
|
||||
Here are some of the first attempts in Desmos (fantastic tool, by the way):
|
||||
|
||||
<iframe src="https://www.desmos.com/calculator/52ph4thjah?embed"/>
|
||||
|
||||
# Technologies
|
||||
|
||||
I wanted to build a web app that theoretically works completely offline. Therefore, after the initial page load, no further data is sent; everything runs in the browser. This is challenging for an application that processes many pixels and requires a lot of performance since browser resources are limited. The following technologies helped to keep the user experience reasonably smooth:
|
||||
|
||||
## Canvas
|
||||
|
||||
When working with pixels in the browser, you can't really avoid Canvas2D. Its programmatic interface makes it ideal for this job.
|
||||
|
||||
## Web Workers
|
||||
|
||||
Normally, all operations of a website run in a single thread in the browser. This means that a computationally intensive task can bring the entire site to a halt. Web Workers provide a solution by allowing arbitrary code to run in a separate thread, though communication with the main thread can be tricky.
|
||||
|
||||
K.A.R.L uses two Web Workers: the `pixel-worker` handles segmentation map analysis and the fill tool, while the `ai-worker` executes TensorFlow code.
|
||||
|
||||
## TensorFlow
|
||||
|
||||
Why manually color segments when the computer can do it automatically? That was my thought, so I integrated TensorFlow with the [ADE20K](https://groups.csail.mit.edu/vision/datasets/ADE20K/) dataset. This network is excellent at recognizing trees, ground, concrete, and sky. In the editor view, this function is hidden under the "AI" button on the right.
|
||||
|
||||
## IndexDB
|
||||
|
||||
When it comes to storing large amounts of data (especially images) locally in the browser, IndexedDB is the best option *(yes, localStorage with Base64 images could work too, but it's limited to 5MB in some browsers)*. However, the IndexedDB API is one of the [most confusing browser APIs out there](https://nolanlawson.github.io/offlinefirst-2016-03/#/27), so I use [idb](npmjs.com/package/idb), a fantastic small wrapper library for IndexedDB that also supports Promises.
|
||||
|
||||
# Interesting Features...
|
||||
|
||||
## Flood Fill Algorithm
|
||||
|
||||
After manually segmenting several panoramas using the tools, I noticed that I often just painted regions with similar colors. This gave me the idea to build a fill tool similar to Photoshop, but much better.
|
||||
|
||||
The tool works roughly as follows:
|
||||
1. The user clicks on the image with the fill tool.
|
||||
2. The x/y coordinates of the click and all pixels of the image are sent to a Web Worker.
|
||||
3. The Web Worker creates a grayscale image where each pixel value represents the spatial and color distance to the clicked region.
|
||||
4. The image is sent back.
|
||||
5. The canvas code then determines whether individual pixels should be filled based on their values.
|
||||
|
||||
## Svelte Bindings
|
||||
|
||||
I always found it complicated to manage state across multiple components. A good example is the editor, which consists of three separate components (Toolbar, Topbar, PaintArea), all of which need to know which color and tool are currently active. One could store this state globally and let each component access it, but in reality, the active tool is only relevant in the editor context. So now, this state resides in the editor component and is passed down to its subcomponents via binding. It looks something like this:
|
||||
|
||||
```svelte
|
||||
<!-- Editor.svelte -->
|
||||
<script lang="ts">
|
||||
import TopBar from "./TopBar.svelte";
|
||||
|
||||
let activeColor = "ff0000";
|
||||
let _activeColor = localStorage.getItem("activeColor");
|
||||
if (_activeColor) {
|
||||
activeColor = _activeColor;
|
||||
}
|
||||
</script>
|
||||
|
||||
<TopBar bind:activeColor/>
|
||||
|
||||
<!-- TopBar.svelte -->
|
||||
<script>
|
||||
export let activeColor = "ff0000";
|
||||
</script>
|
||||
|
||||
{{activeColor}}
|
||||
```
|
||||
|
||||
## Svelte Stores
|
||||
|
||||
For some other cases, stores work really well, such as for toasts/modals. This allows the toast state (the small messages at the bottom left) to be managed in a single component without having to distribute it across multiple components.
|
||||
@@ -26,6 +26,8 @@ import ImageGallery from "@components/ImageGallery.svelte"
|
||||
|
||||
> K.A.R.L ist eine WebApp die einem dabei hilft 360Grad Panoramas in Sektionen einzuteilen, (Himmel, Boden, Bäume usw...) und dann den Anteil der einzelnen Sektionen am Gesamtbild festzustellen.
|
||||
|
||||
# Einleitung
|
||||
|
||||
Das Projekt ist aus der Zusammenarbeit mit zwei Freunden entstanden. Der eine steckt gerade mitten in der Konzeptionsphase seiner Bachelorarbeit (Geographie), die sich mit der Auswirkung von Vegetation auf das Stadtklima beschäftigt. Dazu hat er an verschiedenen Orten in Köln Albedo Messungen vorgenommen, also quasi "wieviel Licht kommt vom Himmel, und wieviel davon wird vom Boden reflektiert". Um diese Messungen in den richtigen Kontext zu setzen hat er von jedem Messort 360 Panoramas angelegt, diese sehen ungefähr so aus:
|
||||
|
||||
<Image src={Crosswalk} alt="Image of a crosswalk" caption="bild von hdrihaven.com" />
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
HostUrl=about:internet
|
||||
@@ -1,133 +1,18 @@
|
||||
---
|
||||
title: "Plantarium"
|
||||
date: 2022-08-31T20:46:26+02:00
|
||||
date: 2022-08-31
|
||||
cover: ./images/plantarium.png
|
||||
featured: true
|
||||
links: [["website", "https://plant.max-richter.dev"], ["git", "https://github.com/jim-fx/plantarium"]]
|
||||
tags: ["Web", "3D", "Svelte", "Node Systems"]
|
||||
icon: /projects/plantarium/favicon.svg
|
||||
draft: false
|
||||
toc: true
|
||||
links: [["website", "https://plant.max-richter.com"], ["git", "https://github.com/jim-fx/plantarium"]]
|
||||
draft: true
|
||||
---
|
||||
|
||||
# Introduction
|
||||
One of the projects I spent the most time with. As a Waldorf student and village kid, a bit cliché, I know, but I've always been passionate about plants, their structure and aesthetics. I'm also an absolute [Blender](https://blender.org) nerd and from these two interests Plantarium was born.
|
||||
|
||||
Plantarium is the intersection of two things I find fascinating: plants and 3D modeling.
|
||||
It is a web app that allows users to create and export plants.
|
||||
Users define the plant’s appearance through a node system, and Plantarium generates a 3D model from it.
|
||||
Plantarium is a tool that allows users to generate procedural plants. The first prototype was finished within two weeks of intensive work and looked something like this:
|
||||
|
||||
<Image src={plantarium_screenshot} alt="Plantarium Screenshot"/>
|
||||
<img src="images/page01-0.jpg"/>
|
||||
|
||||
# The Beginnings and Challenges
|
||||
|
||||
The first prototype was ready within two weeks of intense work and looked something like this:
|
||||
The interface was divided into 4 different levels, "Stems", "Branches", "Leaves" and "Import/Export". The idea was not bad because parts of the interface belonged to parts of the plant and you could quickly find the right settings if you wanted to change something. This way of designing the interface limits the freedom of the user(s) to generate the plants they want. Also, the interface code was unattractive, to say the least. It worked but it was not fun to work on. Also, I was mixing UI code with code that generated the plant.
|
||||
|
||||
import ImageSlider from "@components/ImageSlider.svelte"
|
||||
import Leaves from "./_components/Leaves.svelte"
|
||||
import Image from "@components/Image.astro"
|
||||
import page01_0 from "./images/page01-0.jpg"
|
||||
import page01_1 from "./images/page01-1.jpg"
|
||||
import page01_2 from "./images/page01-2.jpg"
|
||||
import page01_3 from "./images/page01-3.jpg"
|
||||
import page01_5 from "./images/page01-5.jpg"
|
||||
import page01_6 from "./images/page01-6.jpg"
|
||||
import screenshot_geometry_nodes from "./images/screenshot-geometry-nodes.jpg"
|
||||
import screenshot_houdini from "./images/screenshot-houdini.jpg"
|
||||
import screenshot_unreal from "./images/screenshot-unreal.jpg"
|
||||
import screenshot_davinci from "./images/screenshot-davinci.jpg"
|
||||
import plantarium_screenshot from "./images/plantarium-screenshot.png"
|
||||
import Plantarium from "./images/plantarium.png"
|
||||
import ImageGallery from "@components/ImageGallery.svelte"
|
||||
|
||||
<ImageGallery client:load/>
|
||||
|
||||
<Leaves client:load/>
|
||||
|
||||
<ImageSlider title="First Prototype" client:load>
|
||||
<Image src={page01_0} alt="Stem Page"/>
|
||||
<Image src={page01_1} alt="Branches Page"/>
|
||||
<Image src={page01_2} alt="Leaves Page"/>
|
||||
<Image src={page01_3} alt="Import/Export Page"/>
|
||||
<Image src={page01_5} alt="Design of UI Components"/>
|
||||
<Image src={page01_6} alt="Data Flow inside App"/>
|
||||
</ImageSlider>
|
||||
|
||||
Not bad, but as with prototypes, there was still a lot to improve.
|
||||
So here’s a brief history of the biggest changes so far:
|
||||
|
||||
# Refactors
|
||||
|
||||
## Svelte Rewrite
|
||||
|
||||
The first prototype was handwritten, untested JavaScript.
|
||||
That’s like trying to build a car out of toothpicks and duct tape.
|
||||
You might get it to work, and you might even be proud of your creation,
|
||||
but if you revisit it a week later—or worse, try to improve it—it quickly becomes a nightmare.
|
||||
So, it was time for every web developer’s favorite activity:
|
||||
figuring out what’s currently the hottest thing in web development.
|
||||
Once you pick one of the thousands of possible technologies,
|
||||
you make it your personal religion and defend it with fanatical devotion.
|
||||
Easy, right?
|
||||
|
||||
So, I went with the simplest, fastest, and overall best framework: ["Svelte"](https://svelte.dev).
|
||||
|
||||
In essence, frameworks help turn an app’s data (e.g., usernames, login status) into HTML and CSS,
|
||||
which the browser then displays.
|
||||
|
||||
### Node Systems
|
||||
|
||||
The interface was divided into four different sections: "Stem," "Branches," "Leaves," and "Import/Export."
|
||||
The idea wasn’t bad since each part of the plant had its own view,
|
||||
making it easy to find the right settings when making changes.
|
||||
|
||||
However, this structure severely limited users.
|
||||
You could only build plants following the Stem → Branch → Leaf model.
|
||||
But what if you wanted multiple layers of branches or different leaves for stems and branches?
|
||||
This model didn’t allow for that.
|
||||
So, as they say:
|
||||
"Back to the drawing board."
|
||||
|
||||
I already had a solution in mind,
|
||||
but I wasn’t sure if I had the skills to implement it.
|
||||
|
||||
The solution? **Node Systems!**
|
||||
Hmm, what’s that?
|
||||
|
||||
<div class="center my-4">
|
||||

|
||||
</div>
|
||||
|
||||
Node systems consist of nodes and connections.
|
||||
Nodes have inputs and outputs, which can be connected.
|
||||
|
||||
In the example graphic, we have two `input-color` nodes that each produce a color,
|
||||
and a `mix-color` node that blends them.
|
||||
|
||||
The cool thing is that this system is highly flexible.
|
||||
You can program nodes like `generate-stem`, `generate-branches`, or `add-leaves`.
|
||||
|
||||
<ImageSlider title="Examples of Node Systems" client:load>
|
||||
<Image src={screenshot_geometry_nodes} alt="Blender uses nodes to create geometry"/>
|
||||
<Image src={screenshot_houdini} alt="Houdini uses nodes for VFX/simulations"/>
|
||||
<Image src={screenshot_unreal} alt="Unreal uses nodes for game logic"/>
|
||||
<Image src={screenshot_davinci} alt="DaVinci uses nodes for VFX"/>
|
||||
</ImageSlider>
|
||||
|
||||
### Svelte-Kit Rewrite
|
||||
|
||||
# Architecture
|
||||
|
||||
After experimenting with different approaches, I settled on an event-based architecture.
|
||||
This fits well with node systems since individual components can communicate via events.
|
||||
|
||||
<div class="center my-4">
|
||||

|
||||
</div>
|
||||
|
||||
# Design
|
||||
|
||||
# Future
|
||||
|
||||
# Conclusion
|
||||
|
||||
Currently, the architecture looks something like this:
|
||||
|
||||
@@ -10,6 +10,8 @@ draft: false
|
||||
toc: true
|
||||
---
|
||||
|
||||
# Einführung
|
||||
|
||||
Plantarium ist die Schnittmenge zwischen zwei Dingen die ich sehr faszinierend finde, Pflanzen und 3D Modellierung.
|
||||
Es ist eine WebApp die es Nutzern ermöglicht Pflanzen zu erstellen und zu exportieren.
|
||||
Die User legen dabei über ein Node-System fest wie die Pflanze aussieht und Plantarium generiert daraus ein 3D Modell.
|
||||
|
||||
@@ -1,944 +0,0 @@
|
||||
<div class="wrapper">
|
||||
<div class="invert-x">
|
||||
<svg
|
||||
width="262"
|
||||
height="261"
|
||||
viewBox="0 0 262 261"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="mask">
|
||||
<path
|
||||
id="Vector 28"
|
||||
d="M35 165C39.5 160 42 160.5 44 162C46 163.5 46 170 41.5 169C40.6667 168.833 39.1 168.1 39.5 166.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 29"
|
||||
d="M44 153.032C48.5 148.032 52.7137 149.856 54 152C57 157 52.5 161 50.5 161C48.5 161 46.5 160 46.5 157.5C46.5 155 50.4 153.5 50 157.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 30"
|
||||
d="M51 150C57 138.5 64.7137 143.356 66 145.5C69 150.5 63.5 154.171 61.5 154.171C59.5 154.171 57.5 153.171 57.5 150.671C57.5 148.171 61.5 146.5 61.5 150"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 31"
|
||||
d="M65 143.939C72.1803 129.985 81.4115 135.878 82.9507 138.479C86.5409 144.546 79.9589 149 77.5655 149C75.1721 149 72.7787 147.787 72.7787 144.753C72.7787 141.72 77.5655 139.692 77.5655 143.939"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 32"
|
||||
d="M83 139.216C90.1803 123.269 99.4115 130.003 100.951 132.976C104.541 139.91 97.9589 145 95.5655 145C93.1721 145 90.7787 143.613 90.7787 140.146C90.7787 136.68 95.5655 134.363 95.5655 139.216"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 33"
|
||||
d="M101 133.578C107.424 118.627 115.684 124.941 117.061 127.728C120.273 134.228 114.384 139 112.243 139C110.101 139 107.96 137.7 107.96 134.45C107.96 131.2 112.243 129.028 112.243 133.578"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 34"
|
||||
d="M117 127.216C124.18 111.269 133.411 118.003 134.951 120.976C138.541 127.91 131.959 133 129.566 133C127.172 133 124.779 131.613 124.779 128.146C124.779 124.68 129.566 122.363 129.566 127.216"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 35"
|
||||
d="M135 121.216C142.18 105.269 151.411 112.003 152.951 114.976C156.541 121.91 149.959 127 147.566 127C145.172 127 142.779 125.613 142.779 122.146C142.779 118.68 147.566 116.363 147.566 121.216"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 36"
|
||||
d="M154.5 127C168.5 108 181 107 186 115.5C189.959 122.23 183.5 130 179.5 130.5C175.5 131 171.5 129.354 171.5 124.5C171.5 119.646 178 117.147 178 122"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 37"
|
||||
d="M164 131.5C166.5 129.5 176.172 126.515 179 135C181 141 176 146.5 172 147C168 147.5 165.5 144.392 165.5 141C165.5 137.608 171.5 135.147 171.5 140"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 38"
|
||||
d="M148.5 155C151.5 145.5 164.172 142.515 167 151C169 157 165.5 160.5 161.5 161C157.5 161.5 156 159 156 155.5C156 152 161.5 151 161.5 155"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 39"
|
||||
d="M143.5 167.973C146 162 153.035 159.682 154.666 165.317C155.819 169.301 153.801 171.626 151.495 171.958C149.189 172.29 148 171.324 148 169C148 166.676 151.495 165.317 151.495 167.973"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 15"
|
||||
d="M84 192.5C101.167 193.5 137.6 188.8 146 162C154.4 135.2 166.5 128.167 171.5 128"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 16"
|
||||
d="M85 192.5C103.5 194 147.287 189.088 171 155C195 120.5 192.5 113 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 17"
|
||||
d="M83 193C108.667 193.667 160.089 186.414 177.5 163C192 143.5 197 127.5 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 19" d="M10.5 138.5L1 128.5" stroke-linecap="round" />
|
||||
<path id="Vector 20" d="M13 134.5L1 118.5" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 21"
|
||||
d="M18 128.5C17.8333 124.5 16 116.4 10 116C8.5 110 5 108 1 107.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 11"
|
||||
d="M29 230C25.1667 207.5 29.5 160 77.5 150C137.5 137.5 135.5 124 172 128"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 12"
|
||||
d="M28.9999 230C25.1666 207.5 30.5 158 96 158C130.5 158 148 130 172 128"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 1"
|
||||
d="M1 252.5C1.33333 247.333 2.5 240.5 7.5 241.5C12.5 242.5 11 250 8 250C5 250 4.5 245.5 7.5 246.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 2"
|
||||
d="M20 242.5C10.5 224.167 -3.60001 176.9 24 138.5C58.5 90.5 103 97 145 84.5C189 71.4048 236 59.5 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 3"
|
||||
d="M25 236.5C16.3333 217.833 3.99998 166.2 30 131"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 6"
|
||||
d="M193.5 131C215.667 130.833 259.8 104.2 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 1"
|
||||
d="M1 260C1 260 1 223.1 1 200C1 176.9 5.8 138.4 41.5 115.5"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 3"
|
||||
d="M1 260C9 260 29.4 230.5 35.5 223.3C40.9 217 51.3 211.3 59 221C70.1 235 44 247.2 43.3 233.6C42.8 224.3 52.6 227.4 51 231"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 3 copy"
|
||||
d="M59.5 234.5C59.5 234.5 65.4 227.7 71.5 220.5C76.9 214.2 87.3 208.5 95 218.2C106.1 232.2 80 244.4 79.3 230.8C78.8 221.5 88.6 224.6 87 228.2"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 3 copy_2"
|
||||
d="M94.5 232.5C94.5 232.5 98.9366 226.392 104.04 220.322C108.559 215.012 117.26 210.207 123.703 218.384C132.99 230.185 111.152 240.47 110.567 229.005C110.148 221.165 118.348 223.779 117.009 226.813"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 4"
|
||||
d="M29 231.5C26 209.5 23.3 160.6 38.5 143C57.5 121 80 116.5 107.5 110C135 103.5 228 93.5 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 5"
|
||||
d="M29 231.5C26 209.5 24.8 175.1 40 157.5C59 135.5 78.6302 132.623 107.5 123C137.5 113 147.5 103.5 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 7"
|
||||
d="M194.5 125.5C223 118.5 230.5 103 241 87.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 8"
|
||||
d="M194.5 121C214.833 115.833 253.5 89 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 9"
|
||||
d="M188.5 104C203.667 101 240.6 73.8 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 69"
|
||||
d="M177 104.5C184.667 102 192 99.5 207 88.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 10"
|
||||
d="M194 112.5C232.5 93.5 249.5 66.5 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 68"
|
||||
d="M194.5 116.5C205.5 113 217 104 230 90.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 13"
|
||||
d="M30.5 203C32.1667 198.833 39.7 190.7 56.5 191.5C77.5 192.5 122 201 135 145.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 14"
|
||||
d="M78 192.5C96.6667 195 135.3 188.5 140.5 142.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 18"
|
||||
d="M1 154.5C6.83333 142.5 23.8 116.5 41 110.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 21_2"
|
||||
d="M27.5 119C26 108.5 11.5 105 1 94.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 22"
|
||||
d="M28.5 118C27 105 11.5 100.5 1 87.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 23"
|
||||
d="M29.5 117C27.5 99 9.5 94.5 1 80.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 24"
|
||||
d="M36 112.5C34.5 84.5 8 92.5 1 55"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 25"
|
||||
d="M41.5 119C43.5 77 11 84 1 43"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 26"
|
||||
d="M129.5 115C131 110 141.5 95 157 108"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 27"
|
||||
d="M154.5 106C160.5 101.5 171.5 95.5 179.5 108"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 40"
|
||||
d="M123 179.5L140.5 172.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 41" d="M131.5 169.5L146 162" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 42"
|
||||
d="M136.5 159.5L149.5 152.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 43" d="M140 148L155.5 140.5" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 44"
|
||||
d="M63.5 163.5C69 162 69.5 152.5 74.5 151"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 45"
|
||||
d="M81.5 159C86 158.5 85 148.5 89.5 147.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 46"
|
||||
d="M96 158C99 157.5 100 145 104 143.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 47"
|
||||
d="M107.5 157C112.5 155.5 112.5 141.5 120 138"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 48"
|
||||
d="M122 152.5C127.5 150 126 136 133 133"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 49"
|
||||
d="M136.5 144.5C140.5 142 140.5 131 147.5 128.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 50"
|
||||
d="M180.5 108C181.5 105.5 189.5 99.5 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 51"
|
||||
d="M36.5 162.5C39 159 35.5 148.5 40 141.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 52"
|
||||
d="M42 155.5C45.5 151 43.5 138 48.5 133"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 53"
|
||||
d="M49 148.5C53 145.5 50 132.5 52 130.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 54" d="M1 81V67" stroke-linecap="round" />
|
||||
<path id="Vector 55" d="M6 87L7 76.5" stroke-linecap="round" />
|
||||
<path id="Vector 56" d="M13 93L14 85.5" stroke-linecap="round" />
|
||||
<path id="Vector 57" d="M19.5 99L21 91" stroke-linecap="round" />
|
||||
<path id="Vector 58" d="M24 104L28 98" stroke-linecap="round" />
|
||||
<path id="Vector 59" d="M27.5 109L31.5 104.5" stroke-linecap="round" />
|
||||
<path id="Vector 60" d="M29 114L34 110.5" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 61"
|
||||
d="M52 130.5C53.5 129.5 62.5 139 66.5 137"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 62"
|
||||
d="M55.5 128C61 125 78 132.5 84 130.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 63"
|
||||
d="M64 123.5C72 120 92.5 127.5 99.5 125.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 64"
|
||||
d="M74.5 119C82.5 116.5 112 121.5 120 118.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 65"
|
||||
d="M89 114.5C98.5 112 122.5 111 129.5 114.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 66"
|
||||
d="M172.5 101.5C195.5 91 238.5 74 260.5 5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 67"
|
||||
d="M166.5 100.5C177 95.6667 182.2 93.8 189 89"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 70"
|
||||
d="M194.5 112C195 110.333 196 105.9 196 101.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 71"
|
||||
d="M199 110C201.4 109 199.6 100.5 202 98"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 72"
|
||||
d="M206 106C208.5 104.5 206.5 96 211 91"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 73"
|
||||
d="M212 102C216 99 213 89.5 216 86"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 74"
|
||||
d="M220.5 95C223 93 219 84.5 221.5 80.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 75"
|
||||
d="M227.5 88C230.5 85 226 76.5 229 71"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 76"
|
||||
d="M235 78.5C238 74.5 232.5 67 234.5 62.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 77"
|
||||
d="M241.5 67.5C243.5 64 237 59.5 238.5 56"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 78"
|
||||
d="M247 55C248 52 242 50.5 243.5 47"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 79"
|
||||
d="M250.5 44C251.5 41.5 247.7 39.3 248.5 36.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 80"
|
||||
d="M191.5 138.5C194.5 135.833 200.7 130.4 201.5 130"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 81"
|
||||
d="M188.5 146C192.5 144 204.5 135.5 211 126.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 82"
|
||||
d="M41.5 115.5C43.1667 113.167 46.9 106.1 46.5 92.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 83"
|
||||
d="M34.5 90.5C34.8333 87.5 35.3 80.1 34.5 74.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 84"
|
||||
d="M18 73C19.1667 72.1667 21.6 68.5 22 60.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 85"
|
||||
d="M38.5 97C39.1667 92.6667 42.1 83.2 46.5 80"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 90"
|
||||
d="M56 108.5C56 97.5 50.6933 83.2 46 80"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 86"
|
||||
d="M26 80.5C26.6667 76.1667 29 68.5 34.5 65"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 89"
|
||||
d="M43 84C42.2941 79.6667 39.8235 68.5 34 65"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 87"
|
||||
d="M13 67.5C13.6667 63.1667 16 53.5 21.5 50"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 88"
|
||||
d="M30.5 69C29.7941 64.7905 26.8235 53.4 21 50"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 91"
|
||||
d="M183 155C194.5 150.5 206.5 147.5 219 122"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 92"
|
||||
d="M176.5 165C189.167 164.833 217.5 154.2 229.5 113"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 93"
|
||||
d="M246 87.5C251 89.8333 261.1 97.8 261.5 111C261.9 124.2 250 131.833 244 134"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 94"
|
||||
d="M28 216.5C32 215.5 42 202 49 202C56 202 64 214.5 72.5 214.5C81 214.5 92 203.5 100 204C108 204.5 116.5 217.5 127.5 215.5C138.5 213.5 145.5 203.5 153.5 202.5C161.5 201.5 175 210.5 184.5 207.5C192.1 205.1 195 198.5 195.5 195.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 95"
|
||||
d="M30.5 204C32 204 45.5 214 49.5 214C53.5 214 63 204 72 204C81 204 87.5 215 99 215.5C110.5 216 116.5 204 126.5 205C136.5 206 144 214 154 212C164 210 174.5 199 178.5 197C182.5 195 192 197.5 195 196C198 194.5 206 189.5 209 179.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 96"
|
||||
d="M29.5 205C31 202 36.5 196.5 55.5 197.5C74.5 198.5 95 202.5 112 201.5C129 200.5 145.5 198.5 155.5 194.5C165.5 190.5 178.5 179.5 184 172"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 97"
|
||||
d="M123 201C134.667 200.333 160.4 198.2 170 195C179.6 191.8 190.167 182.333 193.5 178.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 98"
|
||||
d="M165.5 174C173.333 174 193.7 168.4 200.5 160C207.3 151.6 213.667 144.167 216 141.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 99"
|
||||
d="M183 170.5C186 174.833 197.5 182.5 215 178"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 100"
|
||||
d="M204 173C207.333 177.333 217.6 183.4 230 169"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 101"
|
||||
d="M221 167.5C224.667 169.667 233.6 171.4 240 161C242.8 156.2 244.333 152 244.5 151"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 102"
|
||||
d="M234.5 153C240.833 154 253.4 150.6 253 129"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 103"
|
||||
d="M204.5 155.5C208.5 151.5 214.937 148.517 219 152C222.5 155 226 162.5 220 167.5C214.664 171.947 210.187 167.904 209.5 165.5C208.214 161 213 157 215 161"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 104"
|
||||
d="M214 144C218 140 226.437 137.054 230.5 140.537C234 143.537 237.5 151.037 231.5 156.037C226.164 160.484 221.687 156.441 221 154.037C219.714 149.537 224 146 226.5 149.537"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 105"
|
||||
d="M223.5 129.5C227.5 125.5 234.437 121.517 238.5 125C242 128 246.5 135.037 240.5 140.037C235.164 144.484 230.687 140.441 230 138.037C228.714 133.537 233.5 130 235.5 133.537"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 106"
|
||||
d="M229.5 113C233.5 109 240.623 102.5 246.5 107.537C250 110.537 253.5 118.037 247.5 123.037C242.164 127.484 237.687 123.441 237 121.037C235.714 116.537 240.5 113 242.5 116.537"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 107"
|
||||
d="M224.5 169C223.833 170.667 220.1 173.7 210.5 172.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 108"
|
||||
d="M238.5 153C238.333 155.5 235.8 160.9 227 162.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 109"
|
||||
d="M248.5 132C248.5 135.167 246.8 142.6 240 147"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 110"
|
||||
d="M242 95C247 95.3333 256.5 100.5 254.5 118.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 111"
|
||||
d="M200.5 160C198.833 162.333 196.5 169.5 200.5 172C205.067 174.854 209 170.5 209 168C209 165.5 205.82 160.5 202.5 165.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 112"
|
||||
d="M207 176C205.167 177 200.4 177.7 192 172.5"
|
||||
stroke-linecap="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="">
|
||||
<svg
|
||||
width="262"
|
||||
height="261"
|
||||
viewBox="0 0 262 261"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="mask">
|
||||
<path
|
||||
id="Vector 28"
|
||||
d="M35 165C39.5 160 42 160.5 44 162C46 163.5 46 170 41.5 169C40.6667 168.833 39.1 168.1 39.5 166.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 29"
|
||||
d="M44 153.032C48.5 148.032 52.7137 149.856 54 152C57 157 52.5 161 50.5 161C48.5 161 46.5 160 46.5 157.5C46.5 155 50.4 153.5 50 157.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 30"
|
||||
d="M51 150C57 138.5 64.7137 143.356 66 145.5C69 150.5 63.5 154.171 61.5 154.171C59.5 154.171 57.5 153.171 57.5 150.671C57.5 148.171 61.5 146.5 61.5 150"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 31"
|
||||
d="M65 143.939C72.1803 129.985 81.4115 135.878 82.9507 138.479C86.5409 144.546 79.9589 149 77.5655 149C75.1721 149 72.7787 147.787 72.7787 144.753C72.7787 141.72 77.5655 139.692 77.5655 143.939"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 32"
|
||||
d="M83 139.216C90.1803 123.269 99.4115 130.003 100.951 132.976C104.541 139.91 97.9589 145 95.5655 145C93.1721 145 90.7787 143.613 90.7787 140.146C90.7787 136.68 95.5655 134.363 95.5655 139.216"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 33"
|
||||
d="M101 133.578C107.424 118.627 115.684 124.941 117.061 127.728C120.273 134.228 114.384 139 112.243 139C110.101 139 107.96 137.7 107.96 134.45C107.96 131.2 112.243 129.028 112.243 133.578"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 34"
|
||||
d="M117 127.216C124.18 111.269 133.411 118.003 134.951 120.976C138.541 127.91 131.959 133 129.566 133C127.172 133 124.779 131.613 124.779 128.146C124.779 124.68 129.566 122.363 129.566 127.216"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 35"
|
||||
d="M135 121.216C142.18 105.269 151.411 112.003 152.951 114.976C156.541 121.91 149.959 127 147.566 127C145.172 127 142.779 125.613 142.779 122.146C142.779 118.68 147.566 116.363 147.566 121.216"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 36"
|
||||
d="M154.5 127C168.5 108 181 107 186 115.5C189.959 122.23 183.5 130 179.5 130.5C175.5 131 171.5 129.354 171.5 124.5C171.5 119.646 178 117.147 178 122"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 37"
|
||||
d="M164 131.5C166.5 129.5 176.172 126.515 179 135C181 141 176 146.5 172 147C168 147.5 165.5 144.392 165.5 141C165.5 137.608 171.5 135.147 171.5 140"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 38"
|
||||
d="M148.5 155C151.5 145.5 164.172 142.515 167 151C169 157 165.5 160.5 161.5 161C157.5 161.5 156 159 156 155.5C156 152 161.5 151 161.5 155"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 39"
|
||||
d="M143.5 167.973C146 162 153.035 159.682 154.666 165.317C155.819 169.301 153.801 171.626 151.495 171.958C149.189 172.29 148 171.324 148 169C148 166.676 151.495 165.317 151.495 167.973"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 15"
|
||||
d="M84 192.5C101.167 193.5 137.6 188.8 146 162C154.4 135.2 166.5 128.167 171.5 128"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 16"
|
||||
d="M85 192.5C103.5 194 147.287 189.088 171 155C195 120.5 192.5 113 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 17"
|
||||
d="M83 193C108.667 193.667 160.089 186.414 177.5 163C192 143.5 197 127.5 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 19" d="M10.5 138.5L1 128.5" stroke-linecap="round" />
|
||||
<path id="Vector 20" d="M13 134.5L1 118.5" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 21"
|
||||
d="M18 128.5C17.8333 124.5 16 116.4 10 116C8.5 110 5 108 1 107.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 11"
|
||||
d="M29 230C25.1667 207.5 29.5 160 77.5 150C137.5 137.5 135.5 124 172 128"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 12"
|
||||
d="M28.9999 230C25.1666 207.5 30.5 158 96 158C130.5 158 148 130 172 128"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 1"
|
||||
d="M1 252.5C1.33333 247.333 2.5 240.5 7.5 241.5C12.5 242.5 11 250 8 250C5 250 4.5 245.5 7.5 246.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 2"
|
||||
d="M20 242.5C10.5 224.167 -3.60001 176.9 24 138.5C58.5 90.5 103 97 145 84.5C189 71.4048 236 59.5 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 3"
|
||||
d="M25 236.5C16.3333 217.833 3.99998 166.2 30 131"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 6"
|
||||
d="M193.5 131C215.667 130.833 259.8 104.2 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 1"
|
||||
d="M1 260C1 260 1 223.1 1 200C1 176.9 5.8 138.4 41.5 115.5"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 3"
|
||||
d="M1 260C9 260 29.4 230.5 35.5 223.3C40.9 217 51.3 211.3 59 221C70.1 235 44 247.2 43.3 233.6C42.8 224.3 52.6 227.4 51 231"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 3 copy"
|
||||
d="M59.5 234.5C59.5 234.5 65.4 227.7 71.5 220.5C76.9 214.2 87.3 208.5 95 218.2C106.1 232.2 80 244.4 79.3 230.8C78.8 221.5 88.6 224.6 87 228.2"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Form 3 copy_2"
|
||||
d="M94.5 232.5C94.5 232.5 98.9366 226.392 104.04 220.322C108.559 215.012 117.26 210.207 123.703 218.384C132.99 230.185 111.152 240.47 110.567 229.005C110.148 221.165 118.348 223.779 117.009 226.813"
|
||||
stroke-miterlimit="100"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 4"
|
||||
d="M29 231.5C26 209.5 23.3 160.6 38.5 143C57.5 121 80 116.5 107.5 110C135 103.5 228 93.5 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 5"
|
||||
d="M29 231.5C26 209.5 24.8 175.1 40 157.5C59 135.5 78.6302 132.623 107.5 123C137.5 113 147.5 103.5 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 7"
|
||||
d="M194.5 125.5C223 118.5 230.5 103 241 87.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 8"
|
||||
d="M194.5 121C214.833 115.833 253.5 89 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 9"
|
||||
d="M188.5 104C203.667 101 240.6 73.8 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 69"
|
||||
d="M177 104.5C184.667 102 192 99.5 207 88.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 10"
|
||||
d="M194 112.5C232.5 93.5 249.5 66.5 261 1"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 68"
|
||||
d="M194.5 116.5C205.5 113 217 104 230 90.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 13"
|
||||
d="M30.5 203C32.1667 198.833 39.7 190.7 56.5 191.5C77.5 192.5 122 201 135 145.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 14"
|
||||
d="M78 192.5C96.6667 195 135.3 188.5 140.5 142.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 18"
|
||||
d="M1 154.5C6.83333 142.5 23.8 116.5 41 110.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 21_2"
|
||||
d="M27.5 119C26 108.5 11.5 105 1 94.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 22"
|
||||
d="M28.5 118C27 105 11.5 100.5 1 87.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 23"
|
||||
d="M29.5 117C27.5 99 9.5 94.5 1 80.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 24"
|
||||
d="M36 112.5C34.5 84.5 8 92.5 1 55"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 25"
|
||||
d="M41.5 119C43.5 77 11 84 1 43"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 26"
|
||||
d="M129.5 115C131 110 141.5 95 157 108"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 27"
|
||||
d="M154.5 106C160.5 101.5 171.5 95.5 179.5 108"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 40"
|
||||
d="M123 179.5L140.5 172.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 41" d="M131.5 169.5L146 162" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 42"
|
||||
d="M136.5 159.5L149.5 152.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 43" d="M140 148L155.5 140.5" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 44"
|
||||
d="M63.5 163.5C69 162 69.5 152.5 74.5 151"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 45"
|
||||
d="M81.5 159C86 158.5 85 148.5 89.5 147.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 46"
|
||||
d="M96 158C99 157.5 100 145 104 143.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 47"
|
||||
d="M107.5 157C112.5 155.5 112.5 141.5 120 138"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 48"
|
||||
d="M122 152.5C127.5 150 126 136 133 133"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 49"
|
||||
d="M136.5 144.5C140.5 142 140.5 131 147.5 128.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 50"
|
||||
d="M180.5 108C181.5 105.5 189.5 99.5 193 109.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 51"
|
||||
d="M36.5 162.5C39 159 35.5 148.5 40 141.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 52"
|
||||
d="M42 155.5C45.5 151 43.5 138 48.5 133"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 53"
|
||||
d="M49 148.5C53 145.5 50 132.5 52 130.5"
|
||||
stroke-linecap="round" />
|
||||
<path id="Vector 54" d="M1 81V67" stroke-linecap="round" />
|
||||
<path id="Vector 55" d="M6 87L7 76.5" stroke-linecap="round" />
|
||||
<path id="Vector 56" d="M13 93L14 85.5" stroke-linecap="round" />
|
||||
<path id="Vector 57" d="M19.5 99L21 91" stroke-linecap="round" />
|
||||
<path id="Vector 58" d="M24 104L28 98" stroke-linecap="round" />
|
||||
<path id="Vector 59" d="M27.5 109L31.5 104.5" stroke-linecap="round" />
|
||||
<path id="Vector 60" d="M29 114L34 110.5" stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 61"
|
||||
d="M52 130.5C53.5 129.5 62.5 139 66.5 137"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 62"
|
||||
d="M55.5 128C61 125 78 132.5 84 130.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 63"
|
||||
d="M64 123.5C72 120 92.5 127.5 99.5 125.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 64"
|
||||
d="M74.5 119C82.5 116.5 112 121.5 120 118.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 65"
|
||||
d="M89 114.5C98.5 112 122.5 111 129.5 114.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 66"
|
||||
d="M172.5 101.5C195.5 91 238.5 74 260.5 5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 67"
|
||||
d="M166.5 100.5C177 95.6667 182.2 93.8 189 89"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 70"
|
||||
d="M194.5 112C195 110.333 196 105.9 196 101.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 71"
|
||||
d="M199 110C201.4 109 199.6 100.5 202 98"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 72"
|
||||
d="M206 106C208.5 104.5 206.5 96 211 91"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 73"
|
||||
d="M212 102C216 99 213 89.5 216 86"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 74"
|
||||
d="M220.5 95C223 93 219 84.5 221.5 80.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 75"
|
||||
d="M227.5 88C230.5 85 226 76.5 229 71"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 76"
|
||||
d="M235 78.5C238 74.5 232.5 67 234.5 62.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 77"
|
||||
d="M241.5 67.5C243.5 64 237 59.5 238.5 56"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 78"
|
||||
d="M247 55C248 52 242 50.5 243.5 47"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 79"
|
||||
d="M250.5 44C251.5 41.5 247.7 39.3 248.5 36.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 80"
|
||||
d="M191.5 138.5C194.5 135.833 200.7 130.4 201.5 130"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 81"
|
||||
d="M188.5 146C192.5 144 204.5 135.5 211 126.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 82"
|
||||
d="M41.5 115.5C43.1667 113.167 46.9 106.1 46.5 92.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 83"
|
||||
d="M34.5 90.5C34.8333 87.5 35.3 80.1 34.5 74.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 84"
|
||||
d="M18 73C19.1667 72.1667 21.6 68.5 22 60.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 85"
|
||||
d="M38.5 97C39.1667 92.6667 42.1 83.2 46.5 80"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 90"
|
||||
d="M56 108.5C56 97.5 50.6933 83.2 46 80"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 86"
|
||||
d="M26 80.5C26.6667 76.1667 29 68.5 34.5 65"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 89"
|
||||
d="M43 84C42.2941 79.6667 39.8235 68.5 34 65"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 87"
|
||||
d="M13 67.5C13.6667 63.1667 16 53.5 21.5 50"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 88"
|
||||
d="M30.5 69C29.7941 64.7905 26.8235 53.4 21 50"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 91"
|
||||
d="M183 155C194.5 150.5 206.5 147.5 219 122"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 92"
|
||||
d="M176.5 165C189.167 164.833 217.5 154.2 229.5 113"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 93"
|
||||
d="M246 87.5C251 89.8333 261.1 97.8 261.5 111C261.9 124.2 250 131.833 244 134"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 94"
|
||||
d="M28 216.5C32 215.5 42 202 49 202C56 202 64 214.5 72.5 214.5C81 214.5 92 203.5 100 204C108 204.5 116.5 217.5 127.5 215.5C138.5 213.5 145.5 203.5 153.5 202.5C161.5 201.5 175 210.5 184.5 207.5C192.1 205.1 195 198.5 195.5 195.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 95"
|
||||
d="M30.5 204C32 204 45.5 214 49.5 214C53.5 214 63 204 72 204C81 204 87.5 215 99 215.5C110.5 216 116.5 204 126.5 205C136.5 206 144 214 154 212C164 210 174.5 199 178.5 197C182.5 195 192 197.5 195 196C198 194.5 206 189.5 209 179.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 96"
|
||||
d="M29.5 205C31 202 36.5 196.5 55.5 197.5C74.5 198.5 95 202.5 112 201.5C129 200.5 145.5 198.5 155.5 194.5C165.5 190.5 178.5 179.5 184 172"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 97"
|
||||
d="M123 201C134.667 200.333 160.4 198.2 170 195C179.6 191.8 190.167 182.333 193.5 178.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 98"
|
||||
d="M165.5 174C173.333 174 193.7 168.4 200.5 160C207.3 151.6 213.667 144.167 216 141.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 99"
|
||||
d="M183 170.5C186 174.833 197.5 182.5 215 178"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 100"
|
||||
d="M204 173C207.333 177.333 217.6 183.4 230 169"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 101"
|
||||
d="M221 167.5C224.667 169.667 233.6 171.4 240 161C242.8 156.2 244.333 152 244.5 151"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 102"
|
||||
d="M234.5 153C240.833 154 253.4 150.6 253 129"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 103"
|
||||
d="M204.5 155.5C208.5 151.5 214.937 148.517 219 152C222.5 155 226 162.5 220 167.5C214.664 171.947 210.187 167.904 209.5 165.5C208.214 161 213 157 215 161"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 104"
|
||||
d="M214 144C218 140 226.437 137.054 230.5 140.537C234 143.537 237.5 151.037 231.5 156.037C226.164 160.484 221.687 156.441 221 154.037C219.714 149.537 224 146 226.5 149.537"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 105"
|
||||
d="M223.5 129.5C227.5 125.5 234.437 121.517 238.5 125C242 128 246.5 135.037 240.5 140.037C235.164 144.484 230.687 140.441 230 138.037C228.714 133.537 233.5 130 235.5 133.537"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 106"
|
||||
d="M229.5 113C233.5 109 240.623 102.5 246.5 107.537C250 110.537 253.5 118.037 247.5 123.037C242.164 127.484 237.687 123.441 237 121.037C235.714 116.537 240.5 113 242.5 116.537"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 107"
|
||||
d="M224.5 169C223.833 170.667 220.1 173.7 210.5 172.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 108"
|
||||
d="M238.5 153C238.333 155.5 235.8 160.9 227 162.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 109"
|
||||
d="M248.5 132C248.5 135.167 246.8 142.6 240 147"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 110"
|
||||
d="M242 95C247 95.3333 256.5 100.5 254.5 118.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 111"
|
||||
d="M200.5 160C198.833 162.333 196.5 169.5 200.5 172C205.067 174.854 209 170.5 209 168C209 165.5 205.82 160.5 202.5 165.5"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
id="Vector 112"
|
||||
d="M207 176C205.167 177 200.4 177.7 192 172.5"
|
||||
stroke-linecap="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style use:global>
|
||||
@import "./mask.css";
|
||||
|
||||
.wrapper {
|
||||
padding: 20px;
|
||||
filter: drop-shadow(0px 0px 40px #be8630aa) drop-shadow(0px 0px 5px black)
|
||||
drop-shadow(0px 0px 5px black) drop-shadow(0px 0px 5px black);
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 50%;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.wrapper > div {
|
||||
max-width: 40vw;
|
||||
}
|
||||
|
||||
.wrapper > div > :global(svg) {
|
||||
overflow: visible;
|
||||
max-width: 100%;
|
||||
will-change: contents;
|
||||
transform: translateZ(1px);
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.wrapper > div :global(path) {
|
||||
animation-duration: 8s !important;
|
||||
}
|
||||
|
||||
svg path {
|
||||
stroke: #ceba51 !important;
|
||||
}
|
||||
|
||||
.invert-x {
|
||||
transform: scaleX(-1) translateX(-2.5px);
|
||||
}
|
||||
</style>
|
||||
@@ -1,121 +0,0 @@
|
||||
<svg width="262" height="261" viewBox="0 0 262 261" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="mask">
|
||||
<path id="Vector 28" d="M35 165C39.5 160 42 160.5 44 162C46 163.5 46 170 41.5 169C40.6667 168.833 39.1 168.1 39.5 166.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 29" d="M44 153.032C48.5 148.032 52.7137 149.856 54 152C57 157 52.5 161 50.5 161C48.5 161 46.5 160 46.5 157.5C46.5 155 50.4 153.5 50 157.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 30" d="M51 150C57 138.5 64.7137 143.356 66 145.5C69 150.5 63.5 154.171 61.5 154.171C59.5 154.171 57.5 153.171 57.5 150.671C57.5 148.171 61.5 146.5 61.5 150" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 31" d="M65 143.939C72.1803 129.985 81.4115 135.878 82.9507 138.479C86.5409 144.546 79.9589 149 77.5655 149C75.1721 149 72.7787 147.787 72.7787 144.753C72.7787 141.72 77.5655 139.692 77.5655 143.939" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 32" d="M83 139.216C90.1803 123.269 99.4115 130.003 100.951 132.976C104.541 139.91 97.9589 145 95.5655 145C93.1721 145 90.7787 143.613 90.7787 140.146C90.7787 136.68 95.5655 134.363 95.5655 139.216" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 33" d="M101 133.578C107.424 118.627 115.684 124.941 117.061 127.728C120.273 134.228 114.384 139 112.243 139C110.101 139 107.96 137.7 107.96 134.45C107.96 131.2 112.243 129.028 112.243 133.578" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 34" d="M117 127.216C124.18 111.269 133.411 118.003 134.951 120.976C138.541 127.91 131.959 133 129.566 133C127.172 133 124.779 131.613 124.779 128.146C124.779 124.68 129.566 122.363 129.566 127.216" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 35" d="M135 121.216C142.18 105.269 151.411 112.003 152.951 114.976C156.541 121.91 149.959 127 147.566 127C145.172 127 142.779 125.613 142.779 122.146C142.779 118.68 147.566 116.363 147.566 121.216" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 36" d="M154.5 127C168.5 108 181 107 186 115.5C189.959 122.23 183.5 130 179.5 130.5C175.5 131 171.5 129.354 171.5 124.5C171.5 119.646 178 117.147 178 122" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 37" d="M164 131.5C166.5 129.5 176.172 126.515 179 135C181 141 176 146.5 172 147C168 147.5 165.5 144.392 165.5 141C165.5 137.608 171.5 135.147 171.5 140" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 38" d="M148.5 155C151.5 145.5 164.172 142.515 167 151C169 157 165.5 160.5 161.5 161C157.5 161.5 156 159 156 155.5C156 152 161.5 151 161.5 155" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 39" d="M143.5 167.973C146 162 153.035 159.682 154.666 165.317C155.819 169.301 153.801 171.626 151.495 171.958C149.189 172.29 148 171.324 148 169C148 166.676 151.495 165.317 151.495 167.973" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 15" d="M84 192.5C101.167 193.5 137.6 188.8 146 162C154.4 135.2 166.5 128.167 171.5 128" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 16" d="M85 192.5C103.5 194 147.287 189.088 171 155C195 120.5 192.5 113 193 109.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 17" d="M83 193C108.667 193.667 160.089 186.414 177.5 163C192 143.5 197 127.5 193 109.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 19" d="M10.5 138.5L1 128.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 20" d="M13 134.5L1 118.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 21" d="M18 128.5C17.8333 124.5 16 116.4 10 116C8.5 110 5 108 1 107.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 11" d="M29 230C25.1667 207.5 29.5 160 77.5 150C137.5 137.5 135.5 124 172 128" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 12" d="M28.9999 230C25.1666 207.5 30.5 158 96 158C130.5 158 148 130 172 128" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 1" d="M1 252.5C1.33333 247.333 2.5 240.5 7.5 241.5C12.5 242.5 11 250 8 250C5 250 4.5 245.5 7.5 246.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 2" d="M20 242.5C10.5 224.167 -3.60001 176.9 24 138.5C58.5 90.5 103 97 145 84.5C189 71.4048 236 59.5 261 1" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 3" d="M25 236.5C16.3333 217.833 3.99998 166.2 30 131" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 6" d="M193.5 131C215.667 130.833 259.8 104.2 261 1" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Form 1" d="M1 260C1 260 1 223.1 1 200C1 176.9 5.8 138.4 41.5 115.5" stroke="#FF1717" stroke-miterlimit="100" stroke-linecap="round"/>
|
||||
<path id="Form 3" d="M1 260C9 260 29.4 230.5 35.5 223.3C40.9 217 51.3 211.3 59 221C70.1 235 44 247.2 43.3 233.6C42.8 224.3 52.6 227.4 51 231" stroke="#FF1717" stroke-miterlimit="100" stroke-linecap="round"/>
|
||||
<path id="Form 3 copy" d="M59.5 234.5C59.5 234.5 65.4 227.7 71.5 220.5C76.9 214.2 87.3 208.5 95 218.2C106.1 232.2 80 244.4 79.3 230.8C78.8 221.5 88.6 224.6 87 228.2" stroke="#FF1717" stroke-miterlimit="100" stroke-linecap="round"/>
|
||||
<path id="Form 3 copy_2" d="M94.5 232.5C94.5 232.5 98.9366 226.392 104.04 220.322C108.559 215.012 117.26 210.207 123.703 218.384C132.99 230.185 111.152 240.47 110.567 229.005C110.148 221.165 118.348 223.779 117.009 226.813" stroke="#FF1717" stroke-miterlimit="100" stroke-linecap="round"/>
|
||||
<path id="Vector 4" d="M29 231.5C26 209.5 23.3 160.6 38.5 143C57.5 121 80 116.5 107.5 110C135 103.5 228 93.5 261 1" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 5" d="M29 231.5C26 209.5 24.8 175.1 40 157.5C59 135.5 78.6302 132.623 107.5 123C137.5 113 147.5 103.5 193 109.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 7" d="M194.5 125.5C223 118.5 230.5 103 241 87.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 8" d="M194.5 121C214.833 115.833 253.5 89 261 1" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 9" d="M188.5 104C203.667 101 240.6 73.8 261 1" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 69" d="M177 104.5C184.667 102 192 99.5 207 88.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 10" d="M194 112.5C232.5 93.5 249.5 66.5 261 1" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 68" d="M194.5 116.5C205.5 113 217 104 230 90.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 13" d="M30.5 203C32.1667 198.833 39.7 190.7 56.5 191.5C77.5 192.5 122 201 135 145.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 14" d="M78 192.5C96.6667 195 135.3 188.5 140.5 142.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 18" d="M1 154.5C6.83333 142.5 23.8 116.5 41 110.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 21_2" d="M27.5 119C26 108.5 11.5 105 1 94.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 22" d="M28.5 118C27 105 11.5 100.5 1 87.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 23" d="M29.5 117C27.5 99 9.5 94.5 1 80.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 24" d="M36 112.5C34.5 84.5 8 92.5 1 55" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 25" d="M41.5 119C43.5 77 11 84 1 43" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 26" d="M129.5 115C131 110 141.5 95 157 108" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 27" d="M154.5 106C160.5 101.5 171.5 95.5 179.5 108" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 40" d="M123 179.5L140.5 172.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 41" d="M131.5 169.5L146 162" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 42" d="M136.5 159.5L149.5 152.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 43" d="M140 148L155.5 140.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 44" d="M63.5 163.5C69 162 69.5 152.5 74.5 151" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 45" d="M81.5 159C86 158.5 85 148.5 89.5 147.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 46" d="M96 158C99 157.5 100 145 104 143.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 47" d="M107.5 157C112.5 155.5 112.5 141.5 120 138" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 48" d="M122 152.5C127.5 150 126 136 133 133" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 49" d="M136.5 144.5C140.5 142 140.5 131 147.5 128.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 50" d="M180.5 108C181.5 105.5 189.5 99.5 193 109.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 51" d="M36.5 162.5C39 159 35.5 148.5 40 141.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 52" d="M42 155.5C45.5 151 43.5 138 48.5 133" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 53" d="M49 148.5C53 145.5 50 132.5 52 130.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 54" d="M1 81V67" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 55" d="M6 87L7 76.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 56" d="M13 93L14 85.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 57" d="M19.5 99L21 91" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 58" d="M24 104L28 98" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 59" d="M27.5 109L31.5 104.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 60" d="M29 114L34 110.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 61" d="M52 130.5C53.5 129.5 62.5 139 66.5 137" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 62" d="M55.5 128C61 125 78 132.5 84 130.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 63" d="M64 123.5C72 120 92.5 127.5 99.5 125.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 64" d="M74.5 119C82.5 116.5 112 121.5 120 118.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 65" d="M89 114.5C98.5 112 122.5 111 129.5 114.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 66" d="M172.5 101.5C195.5 91 238.5 74 260.5 5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 67" d="M166.5 100.5C177 95.6667 182.2 93.8 189 89" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 70" d="M194.5 112C195 110.333 196 105.9 196 101.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 71" d="M199 110C201.4 109 199.6 100.5 202 98" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 72" d="M206 106C208.5 104.5 206.5 96 211 91" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 73" d="M212 102C216 99 213 89.5 216 86" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 74" d="M220.5 95C223 93 219 84.5 221.5 80.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 75" d="M227.5 88C230.5 85 226 76.5 229 71" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 76" d="M235 78.5C238 74.5 232.5 67 234.5 62.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 77" d="M241.5 67.5C243.5 64 237 59.5 238.5 56" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 78" d="M247 55C248 52 242 50.5 243.5 47" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 79" d="M250.5 44C251.5 41.5 247.7 39.3 248.5 36.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 80" d="M191.5 138.5C194.5 135.833 200.7 130.4 201.5 130" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 81" d="M188.5 146C192.5 144 204.5 135.5 211 126.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 82" d="M41.5 115.5C43.1667 113.167 46.9 106.1 46.5 92.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 83" d="M34.5 90.5C34.8333 87.5 35.3 80.1 34.5 74.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 84" d="M18 73C19.1667 72.1667 21.6 68.5 22 60.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 85" d="M38.5 97C39.1667 92.6667 42.1 83.2 46.5 80" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 90" d="M56 108.5C56 97.5 50.6933 83.2 46 80" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 86" d="M26 80.5C26.6667 76.1667 29 68.5 34.5 65" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 89" d="M43 84C42.2941 79.6667 39.8235 68.5 34 65" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 87" d="M13 67.5C13.6667 63.1667 16 53.5 21.5 50" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 88" d="M30.5 69C29.7941 64.7905 26.8235 53.4 21 50" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 91" d="M183 155C194.5 150.5 206.5 147.5 219 122" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 92" d="M176.5 165C189.167 164.833 217.5 154.2 229.5 113" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 93" d="M246 87.5C251 89.8333 261.1 97.8 261.5 111C261.9 124.2 250 131.833 244 134" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 94" d="M28 216.5C32 215.5 42 202 49 202C56 202 64 214.5 72.5 214.5C81 214.5 92 203.5 100 204C108 204.5 116.5 217.5 127.5 215.5C138.5 213.5 145.5 203.5 153.5 202.5C161.5 201.5 175 210.5 184.5 207.5C192.1 205.1 195 198.5 195.5 195.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 95" d="M30.5 204C32 204 45.5 214 49.5 214C53.5 214 63 204 72 204C81 204 87.5 215 99 215.5C110.5 216 116.5 204 126.5 205C136.5 206 144 214 154 212C164 210 174.5 199 178.5 197C182.5 195 192 197.5 195 196C198 194.5 206 189.5 209 179.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 96" d="M29.5 205C31 202 36.5 196.5 55.5 197.5C74.5 198.5 95 202.5 112 201.5C129 200.5 145.5 198.5 155.5 194.5C165.5 190.5 178.5 179.5 184 172" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 97" d="M123 201C134.667 200.333 160.4 198.2 170 195C179.6 191.8 190.167 182.333 193.5 178.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 98" d="M165.5 174C173.333 174 193.7 168.4 200.5 160C207.3 151.6 213.667 144.167 216 141.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 99" d="M183 170.5C186 174.833 197.5 182.5 215 178" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 100" d="M204 173C207.333 177.333 217.6 183.4 230 169" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 101" d="M221 167.5C224.667 169.667 233.6 171.4 240 161C242.8 156.2 244.333 152 244.5 151" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 102" d="M234.5 153C240.833 154 253.4 150.6 253 129" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 103" d="M204.5 155.5C208.5 151.5 214.937 148.517 219 152C222.5 155 226 162.5 220 167.5C214.664 171.947 210.187 167.904 209.5 165.5C208.214 161 213 157 215 161" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 104" d="M214 144C218 140 226.437 137.054 230.5 140.537C234 143.537 237.5 151.037 231.5 156.037C226.164 160.484 221.687 156.441 221 154.037C219.714 149.537 224 146 226.5 149.537" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 105" d="M223.5 129.5C227.5 125.5 234.437 121.517 238.5 125C242 128 246.5 135.037 240.5 140.037C235.164 144.484 230.687 140.441 230 138.037C228.714 133.537 233.5 130 235.5 133.537" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 106" d="M229.5 113C233.5 109 240.623 102.5 246.5 107.537C250 110.537 253.5 118.037 247.5 123.037C242.164 127.484 237.687 123.441 237 121.037C235.714 116.537 240.5 113 242.5 116.537" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 107" d="M224.5 169C223.833 170.667 220.1 173.7 210.5 172.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 108" d="M238.5 153C238.333 155.5 235.8 160.9 227 162.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 109" d="M248.5 132C248.5 135.167 246.8 142.6 240 147" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 110" d="M242 95C247 95.3333 256.5 100.5 254.5 118.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 111" d="M200.5 160C198.833 162.333 196.5 169.5 200.5 172C205.067 174.854 209 170.5 209 168C209 165.5 205.82 160.5 202.5 165.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
<path id="Vector 112" d="M207 176C205.167 177 200.4 177.7 192 172.5" stroke="#FF1717" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 16 KiB |